Technical Article

Oblikovanje arabskega in RTL besedila v Delphi PDF-jih s HotPDF

Pošljite arabsko frazo يوضح ملف PDF v TextOut in odprite rezultat. Črke potekajo v napačno smeri, vsaka pa stoji v svoji izolirani obliki z vidnim razmikom pred naslednjo, kot bi nekdo tipkal angleščino nazaj in med vsakim znakom pritisnil preslednico. Izjema se ni sprožila. Opozorilo se ni izpisalo. Izhod je preprosto napačen in napačen je zato, ker se dve ločeni transformaciji, od katerih je odvisna arabščina, nista nikoli zgodili. Poznavanje teh dveh transformacij in klica, ki ju izvaja, je večina tistega, na kar se zvede izpis PDF s kompleksnimi pisavami.

HotPDF je nativna komponenta VCL PDF za Delphi in C++Builder, ki z ločenim klicem namesto vas opravi delo od desne proti levi. Ustavi se tudi na nekaterih specifičnih mestih, ki jih želite spoznati, preden uveljavite lokalizacijo, zato imajo poštene omejitve svojo sekcijo blizu konca.

Zakaj se pravilen niz še vedno izpiše napačno

Unicode ohranja besedilo v logičnem vrstnem redu, to je vrstni red, v katerem ga tipkate in berete na glas. Upodabljalnik mora glife postaviti v vizualni vrstni red. Pri pisavah od leve proti desni se ta vrstna reda ujemata in nihče ne razmišlja o tem. Pri arabščini in hebrejščini pa se ne, in ko ena vrstica meša smeri, na primer arabski stavek, ki vsebuje latinsko besedo 'PDF', ali cena, zapisana s številkami, dvosmerni algoritem Unicode (UAX #9) natančno določi, kako se fragmenti od leve proti desni ugnezdijo v vrstico od desne proti levi. To je prva transformacija, prerazporejanje, in njen preskok je tisto, kar obrne vrstico.

Druga je kontekstualno oblikovanje. Arabska črka je narisana drugače glede na to, kje v besedi se nahaja: na začetku, v sredini, na koncu ali samostojno. Kodna točka ves čas ostaja enaka, spremeni se le glif. Cevovod, ki vsako kodno točko pošlje neposredno v njen privzeti glif, ustvari natanko tisti nepovezani izhod v izolirani obliki iz uvodnega odstavka. Hebrejščina ta korak preskoči, saj se njene črke ne povezujejo, vendar še vedno potrebuje prerazporejanje. Arabščina potrebuje oboje in zato je arabščina in ne hebrejščina tisti niz, s katerim testirate.

Na namizju nič od tega ni vaš problem. Ko obrazec VCL izriše arabščino v komponento TEdit, besedilni sklad operacijskega sistema tiho prerazporedi in oblikuje besedilo, kar je natanko razlog, zakaj se niz, ki je na zaslonu videti popoln, v preprostem PDF-ju izpiše pokvarjen. Tok vsebine ne shranjuje besedila, ki ga je mogoče urejati. Shranjuje pozicionirane glife, zato tisti, ki oddaja tok, poddeduje nalogo oblikovanja, ki jo je prej opravljal operacijski sistem. Klic RtLTextOut je tisti, ki to nalogo prevzame nazaj.

RtLTextOut izvaja prerazporejanje in povezovanje

HotPDF ohranja latinsko pot in pot za kompleksne pisave kot dve različni metodi. TextOut izpiše tisto, kar mu posredujete, v vrstnem redu, kot ste mu posredovali. RtLTextOut najprej prerazporedi in izvede kontekstualno analizo, nato pa izpiše. Katera pravila pisave se uporabijo, izhaja iz argumenta nabora znakov (charset) pri metodi SetFont: 178 pomeni arabščino, 177 va hebrejščino.

// Arabic: pass logical order; RtLTextOut reorders and joins
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF');

// Hebrew: reordering only, no contextual joining
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');

Ena napaka tukaj vzame več ur odpravljanja napak kot katera koli druga. Metoda RtLTextOut sama obrne niz, tako da če ji posredujete besedilo, ki ste ga že ročno obrnili (običajno kot začasno rešitev iz prejšnjega poskusa z navadnim klicem TextOut), ga obrne še enkrat in ste spet na začetku. Krut del je, da je dvakrat obrnjeno besedilo lahko videti pravilno za en sam testni niz, ki vsebuje le arabščino, nato pa razpade v trenutku, ko vrstica vsebuje latinsko besedo ali številko, saj ti vgrajeni deli ne sledijo več algoritmu UAX #9. Vedno posredujte logični vrstni red in pustite klicu, da ga uredi sam.

To isto vedenje z mešanimi smermi bolj zmede recenzente kot kodo. Znotraj vrstice od desne proti levi se številke in vgrajene latinske besede še vedno berejo od leve proti desni. Nekdo, ki ni delal z dvosmerno postavitvijo, bo pogledal izrisan račun, videl številko računa, ki se bere v 'napačno' smer glede na arabščino okoli nje, in to prijavil kot napako. Vendar je to po specifikaciji pravilen rezultat. Kratka opomba v vaših sprejemnih kriterijih, napisana pred prvim pregledom naravnega govorca, prihrani to pot.

Pokritost z glifi se določi pred samim zagonom oblikovanja

Oblikovanje izbira glife iz pisave. Če jih pisava ne vsebuje, nima česa izbrati. To je napaka pri namestitvi, ki zapravi celo popoldne: poročilo je brezhibno na razvijalčevem računalniku, kjer je nameščena pisava Arial Unicode MS, na strankinem strežniku pa se prikaže kot vrsta praznih kvadratkov, saj je Windows nadomestil pisavo s takšno, ki sploh nima arabskih znakov. Rešitev je, da ne zaupate pisavam na posameznem računalniku, temveč registrirate tisto, ki jo pošljete skupaj z aplikacijo.

// Ship a known font instead of relying on installed system fonts
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12);

