Lähetä arabiankielinen lause يوضح ملف PDF هذا tavalliselle TextOut-funktiolle ja sivu, joka tulee takaisin, on väärin kahdella tavalla kerralla. Sanat kulkevat vasemmalta oikealle eikä oikealta vasemmalle, ja kirjaimet istuvat erillään eristetyissä muodoissaan sen sijaan, että ne yhtyisivät toisiinsa liittyviksi sanoiksi. Mikään ei aiheuta virhettä. Delphi kääntää koodin, tiedosto avautuu, ja arvioija, joka lukee arabiaa, kertoo sinulle, että tuloste on käyttökelvoton. Korjaus on yksi kutsu (call), ei kirjaston vaihto: HotPDF reitittää (routes) oikealta vasemmalle (right-to-left) -tekstin erillisen metodin, RtLTextOut, kautta, joka hoitaa sen uudelleenjärjestämisen (reordering), jota tavallinen TextOut ei tee. Tämä sivu on työreferenssi tuolle metodille: signatuuri (signature) ja sen parametrit, merkistöargumentti (charset), joka valitsee kirjoitusjärjestelmän (script), asiakirjatason sivuvaikutus (side effect), fontin asetus joka on tehtävä ensin, ja epäonnistumiset, jotka todella saavuttavat tuen, kukin korjauksineen
Signatuuri ja parametrit
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: WideString); overload;
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: PWORD; TextLength: Integer); overload;
X ja Y ankkuroivat pätkän (run) sivun omaan koordinaatistoon, mitattuna vasemmasta alakulmasta (bottom-left) Y:n kasvaessa ylöspäin (upward), eli samaan origoon (origin), jota jokainen TextOut-kutsu käyttää; RtLTextOut muuttaa glyyfijärjestystä (glyph order), ei sitä mistä sivu mittaa. angle kiertää (rotates) perusviivaa (baseline) täsmälleen kuten TextOut-kutsussa, joten 0 piirtää vaakasuoran viivan. Text on merkkijono loogisessa järjestyksessä (logical order), siinä järjestyksessä kuin kirjoittaisit sen, ja toinen ylikuormitus (overload) ottaa saman UTF-16-datan raakana PWORD-puskurina, jossa on eksplisiittinen koodiyksiköiden (code-unit) määrä, mikä on muoto, jota käytetään, kun teksti saapuu API:sta eikä Delphi-merkkijonosta. Vanhemmissa Delphi-versioissa, jotka edeltävät (predate) ylikuormituksen ratkaisemista (overload resolution) näille tyypeille, merkkijonomuoto on paljastettu (exposed) nimellä RtLTextOutStr täsmälleen samalla parametrilistalla
Työnjako (division of labor) näiden kahden tulostekutsun (output calls) välillä on tiukka. TextOut piirtää koodipisteet (codepoints) siinä järjestyksessä kuin välität (pass) ne, mikä on oikein latinalaisille, kyrillisille ja CJK-merkistöille, mutta väärin arabialle ja heprealle. RtLTextOut järjestää jokaisen rivin ensin visuaaliseen oikealta vasemmalle -järjestykseen ja sitten piirtää, pitäen upotetut latinalaiset sanat ja numerot lukusuunnassa vasemmalta oikealle rivin sisällä. HotPDF pitää nämä kaksi metodia tarkoituksella erillään sen sijaan, että se arvaisi suunnan (direction) merkeistä (characters), joten valinta siitä, kumpaa kutsut, on valinta siitä, minkä kirjoitusjärjestelmän käyttäytymisen (script behavior) saat; käytä RtLTextOut-metodia oikealta vasemmalle -pätkille, TextOut-metodia kaikelle muulle, äläkä koskaan reititä (route) yhtä toisen kautta. Se, miksi uudelleenjärjestäminen (reordering) on ylipäätään olemassa, mitä Unicode Bidirectional Algorithm ja arabian kontekstuaalinen yhdistyminen (contextual joining) todella tekevät, ja mihin HotPDF:n muotoilu (shaping) pysähtyy, ovat aiheena kumppaniartikkelissa Arabian ja RTL-tekstin muotoilusta HotPDF:n avulla; kaikki alla oleva on käytännön asetus (practical setup)

