Stuur de Arabische zin يوضح ملف PDF هذا naar een gewone TextOut en de pagina die terugkomt is op twee manieren tegelijk fout. De woorden lopen van links naar rechts in plaats van rechts naar links, en de letters staan los van elkaar in hun geïsoleerde vormen (isolated forms) in plaats van samen te vloeien in verbonden woorden. Er is geen foutmelding. De Delphi code compileert, het bestand opent, en een beoordelaar die Arabisch leest, vertelt u dat de uitvoer onbruikbaar is. De oplossing is één enkele aanroep, geen bibliotheekwissel: HotPDF leidt rechts-naar-links tekst door een afzonderlijke methode, RtLTextOut, die de herordening afhandelt die de gewone TextOut niet doet. Deze pagina is de werkende referentie voor die methode: de signatuur en zijn parameters, het charset-argument dat het script selecteert, de bijwerking op documentniveau, de lettertype-instelling (font setup) die eerst moet komen, en de fouten die daadwerkelijk de support bereiken, elk met zijn oplossing
Signatuur en parameters
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: WideString); overload;
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: PWORD; TextLength: Integer); overload;
X en Y verankeren de reeks (run) in het eigen coördinatensysteem van de pagina, gemeten vanaf de linkerbenedenhoek waarbij Y naar boven groeit, dezelfde oorsprong die elke TextOut-aanroep gebruikt; RtLTextOut verandert de glyph-volgorde, niet van waaruit de pagina meet. angle roteert de basislijn precies zoals het dat in TextOut doet, dus 0 tekent een horizontale lijn. Text is de tekenreeks in logische volgorde (logical order), de volgorde waarin u deze zou typen, en de tweede overbelasting (overload) neemt dezelfde UTF-16 data aan als een rauwe PWORD buffer met een expliciete code-unit telling, wat de vorm is om te gebruiken wanneer de tekst afkomstig is van een API in plaats van een Delphi-tekenreeks. Op oudere Delphi-versies die dateren van voor overbelastingsresolutie voor deze typen, wordt de tekenreeksvorm blootgesteld onder de naam RtLTextOutStr met de identieke parameterlijst
De taakverdeling tussen de twee uitvoeraanroepen is strikt. TextOut tekent codepunten in de volgorde waarin u ze doorgeeft, wat correct is voor Latijn, Cyrillisch en CJK (Chinees, Japans, Koreaans) en verkeerd voor Arabisch en Hebreeuws. RtLTextOut herordent elke regel eerst naar de visuele rechts-naar-links volgorde, tekent ze vervolgens en zorgt ervoor dat ingebedde Latijnse woorden en cijfers van links naar rechts lezen binnen de regel. HotPDF houdt de twee methoden opzettelijk gescheiden in plaats van de richting af te leiden uit de karakters, dus de keuze welke methode u aanroept, is de keuze welk scriptgedrag u krijgt; gebruik RtLTextOut voor reeksen van rechts naar links, TextOut voor al het andere, en stuur nooit de een door de ander. Waarom de herordening überhaupt bestaat, wat het Unicode Bidirectional Algorithm en Arabische contextuele verbinding (contextual joining) daadwerkelijk doen, en waar de vormgeving (shaping) van HotPDF stopt, is het onderwerp van het bijbehorende artikel over Arabische en RTL-tekstvormgeving met HotPDF; alles hieronder is de praktische installatie