// Audit coverage for the codepoints your data actually uses
GID := Pdf.GetUnicodeGlyphForCodepoint($0628);  // U+0628 ARABIC LETTER BEH
LogGlyphAudit($0628, GID);

S tem sta povezani dve omejitvi. Pisava, registrirana prek RegisterUnicodeTTF, se vgradi v dokument, HotPDF-jevo upravljanje vgrajenega Unicode-a pa zahteva dokument različice PDF 1.5 ali novejše. To je težava le, če nekaj kasneje v verigi vztraja pri različici PDF 1.4, vendar ko se to zgodi, je simptom tih. Druga omejitev je pravna: datoteke TrueType vsebujejo bite z dovoljenji za vgradnjo, pisava, ki se lepo izriše na zaslonu, pa je lahko licencirana na način, ki prepoveduje njeno pošiljanje v dokumentih strank. Preverite pred pošiljanjem in ne šele po pritožbi.

Drugi klic, GetUnicodeGlyphForCodepoint, je vaš sistem za zgodnje opozarjanje. Ob zagonu storitve se sprehodite skozi območja kodnih točk, ki jih vaši podatki dejansko uporabljajo, in zabeležite, kateri ID-ji glifov se vrnejo. Vrzel v pokritosti se nato med uvajanjem prikaže kot vrstica v zagonskem dnevniku namesto manjkajočih znakov na računu, ki je že dosegel stranko.

Besedilo, ki je Unicode, vendar ne poteka od desne proti levi (nizi CJK, vietnamščina s svojimi naloženimi diakritičnimi znaki, mešano evropsko besedilo), gre skozi običajno pot. TextOut sprejme WideString in ga izriše prek registrirane pisave brez kakršne koli dvosmerne analize. V kodi poročila je smiselno ohraniti ti dve poti fizično ločeni (ena rutina za dele RTL in ena za vse ostalo), tako da je logika lokalizacije vidna na mestu klica in ne skrita za zastavico, ki jo bo nekdo sčasoma pozabil nastaviti.

Smer branja pripada dokumentu in ne glifom

