Tehnički članak

CJK vertikalni tekst u PDF-u sa Delphi-jem i HotPDF-om

Postavite japanski roman na stranicu i prva stvar koju primetite je da tekst tece niz kolonu, a ne duz linije, i da kolone napreduju od desne ivice lista prema levoj. Citaocu koji je odrastao s njim horizontalni tekst deluje blago kliniicki. Inzenjarski problem je da PDF, poput skoro svakog digitalnog tekstualnog sistema, izgraddjen je oko horizontalne bazne linije koja raste sleva nadesno, i tok sadrzaja nema pojam o "pisanju ovog pasusa nadole umesto". Stoga, kada Delphi aplikacija mora da proizvede sertifikat, pesmu, oznaku ili tradicionalno formatiran pravni dokument za tajvanskog, japanskog ili korejskog citaoca, raspored mora biti sastavljen rucno: jedan karakter ispod drugog, jedna kolona levo od poslednje

HotPDF vam daje prekidac koji obavlja knjizenje po karakteru umesto vas. Font koji postavite nosi zastavicu IsVertical, i kada je ukljucena, jedan poziv TextOut slaze citav string u vertikalnu kolonu umesto da ga vodi duz bazne linije. Postavljanje kolona, redosled zdesna nalevo i jedna tiho vazna zamena glifa su ono sto ostatak ove stranice razradjuje

A4 PDF stranica generisana HotPDF-om prikazujuci kineski, japanski i korejski tekst slagan u vertikalne kolone koje se citaju zdesna nalevo
Jedna A4 stranica s kineskim, japanskim i korejskim tekstom u vertikalnim kolonama, generisana iz jedne Delphi procedure.

Prekidac se nalazi na SetFont

Vertikalni raspored nije svojstvo stranice ili dokumenta. To je svojstvo fonta s kojim crtate, i ukljucujete ga petim argumentom SetFont-a:

// SetFont(FontName, FontStyle, Size, FontCharset, IsVertical)
// The 5th argument flips the current font into vertical mode.
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, False); // horizontal
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, True);  // vertical

Buduci da zastavica ide uz font, prelazite izmedzu horizontalnog i vertikalnog pisanja jednostavnim ponovnim pozivanjem SetFont-a s razlicitim poslednjim argumentom. Stranica moze imati horizontalni naslov na vrhu i vertikalni tekst tela ispod, i HotPDF cva ta dva rezima odvojena po objektu fonta, a ne po stranici. To je ono sto cini mesovite rasporede mogucim bez ikakvog posebnog upravljanja rezimima s vase strane: svaki TextOut posle vertikalnog SetFont-a slaze, svaki TextOut posle horizontalnog tece duz bazne linije, i poslednji SetFont pobedjuje

Cetvrti argument je Windows skup karaktera, isti koji uzima horizontalni poziv. Prosledjivanjem DEFAULT_CHARSET pustamo sistem da razresi glifove po stringu, sto je ovde vazno jer vertikalni dokument cesto mesa skriptove. Sve ostalo o fontu i dalje vazi: mora biti instaliran na masini za izgradnju, i gotovo uvek zelite FontEmbedding := True da bi fajl renderovao iste CJK glifove na citacu koji nikada nije imao Arial Unicode MS

Jedan poziv TextOut je jedna kolona

Uz aktivan vertikalni font, poziv TextOut vise ne siri string bocno od date tacke. On postavlja prvi karakter na vrhu i vodi ostale ravno nize, napredujuci za visinu linije fonta za svaki glif. X koji prosledjujete fiksira kolonu; Y koji prosledjujete fiksira gde pocinje vrh kolone. Da biste rasporedili pravi odlomak, stoga izdajete jedan TextOut po koloni i koracate X ulevo izmedzu poziva, jer se CJK kolone citaju zdesna nalevo

var
  Pdf: THotPDF;
const
  ColTop = 760;   // y of the first glyph in every column (points)
  ColGap = 28;    // horizontal distance between columns
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'VerticalText.pdf';
    Pdf.FontEmbedding := True;       // embed the CJK face for portable rendering
    Pdf.BeginDoc;
    Pdf.CurrentPage.Size := psA4;

    // A horizontal heading first, in the ordinary writing mode.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 16, DEFAULT_CHARSET, False);
    Pdf.CurrentPage.TextOut(60, 800, 0, 'Tang poem, vertical layout');

    // Switch the font into vertical mode; every TextOut below now stacks.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 18, DEFAULT_CHARSET, True);

    // Columns advance right to left, so X decreases with each call.
    Pdf.CurrentPage.TextOut(520, ColTop, 0, '床前明月光');
    Pdf.CurrentPage.TextOut(520 - ColGap,     ColTop, 0, '疑是地上霜');
    Pdf.CurrentPage.TextOut(520 - ColGap * 2, ColTop, 0, '舉頭望明月');
    Pdf.CurrentPage.TextOut(520 - ColGap * 3, ColTop, 0, '低頭思故鄉');

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Pesma se cita kako treba: najpre desna kolona, odozgo nadole, zatim oko skace levo na sledecu kolonu. Nista u samom stringu ne kodira taj redosled. Vi ga kodirate u X koordinatama, smanjujuci ih poziv po poziv. Okrenite smer naopako i stih izadje obrnut, sto je jedna najcesca greska pri prenosenju horizontalnog rasporeda u vertikalni

Koordinate: niz stranicu, zdesna nalevo