Het charset-argument bepaalt het script
Wat RtLTextOut vertelt of het Arabisch of Hebreeuws aan het opmaken is, is niet de methode, het is het lettertype. SetFont neemt een Windows-tekenset (charset) aan als zijn vierde argument, en die waarde draagt de scriptregels over naar de rechts-naar-links aanroep: 178 selecteert Arabisch, 177 selecteert Hebreeuws. Stel de charset in, teken dan, en de twee onderstaande regels komen in de juiste leesvolgorde naar buiten zonder verdere configuratie
// Arabic: charset 178 tells RtLTextOut to apply Arabic rules
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
// Hebrew: charset 177 switches the rules to Hebrew
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');
Eén detail qua volgorde wordt makkelijk over het hoofd gezien: de SetFont moet als eerste komen en moet na elke AddPage worden herhaald, omdat het huidige lettertype, inclusief de charset, een pagina-einde (page break) niet overleeft. Vergeet de herhaling en de tweede pagina valt terug op welk lettertype er dan ook actief was, wat voor Arabisch meestal lege vierkantjes betekent
Het keert geen tekst om die u al hebt omgekeerd
De enkele fout die hier de meeste foutopsporingstijd (debugging time) opslokt, is het voeden van RtLTextOut met een tekenreeks die u al handmatig hebt omgedraaid (flipped). Mensen komen bij deze methode uit nadat een eerste poging met gewone TextOut achterstevoren uitviel, en een veelvoorkomende noodoplossing is om de tekens in de code om te draaien voordat er wordt getekend. RtLTextOut keert intern uit zichzelf om, dus een vooraf omgekeerde tekenreeks wordt een tweede keer omgekeerd en belandt precies waar deze begon. Geef de tekst door in logische volgorde, de volgorde waarin u deze zou typen en hardop zou voorlezen, en laat de aanroep het herordenen doen
De valstrik is gemener dan een simpele omdraaiing omdat een dubbel omgekeerde tekenreeks er correct kan uitzien voor een testzin die volledig uit Arabisch bestaat, en vervolgens stuk kan gaan op het moment dat een regel een Latijns woord of een nummer bevat. Binnen een regel van rechts naar links horen die ingebedde reeksen (embedded runs) van links naar rechts te lezen, en handmatige omkering vernietigt die nesting terwijl de puur-Arabische variant het toevallig overleeft. Dus de bug zeilt door uw eerste rooktest (smoke test) en duikt later op op een echte factuur met een rekeningnummer erin. Verwijder elke handmatige omkering op het moment dat u overschakelt naar RtLTextOut
De Direction bijwerking (side effect) om te weten
Het aanroepen van RtLTextOut verandert meer dan alleen de regel die u tekent. Het draait ook de leesrichting-voorkeur (reading-direction preference) van het document naar rechts-naar-links, hetzelfde wat u anders zelf zou instellen via de eigenschap Direction. Deze setter voegt vpDirection toe aan de ViewerPreferences van het document, die een viewer vertelt hoe een overzicht van twee pagina's (two-up spread) te rangschikken en aan welke kant een layout met tegenoverliggende pagina's begint. Wanneer het hele document Arabisch of Hebreeuws is, is dit precies wat u wilt, en u krijgt het gratis
Het is goed om dit te weten, juist omdat het onzichtbaar is op een enkele pagina. Als het document voornamelijk van links-naar-rechts is met één rechts-naar-links blok, zal de eerste RtLTextOut aanroep nog steeds de voorkeur van het hele bestand doen kantelen, en niets in uw proef van één pagina zal dit aantonen. Het symptoom verschijnt weken later wanneer iemand een dubbelzijdig boekje afdrukt en de spreads (pagina-overzichten) in spiegelbeeld uitkomen. Als dat niet is wat u wilt, stel dan Direction expliciet weer in na de rechts-naar-links reeks:
// RtLTextOut already set the document direction to RightToLeft;
// restore left-to-right if the document is predominantly LTR
Pdf.Direction := LeftToRight;
Voor een document dat echt van rechts-naar-links leest, laat u dit met rust. Het punt is te weten dat de aanroep een documentbreed effect heeft, zodat de verrassing met het boekje nooit optreedt
Registreer het lettertype dat u meelevert, niet datgene wat u hoopt dat geïnstalleerd is
Geen van de herordeningen maakt uit als het lettertype geen tekens (glyphs) heeft om te tekenen. De klassieke fout is een rapport dat foutloos weergeeft op de machine van de ontwikkelaar, waar Arial Unicode MS toevallig aanwezig is, en eruit komt als rijen van lege vierkantjes op de server van een klant, waar Windows stilletjes een lettertype zonder enige Arabische dekking verving. De remedie is te stoppen met het vertrouwen van geïnstalleerde systeemlettertypen en een lettertype te registreren dat u meelevert (ship) met de toepassing
// Ship a known Arabic font and register it before drawing
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
Twee grenzen rijden mee met registratie. Een lettertype binnengehaald via RegisterUnicodeTTF wordt ingebed, en de verwerking van ingebedde Unicode van HotPDF vereist dat het document op PDF 1.5 of later is; dat is alleen nadelig als iets stroomafwaarts (downstream) aandringt op PDF 1.4, maar wanneer dat gebeurt, is de storing stilzwijgend. De andere is juridisch in plaats van technisch: TrueType-bestanden dragen inbeddingstoestemming-bits (embedding-permission bits), en een lettertype dat er op het scherm prima uitziet, kan op een manier gelicentieerd zijn die het verzenden ervan binnen klantdocumenten verbiedt. Controleer de licentie voordat u insluit (embed), niet na een klacht
Een compleet consolevoorbeeld
Zet u de stukken samen, dan is hier een zelfstandig programma dat één pagina schrijft met een Arabische regel, een Hebreeuwse regel, en een gemengde regel met een Latijnse productnaam. Elk blok stelt zijn charset in, en tekent dan in logische volgorde
program RtLTextOutDemo;
{$APPTYPE CONSOLE}
uses
HPDFDoc; // HotPDF main unit
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'RtLTextOut.pdf';
Pdf.BeginDoc;
// A Latin heading goes through the ordinary TextOut path
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(40, 780, 0, 'Right-to-left text with HotPDF');
// Arabic: charset 178, logical order, RtLTextOut does the reordering
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 720, 0,
'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');
// Hebrew: charset 177
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 680, 0,
'קובץ PDF זה מדגים טקסט עברי הזורם מימין לשמאל.');
// Mixed line: the embedded Latin word still reads left to right
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.
Voer het uit en open het resultaat. De Arabische en Hebreeuwse regels lezen van rechts naar links, de letters verbinden zich waar het script ze verbindt, en in de laatste regel staat het teken HotPDF van links-naar-rechts binnen de Arabische reeks. Dat nesten (nesting) is het correcte bidirectionele resultaat, geen bug, hoewel de eerste keer-beoordelaars het routinematig als één indienen; het hierboven gelinkte vormgevingsartikel legt uit waarom de Unicode-regels het eisen en hoe u uw acceptatiecriteria zo formuleert dat het rapport nooit wordt ingediend
Veelvoorkomende fouten en hun oplossingen
Elke onderstaande storing is in een echte support thread verschenen, en elk is terug te voeren naar een van de bovenstaande secties
- Uitvoer leest achterstevoren of raakt in de war op gemengde regels — de tekenreeks werd met de hand omgedraaid vóór de aanroep, meestal een overgebleven tijdelijke oplossing (workaround) van een
TextOutpoging. Verwijder elke handmatige omkering en geef de logische volgorde door;RtLTextOutkeert intern om - Letters worden losgekoppeld in geïsoleerde vormen afgedrukt — de tekst ging door de gewone
TextOut, ofSetFontwerd aangeroepen zonder een rechts-naar-links charset. Teken metRtLTextOuten geef 178 door voor Arabisch of 177 voor Hebreeuws als het vierdeSetFontargument - Lege vierkantjes op de machine van de klant — Windows heeft een lettertype zonder Arabische of Hebreeuwse dekking vervangen. Stop met het benoemen van geïnstalleerde lettertypen; registreer een lettertype dat u meelevert (ship) via
RegisterUnicodeTTFen voer erSetFontop uit met die naam - Tweede pagina rendert in het verkeerde lettertype — het huidige lettertype overleeft
AddPageniet. Herhaal deSetFontaanroep, inclusief charset, na elk pagina-einde (page break) - Dubbelzijdige overzichten drukken gespiegeld af op een overwegend LTR (links-naar-rechts) document — de eerste
RtLTextOutaanroep kantelde deDirection(richting) van het document als een bijwerking (side effect). StelPdf.Direction := LeftToRightin na de rechts-naar-links reeks - Ingebedde Unicode-tekst degradeert stilzwijgend stroomafwaarts (downstream) — iets in de pijplijn (pipeline) dwingt PDF 1.4 af, en de afhandeling van ingebedde Unicode van HotPDF heeft 1.5 of later nodig. Verhoog de documentversie of verwijder de downstream beperking
Voordat het format wordt uitgebracht (ships), verifieer het dan verder dan slechts ernaar te kijken: kopieer de tekst terug uit de viewer, voer de in-document zoekopdracht uit, open het bestand op een machine zonder uw ontwikkellettertypen, en leg één echt document voor aan een native (moedertaal) lezer. De volledige verificatiecontrolelijst, de dekkingskaart per script, en het test-tekenreeks corpus (test-string corpus) dat de moeite waard is om te bouwen, staan allemaal in het bijbehorende artikel over Arabische en RTL-tekstvormgeving met HotPDF
De RtLTextOut, SetFont, en RegisterUnicodeTTF aanroepen die hier worden getoond, maken deel uit van de HotPDF-component voor Delphi en C++Builder