Charset-argumentti päättää kirjoitusjärjestelmän
Se, mikä kertoo RtLTextOut-metodille, asemoiko se (laying out) arabiaa vai hepreaa, ei ole metodi, vaan fontti. SetFont ottaa Windowsin merkistön (charset) neljäntenä argumenttina, ja tuo arvo kantaa kirjoitusjärjestelmän säännöt (script rules) oikealta vasemmalle -kutsuun: 178 valitsee arabian, 177 valitsee heprean. Aseta charset, sitten piirrä, ja kaksi alla olevaa riviä tulevat ulos oikeassa lukujärjestyksessä (reading order) ilman mitään muuta konfiguraatiota
// Arabia: charset 178 kertoo RtLTextOut-kutsulle soveltaa arabian sääntöjä
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
// Heprea: charset 177 vaihtaa säännöt hepreaksi
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');
Yksi jaksottamisen yksityiskohta (sequencing detail) on helppo missata: SetFont-kutsu on tultava ensin, ja se on toistettava (repeated) jokaisen AddPage-kutsun jälkeen, koska nykyinen fontti, charset mukaan lukien, ei selviä (survive) sivunvaihdosta (page break). Unohda toisto, ja toinen sivu putoaa takaisin siihen fonttiin, mikä tahansa sattuikin olemaan aktiivinen, mikä arabian kohdalla tarkoittaa yleensä tyhjiä laatikoita (empty boxes)
Se ei käännä (reverse) tekstiä, jonka olet jo kääntänyt
Yksittäinen virhe (mistake), joka nielee eniten vianetsintäaikaa (debugging time) täällä, on syöttää RtLTextOut-kutsulle merkkijono, jonka olet jo kääntänyt (flipped) käsin. Ihmiset saapuvat tämän metodin pariin sen jälkeen, kun ensimmäinen yritys tavallisella TextOut-funktiolla tuli ulos väärinpäin (backwards), ja yleinen väliaikaisratkaisu (stopgap) on kääntää merkit (characters) koodissa ennen piirtämistä. RtLTextOut kääntää itse sisäisesti (reverses internally on its own), joten esikäännetty (pre-reversed) merkkijono käännetään toisen kerran ja se päätyy takaisin sinne mistä se aloitti. Välitä teksti loogisessa järjestyksessä (logical order), siinä järjestyksessä kuin kirjoittaisit sen ja lukisit sen ääneen, ja anna kutsun tehdä uudelleenjärjestäminen (reordering)
Ansa (trap) on ilkeämpi kuin pelkkä käännös (plain flip), koska kaksoiskäännetty (double-reversed) merkkijono voi näyttää oikealta yhdessä puhtaasti arabiankielisessä testilauseessa (all-Arabic test phrase) ja sitten rikkoutua sillä hetkellä, kun rivi kantaa latinalaista sanaa tai numeroa. Oikealta vasemmalle -rivin sisällä noiden upotettujen pätkien (embedded runs) on tarkoitus lukea vasemmalta oikealle, ja käsinkääntäminen (hand-reversal) pilaa (wrecks) tuon sisäkkäisyyden (nesting), kun taas puhtaasti arabiankielinen tapaus sattuu selviytymään (survive) siitä. Joten bugi purjehtii ensimmäisen savutestisi (smoke test) läpi ja nousee pintaan (surfaces) myöhemmin oikealla laskulla, jossa on tilinumero. Karsi pois (strip out) jokainen manuaalinen käännös (manual reversal) heti kun vaihdat RtLTextOut-kutsun käyttöön
Direction-sivuvaikutus (side effect), joka on syytä tietää
RtLTextOut-kutsu muuttaa enemmän kuin vain rivin, jota olet piirtämässä. Se myös kääntää asiakirjan lukusuunnan (reading-direction preference) oikealta vasemmalle, eli saman asian, jonka muutoin asettaisit itse Direction-ominaisuuden kautta. Tuo asettaja (setter) lisää vpDirection-merkinnän asiakirjan ViewerPreferences-kohtaan, joka kertoo katseluohjelmalle (viewer), kuinka asetella aukeamat (two-up spreads) ja kummalta puolelta aukeamataitto (facing-page layout) alkaa. Kun koko asiakirja on arabiaa tai hepreaa, tämä on juuri sitä mitä haluat, ja saat sen ilmaiseksi (for free)
Se on syytä tietää nimenomaan siksi, että se on näkymätön yhdellä sivulla. Jos asiakirja on enimmäkseen vasemmalta oikealle ja siinä on yksi oikealta vasemmalle -lohko (block), ensimmäinen RtLTextOut-kutsu kääntää (tip) silti koko tiedoston mieltymyksen (preference), eikä mikään yksisivuisessa vedoksessasi (one-page proof) näytä sitä. Oire (symptom) ilmestyy viikkoja myöhemmin, kun joku tulostaa kaksipuoleisen (duplex) vihkon (booklet) ja aukeamat (spreads) tulevat ulos peilattuina (mirrored). Jos tuo ei ole sitä mitä haluat, aseta Direction takaisin eksplisiittisesti (explicitly) oikealta vasemmalle -pätkän jälkeen:
// RtLTextOut asetti jo asiakirjan suunnan (document direction) tilaan RightToLeft;
// palauta vasemmalta oikealle (left-to-right), jos asiakirja on pääasiassa LTR
Pdf.Direction := LeftToRight;
Asiakirjalle, jota aidosti (genuinely) luetaan oikealta vasemmalle, jätä se rauhaan. Pointti on tietää, että kutsulla on asiakirjanlaajuinen (document-wide) vaikutus, jotta vihkoyllätys (booklet surprise) ei koskaan tapahdu
Rekisteröi fontti, jonka toimitat (ship), ei sitä, jonka toivot olevan asennettuna
Mikään uudelleenjärjestämisestä ei merkitse mitään, jos fontilla ei ole glyyfejä (glyphs) piirrettäväksi. Klassinen epäonnistuminen on raportti, joka renderöityy (renders) virheettömästi kehittäjän koneella, jossa Arial Unicode MS sattuu olemaan läsnä, ja tulee ulos riveinä tyhjiä laatikoita (empty boxes) asiakkaan palvelimella, jossa Windows vaivihkaa (quietly) korvasi sen (substituted) fontilla, jolla ei ole lainkaan arabian peittoa (Arabic coverage). Parannuskeino on lopettaa asennettuihin järjestelmäfontteihin luottaminen (trusting installed system fonts) ja rekisteröidä sellainen, jonka toimitat (ship) sovelluksen mukana
// Toimita (ship) tunnettu arabialainen fontti ja rekisteröi se ennen piirtämistä
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
Kaksi rajaa (boundaries) kulkee mukana rekisteröinnin yhteydessä. RegisterUnicodeTTF-kutsun kautta tuotu kirjasin uppoutuu (embedded), ja HotPDF:n upotetun Unicoden (embedded Unicode) käsittely (handling) vaatii asiakirjan olevan PDF 1.5 tai uudempi; tuo puree (bites) vain, jos jokin alavirrassa (downstream) vaatii (insists on) PDF 1.4:ää, mutta kun se puree, epäonnistuminen on hiljainen (silent). Toinen on oikeudellinen (legal) pikemminkin kuin tekninen: TrueType-tiedostot kantavat upotuslupabittejä (embedding-permission bits), ja fontti, joka näyttää hyvältä näytöllä, voi olla lisensoitu (licensed) tavalla, joka kieltää sen toimittamisen asiakirjojen sisällä. Vahvista lisenssi ennen kuin upotat, ei valituksen jälkeen
Täydellinen konsoliesimerkki
Kokoamalla palaset yhteen, tässä on itsenäinen (self-contained) ohjelma, joka kirjoittaa yhden sivun, jossa on arabiankielinen rivi, hepreankielinen rivi ja sekakielinen (mixed) rivi, joka kantaa latinalaista tuotenimeä. Jokainen lohko asettaa oman charset-arvonsa, ja sitten piirtää loogisessa järjestyksessä (logical order)
program RtLTextOutDemo;
{$APPTYPE CONSOLE}
uses
HPDFDoc; // HotPDF-pääyksikkö
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'RtLTextOut.pdf';
Pdf.BeginDoc;
// Latinalainen otsikko menee tavallisen TextOut-polun kautta
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(40, 780, 0, 'Right-to-left text with HotPDF');
// Arabia: charset 178, looginen järjestys (logical order), RtLTextOut tekee uudelleenjärjestelyn (reordering)
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 720, 0,
'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');
// Heprea: charset 177
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 680, 0,
'קובץ PDF זה מדגים טקסט עברי הזורם מימין לשמאל.');
// Sekarivi (Mixed line): upotettu latinalainen sana lukee silti vasemmalta oikealle
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 640, 0,
'مرحبا بالعالم! تم إنشاؤه بواسطة HotPDF');
Pdf.EndDoc;
Writeln('Wrote RtLTextOut.pdf');
finally
Pdf.Free;
end;
end.
Aja se ja avaa tulos. Arabian- ja hepreankieliset rivit luetaan oikealta vasemmalle, kirjaimet yhtyvät (join), missä kirjoitusjärjestelmä yhdistää ne, ja viimeisellä rivillä token HotPDF istuu vasemmalta oikealle (left-to-right) arabiankielisen pätkän (run) sisällä. Tuo sisäkkäisyys (nesting) on oikea kaksisuuntainen tulos (bidirectional result), ei bugi, vaikka ensikertalaiset arvioijat rutiininomaisesti jättävät sen sellaisena (file it as one); yllä linkitetty muotoilu-artikkeli (shaping article) selittää, miksi Unicoden säännöt vaativat sitä ja kuinka muotoilet (word) hyväksymiskriteerisi (acceptance criteria), jotta raporttia ei koskaan jätetä (filed)
Yleiset virheet ja niiden korjaukset
Jokainen alla oleva epäonnistuminen (failure) on ilmestynyt oikeassa tukiketjussa (support thread), ja jokainen johtaa takaisin (traces back) yhteen yllä olevista osioista
- Tuloste lukee takaperin tai sekoittuu (scrambles) sekakielisillä riveillä — merkkijono (string) käännettiin käsin (reversed by hand) ennen kutsua, yleensä jäännöksenä väliaikaisratkaisusta (workaround)
TextOut-yrityksestä. Poista jokainen manuaalinen käännös (manual reversal) ja välitä (pass) teksti loogisessa järjestyksessä (logical order);RtLTextOutkääntää (reverses) itse sisäisesti (internally) - Kirjaimet tulostuvat toisistaan irti eristetyissä muodoissa (isolated forms) — teksti meni tavallisen
TextOut-kutsun kautta, taiSetFont-kutsua kutsuttiin ilman oikealta vasemmalle -merkistöä (charset). Piirrä käyttämälläRtLTextOut-kutsua ja välitä neljäntenäSetFont-argumenttina 178 arabialle tai 177 heprealle - Tyhjiä laatikoita asiakkaan koneella — Windows korvasi fontin (substituted a font), jolla ei ole lainkaan arabian- tai hepreankielistä peittoa (coverage). Lopeta asennettujen (installed) fonttien nimeäminen; rekisteröi kirjasin (face), jonka toimitat sovelluksen mukana, käyttämällä
RegisterUnicodeTTF-kutsua ja aseta seSetFont-kutsulla tuolla nimellä - Toinen sivu renderöityy väärällä fontilla — nykyinen fontti ei selviä (does not survive)
AddPage-kutsusta. ToistaSetFont-kutsu charset-parametreineen jokaisen sivunvaihdon (page break) jälkeen - Kaksipuoliset aukeamat (Duplex spreads) tulostuvat peilattuina (mirrored) asiakirjassa, joka on pääosin vasemmalta oikealle (LTR) — ensimmäinen
RtLTextOut-kutsu käänsi asiakirjanDirection-ominaisuuden sivuvaikutuksena (side effect). AsetaPdf.Direction := LeftToRightoikealta vasemmalle -pätkän (run) jälkeen - Upotettu Unicode-teksti rapistuu (degrades) äänettömästi alavirrassa (downstream) — jokin liukuhihnassa (pipeline) pakottaa (forces) PDF 1.4:n, ja HotPDF:n upotetun Unicoden (embedded Unicode) käsittely vaatii 1.5:n tai uudemman. Nosta asiakirjan versiota (document version) tai poista alavirran rajoitus (downstream constraint)
Ennen kuin tiedostomuoto menee tuotantoon (ships), varmista, että tiedosto toimii silmämääräistä tarkastusta (eyeballing) pidemmälle: kopioi (copy) teksti takaisin ulos katseluohjelmasta (viewer), aja asiakirjan sisäinen haku (in-document search), avaa tiedosto koneella, jolla ei ole kehitysfonttejasi (development fonts), ja laita yksi aito (genuine) asiakirja syntyperäisen lukijan (native reader) eteen. Täysi varmistustarkistuslista (verification checklist), kirjoitusjärjestelmäkohtainen peittokartta (per-script coverage map) ja rakentamisen arvoinen testimerkkijonokokoelma (test-string corpus) elävät kaikki kumppaniartikkelissa Arabian ja RTL-tekstin muotoilusta HotPDF:n avulla
Tässä esitetyt RtLTextOut, SetFont ja RegisterUnicodeTTF-kutsut ovat osa Delphin ja C++Builderin HotPDF-komponenttia