Dve ose su u igri i vuku u suprotnim smerovima, pa vredi biti precizan. HotPDF meri od donjeg levog ugla stranice, Y raste nagore, u tackama. Vertikalna kolona stoga pocinje na visokom Y (blizu vrha lista) i karakteri se spustaju od tamo kako HotPDF oduzima visinu linije za svaki. Taj pocetni Y postavite jednom po koloni i komponenta upravlja spustanjem

Horizontalnom osom upravljate rucno. Svaka kolona sedi na sopstvenom X, i uzastopne kolone koracaju ka manjim X vrednostima jer je redosled citanja zdesna nalevo. Razuman ritam je odabir X krajnje desne kolone, zatim oduzimanje fiksnog razmaka kolone za svaki naredni poziv, kao sto primer radi s ColGap. Razmak birate sami; previse teskano i susedne kolone se dodiruju, previse olabavljeno i blok izgleda retko. Za tekst tela od 12 do 18 tacaka, razmak malo veci od velicine fonta cita se udobno

Glifovi koji moraju promeniti oblik

Nekoliko karaktera nije samo rotiranih kopija svojih horizontalnih verzija; moraju se zameniti kada tekst postane vertikalan. Jedan koji HotPDF resava umesto vas je japanski znak za produzeni zvuk (U+30FC), traka dugog samoglasnika koja se pojavljuje u katakana recima kao sto su kohi. Nacrtan horizontalno to je kratka crtica na baznoj liniji. Ako bi isti glif slozen u kolonu lezao ravno duz kolone, to bi bilo pogresno: u vertikalnom japanskom taj znak postaje vertikalna crta koja spaja dva karaktera izmedzu kojih sedi. HotPDF otkriva U+30FC na vertikalnoj putanji i renderuje ga kao vertikalnu crtu (U+007C) tako da znak dugog samoglasnika pokazuje u pravom smeru bez ikakvog posla s vase strane

Ta jedna zamena pokriva slucaj koji lomi vecinu naivnih implementacija, ali vredi znati gde opsti problem ide dublje. Puna vertikalna tipografija takodje rotira latinicna slova i zapadnjacku interpunkciju za devedeset stepeni, pomera male kana znake, i repozicionira zagrade i zareze u njihove vertikalne oblike, i potpuna implementacija toga zivi u OpenType vertikalnim karakteristikama fonta a ne u fiksnim pravilima. HotPDF moze koristiti te karakteristike kada ih font nosi: vertikalne alternative (GSUB karakteristike vert i vrt2) i vertikalni kerning (vkrn i vpal GPOS pretrazivanja) su opcionalni, primenjeni duz vertikalne putanje samo kada ih aktivni font zapravo definise. Za mesoviti CJK tekst u jednom sirokom licu poput Arial Unicode MS, ugradjena obrada U+30FC zajedno s jednakim korakom visine linije dovoljna je za tacne, citljive kolone; OpenType karakteristike su vazne kada predjete na font dizajniran za fino vertikalno slaganje i zelite njegovo izvornu pozicioniranje kana znakova i razmak izmedzu glifova

Mesanje skriptova i orijentacija na jednoj stranici

Pravi dokumenti su retko cisti. Vertikalna japanska stranica moze nositi horizontalni engleski natpis, broj stranice duz dna, ili blok korejskog postavljenog vertikalno pored japanskog. Buduci da je vertikalna zastavica prekidac na nivou fonta, ovo sastavljate naizmenicnim pozivima SetFont, a ne upravljanjem ikakvim stanjem na nivou stranice. Postavite horizontalni font, napisite zaglavlje i folijo, postavite vertikalni font, rasporedite kolone, postavite horizontalni font opet za podnozje. Svaka oblast preuzima rezim poslednjeg SetFont-a, pa je jedina disciplina potrebna pozivanje ga kad god menjate smer

Jedan detalj za planiranje kada se skriptovi mesaju: kineski, japanski i korejski ideogrami su blizu kvadratnih i slazu se ravnomernim korakom, ali latinicni nizovi ugradeni u vertikalnu kolonu nemaju taj jednoobrazni napredak. Ako vam je potrebno nekoliko latinicnih reci unutar inace vertikalnog teksta, odlucite namerno da li treba da budu rotirane da teku niz kolonu ili postavljene uspravno kao kratak horizontalni umetak, i pozicionirajte taj fragment sopstvenim TextOut-om umesto da ga pustite da jase vertikalni korak namenjen ideogramima. Tretiranje mesovitog niza kao sopstvenog problema postavljanja cuva ritam kolone netaknutim

Od primera do produkcije

Delovi su mali i predvidivo se kombinuju. Ukljucite vertikalnu zastavicu u SetFont-u, izdajte jedan TextOut po koloni od fiksnog gornjeg Y, i smanjujte X izmedzu poziva da se kolone citaju zdesna nalevo. Ugradite font da CJK glifovi prezive putovanje do citaoceve masine, i pustite komponentu da upravlja znakom dugog samoglasnika U+30FC i, tamo gde font to podrzava, OpenType vertikalnim karakteristikama. Od tuda, prebacivanje rasporeda u produkciju je uglavnom aritmetika: izvodjenje X pozicija kolona iz izmerene sirine bloka, razbijanje dugih odlomaka u kolone koje staju u visinu stranice, i rezervisanje prostora za horizontalna zaglavlja i folija

Za siru tekstualnu i fontovsku povrsinu na kojoj ovo poiva, ukljucujuci horizontalne konvencije TextOut-a i registraciju Unicode fontova, pogledajte visejezicni Hello World primer i primer TextOut. Vertikalni prekidac SetFont-a i pozivi TextOut-a prikazani ovde deo su HotPDF Component za Delphi i C++Builder