Pravilen prikaz vsakega glifa še vedno pusti eno stvar nedokončano. Standard ISO 32000-1 §12.2 opredeljuje nastavitev pregledovalnika z imenom /Direction, ki določa splošno smer branja dokumenta. Ta ne vpliva na same glife. Pregledovalniku pove, kako naj razporedi dvostranske postavitve, na kateri strani naj se začne postavitev nasprotnih strani in v katero smer naj se nagne uporabniški vmesnik za branje. Nič od tega se ne vidi na eni sami strani, zato se to pogosto pozabi.

// Declare right-to-left reading order at the document level
Pdf.Direction := RightToLeft;  // adds vpDirection to ViewerPreferences

Nastavitev lastnosti Direction je celotno delo: nastavitvena metoda doda vpDirection v ViewerPreferences dokumenta, tako da ena vrstica prenese to nastavitev v datoteko. Če jo izpustite, se zdi neškodljivo, saj je enostranski preizkus, ki ga gledate, v obeh primerih videti enak. Nato pa nekdo natisne dvostransko knjižico, strani pa se natisnejo zrcalno, vzrok pa je manjkajoča vrstica kode izpred tednov.

Kje se oblikovanje HotPDF zaključi

Iskren pregled omejitev je vreden enega tedna ocenjevanja. Metoda RtLTextOut sama pokriva dvosmerno prerazporejanje in arabsko kontekstualno povezovanje. Kar pa ne počne samodejno, je splošna uporaba funkcij OpenType. Izbirne ligature in podobne tipografske funkcije gredo skozi GetSingleSubstituteGlyph(GID, 'liga'), ki razreši eno zamenjavo naenkrat (ID glifa najprej, oznaka funkcije pa na drugem mestu) in vrne nespremenjen vhodni glif, če se funkcija ne uporabi. To je dovolj za vodenje znanega, končnega seznama ligatur, ki ga vzdržujete sami. Ne gre pa za celoten pogon GSUB. Za pisave, ki potrebujejo več kot le prerazporejanje in povezovanje (indijske pisave s svojimi prerazporejenimi samoglasniškimi znaki so klasičen primer), izvedite pravi preizkus z realnimi nizi strank, preden obljubite podporo. Delovanje v arabščini ni dokaz, da bo delovalo tudi v pisavi Devanagari.

Preverite delovanje od začetka do konca, saj je stran lahko videti pravilna, a je še vedno neuporabna za vse naslednje korake. Tri preverjanja odkrijejo večino težav. Kopirajte besedilo nazaj iz programa Acrobat in primerjajte kodne točke z vašim izvornim nizom. Zaženite iskanje besede znotraj dokumenta v pregledovalniku. In odprite izhod na računalniku, ki nima vaših razvijalskih pisav, kar bo najverjetneje razkrilo zamenjavo pisave. Nič od tega ne nadomestil ročnega pregleda naravnega govorca ob enem resničnem dokumentu, kar odkrije stvari, ki jih noben sintetični korpus ne bo. Načrtujte ta pregled pred pošiljanjem končne različice.

Testne nize izberite namensko namesto recikliranja tistega, kar je prevajalec poslal lani. Izvedljiv minimum na lokalizacijo: stavek v eni pisavi, stavek z vgrajenimi latinskimi imeni blagovnih znamk, vrstica s številkami in valuto ter imena z diakritičnimi ali sestavljenimi znaki. Dejanska imena strank podrejo predpostavke, ki jih slepo besedilo ne doseže, zato naj se regresijski nabor poveča za en niz vsakič, ko primer podpore razkrije vzorec, ki ga še niste videli.

Registracija pisav, deljenje pisav in vsakodnevni API za risanje besedila so opisani v članku o izhodu poročil, pisavah in slikah s HotPDF. Ko morajo isti dokumenti izpolnjevati tudi profile dostopnosti, se pravila označevanja jezika in strukture iz članka o preverjanju PDF/A in PDF/UA postavijo nad delo z oblikovanjem, opisano tukaj.

Zgoraj opisani vmesniki API za pisave od desne proti levi in Unicode so del komponente HotPDF Component za Delphi in C++Builder; stran izdelka vsebuje povezavo do celotne referenčne dokumentacije za izpis besedila.