Στείλτε την αραβική πρόταση يوضح ملف PDF هذا στην απλή TextOut και η σελίδα που επιστρέφει είναι λανθασμένη με δύο τρόπους ταυτόχρονα. Οι λέξεις τρέχουν από αριστερά προς τα δεξιά αντί από δεξιά προς τα αριστερά, και τα γράμματα στέκονται χωριστά στις απομονωμένες μορφές τους αντί να ενώνονται σε συνδεδεμένες λέξεις. Τίποτα δεν βγάζει σφάλμα. Ο κώδικας Delphi μεταγλωττίζεται, το αρχείο ανοίγει, και ένας αξιολογητής που διαβάζει αραβικά σάς λέει ότι η έξοδος είναι άχρηστη. Η διόρθωση είναι μία κλήση, όχι αλλαγή βιβλιοθήκης: το HotPDF δρομολογεί το κείμενο από δεξιά προς αριστερά μέσω μιας ξεχωριστής μεθόδου, της RtLTextOut, που χειρίζεται την αναδιάταξη την οποία η απλή TextOut δεν κάνει. Αυτή η σελίδα είναι η πρακτική αναφορά για τη μέθοδο αυτή: η υπογραφή και οι παράμετροί της, το όρισμα charset που επιλέγει τη γραφή, η παρενέργεια σε επίπεδο εγγράφου, η ρύθμιση γραμματοσειράς που πρέπει να προηγηθεί, και οι αποτυχίες που φτάνουν πραγματικά στην υποστήριξη, καθεμία με τη διόρθωσή της
Υπογραφή και παράμετροι
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: WideString); overload;
procedure RtLTextOut(X, Y: Single; angle: Extended;
Text: PWORD; TextLength: Integer); overload;
Τα X και Y αγκυρώνουν τη σειρά κειμένου στο σύστημα συντεταγμένων της ίδιας της σελίδας, μετρημένα από την κάτω αριστερή γωνία με το Y να αυξάνεται προς τα πάνω, την ίδια αρχή που χρησιμοποιεί κάθε κλήση TextOut· η RtLTextOut αλλάζει τη σειρά των γλύφων, όχι το σημείο από το οποίο μετρά η σελίδα. Το angle περιστρέφει τη γραμμή βάσης ακριβώς όπως στην TextOut, οπότε το 0 σχεδιάζει οριζόντια γραμμή. Το Text είναι η συμβολοσειρά σε λογική σειρά, τη σειρά με την οποία θα την πληκτρολογούσατε, και η δεύτερη υπερφόρτωση δέχεται τα ίδια δεδομένα UTF-16 ως ακατέργαστο buffer PWORD με ρητό πλήθος μονάδων κώδικα, που είναι η μορφή που πρέπει να χρησιμοποιείτε όταν το κείμενο έρχεται από ένα API και όχι από συμβολοσειρά Delphi. Σε παλαιότερες εκδόσεις Delphi που προηγούνται της επίλυσης υπερφόρτωσης για αυτούς τους τύπους, η μορφή συμβολοσειράς εκτίθεται με το όνομα RtLTextOutStr με πανομοιότυπη λίστα παραμέτρων
Ο καταμερισμός εργασίας ανάμεσα στις δύο κλήσεις εξόδου είναι αυστηρός. Η TextOut σχεδιάζει τα σημεία κώδικα με τη σειρά που τα περνάτε, κάτι σωστό για λατινικά, κυριλλικά και CJK και λάθος για αραβικά και εβραϊκά. Η RtLTextOut αναδιατάσσει πρώτα κάθε γραμμή σε οπτική σειρά από δεξιά προς αριστερά, έπειτα σχεδιάζει, διατηρώντας τις ενσωματωμένες λατινικές λέξεις και τα ψηφία να διαβάζονται από αριστερά προς τα δεξιά μέσα στη γραμμή. Το HotPDF κρατά τις δύο μεθόδους σκόπιμα ξεχωριστές αντί να μαντεύει την κατεύθυνση από τους χαρακτήρες, οπότε η επιλογή του ποια θα καλέσετε είναι η επιλογή της συμπεριφοράς γραφής που θα λάβετε· χρησιμοποιήστε την RtLTextOut για σειρές από δεξιά προς αριστερά, την TextOut για όλα τα υπόλοιπα, και ποτέ μην περνάτε τη μία μέσα από την άλλη. Γιατί υπάρχει καθόλου η αναδιάταξη, τι κάνουν στην πραγματικότητα ο Unicode Bidirectional Algorithm και η αραβική συμφραστική σύνδεση, και πού σταματά η διαμόρφωση (shaping) του HotPDF είναι το θέμα του συνοδευτικού άρθρου για τη διαμόρφωση αραβικού και RTL κειμένου με το HotPDF· όλα όσα ακολουθούν είναι η πρακτική ρύθμιση

Το όρισμα charset καθορίζει τη γραφή
Αυτό που λέει στην RtLTextOut αν διατάσσει αραβικά ή εβραϊκά δεν είναι η μέθοδος, είναι η γραμματοσειρά. Η SetFont δέχεται ένα charset των Windows ως τέταρτο όρισμα, και αυτή η τιμή μεταφέρει τους κανόνες γραφής στην κλήση από δεξιά προς αριστερά: το 178 επιλέγει αραβικά, το 177 επιλέγει εβραϊκά. Ορίστε το charset, έπειτα σχεδιάστε, και οι δύο παρακάτω γραμμές βγαίνουν σε σωστή σειρά ανάγνωσης χωρίς καμία περαιτέρω ρύθμιση
// Αραβικά: το charset 178 λέει στην RtLTextOut να εφαρμόσει αραβικούς κανόνες
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
// Εβραϊκά: το charset 177 αλλάζει τους κανόνες σε εβραϊκούς
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');
Μια λεπτομέρεια ακολουθίας ξεφεύγει εύκολα: η SetFont πρέπει να προηγείται και πρέπει να επαναλαμβάνεται μετά από κάθε AddPage, επειδή η τρέχουσα γραμματοσειρά, μαζί με το charset, δεν επιβιώνει από μια αλλαγή σελίδας. Ξεχάστε την επανάληψη και η δεύτερη σελίδα επιστρέφει σε όποια γραμματοσειρά ήταν ενεργή, κάτι που για τα αραβικά συνήθως σημαίνει κενά πλαίσια
Δεν αντιστρέφει κείμενο που έχετε ήδη αντιστρέψει
Το ένα λάθος που καταπίνει τον περισσότερο χρόνο αποσφαλμάτωσης εδώ είναι να τροφοδοτήσετε την RtLTextOut με μια συμβολοσειρά που έχετε ήδη αντιστρέψει χειροκίνητα. Οι προγραμματιστές φτάνουν σε αυτή τη μέθοδο αφού μια πρώτη προσπάθεια με την απλή TextOut βγήκε ανάποδα, και μια συνηθισμένη πρόχειρη λύση είναι να αντιστρέψουν τους χαρακτήρες στον κώδικα πριν τη σχεδίαση. Η RtLTextOut αντιστρέφει εσωτερικά από μόνη της, οπότε μια προ-αντεστραμμένη συμβολοσειρά αντιστρέφεται δεύτερη φορά και καταλήγει ακριβώς εκεί από όπου ξεκίνησε. Περάστε το κείμενο σε λογική σειρά, τη σειρά με την οποία θα το πληκτρολογούσατε και θα το διαβάζατε φωναχτά, και αφήστε την κλήση να κάνει την αναδιάταξη
Η παγίδα είναι πιο ύπουλη από μια απλή αντιστροφή, επειδή μια διπλά αντεστραμμένη συμβολοσειρά μπορεί να φαίνεται σωστή για μια δοκιμαστική φράση αποκλειστικά στα αραβικά και έπειτα να χαλάει τη στιγμή που μια γραμμή περιέχει μια λατινική λέξη ή έναν αριθμό. Μέσα σε μια γραμμή από δεξιά προς αριστερά αυτές οι ενσωματωμένες σειρές πρέπει να διαβάζονται από αριστερά προς τα δεξιά, και η χειροκίνητη αντιστροφή καταστρέφει αυτή την ένθεση ενώ η αμιγώς αραβική περίπτωση τυχαίνει να την επιβιώνει. Έτσι το σφάλμα περνά αβίαστα το πρώτο σας smoke test και εμφανίζεται αργότερα σε ένα πραγματικό τιμολόγιο με έναν αριθμό λογαριασμού μέσα. Αφαιρέστε κάθε χειροκίνητη αντιστροφή τη στιγμή που περνάτε στην RtLTextOut
Η παρενέργεια Direction που αξίζει να γνωρίζετε
Η κλήση της RtLTextOut αλλάζει περισσότερα από τη γραμμή που σχεδιάζετε. Αλλάζει επίσης την προτίμηση κατεύθυνσης ανάγνωσης του εγγράφου σε δεξιά προς αριστερά, το ίδιο πράγμα που διαφορετικά θα ορίζατε εσείς μέσω της ιδιότητας Direction. Αυτός ο setter προσθέτει το vpDirection στα ViewerPreferences του εγγράφου, που λέει σε ένα πρόγραμμα προβολής πώς να διατάξει τα δισέλιδα και από ποια πλευρά ξεκινά μια διάταξη αντικριστών σελίδων. Όταν ολόκληρο το έγγραφο είναι αραβικό ή εβραϊκό αυτό είναι ακριβώς ό,τι θέλετε, και το παίρνετε δωρεάν
Αξίζει να το γνωρίζετε ακριβώς επειδή είναι αόρατο σε μία μόνο σελίδα. Αν το έγγραφο είναι κατά κύριο λόγο από αριστερά προς τα δεξιά με ένα μόνο μπλοκ από δεξιά προς αριστερά, η πρώτη κλήση RtLTextOut θα αλλάξει και πάλι την προτίμηση ολόκληρου του αρχείου, και τίποτα στο μονοσέλιδο δοκίμιό σας δεν θα το δείξει. Το σύμπτωμα εμφανίζεται εβδομάδες αργότερα, όταν κάποιος εκτυπώνει ένα φυλλάδιο διπλής όψης και τα δισέλιδα βγαίνουν κατοπτρικά. Αν δεν είναι αυτό που θέλετε, επαναφέρετε ρητά το Direction μετά τη σειρά από δεξιά προς αριστερά:
// Η RtLTextOut έχει ήδη ορίσει την κατεύθυνση του εγγράφου σε RightToLeft·
// επαναφέρετε αριστερά προς δεξιά αν το έγγραφο είναι κατά κύριο λόγο LTR
Pdf.Direction := LeftToRight;
Για ένα έγγραφο που όντως διαβάζεται από δεξιά προς αριστερά, αφήστε το ως έχει. Το ζητούμενο είναι να γνωρίζετε ότι η κλήση έχει επίδραση σε ολόκληρο το έγγραφο, ώστε η έκπληξη με το φυλλάδιο να μη συμβεί ποτέ
Καταχωρίστε τη γραμματοσειρά που διανέμετε, όχι αυτή που ελπίζετε ότι είναι εγκατεστημένη
Τίποτα από την αναδιάταξη δεν έχει σημασία αν η γραμματοσειρά δεν έχει γλύφους να σχεδιάσει. Η κλασική αποτυχία είναι μια αναφορά που αποδίδεται άψογα στον υπολογιστή του προγραμματιστή, όπου τυχαίνει να υπάρχει η Arial Unicode MS, και βγαίνει ως σειρές κενών πλαισίων στον διακομιστή ενός πελάτη, όπου τα Windows αντικατέστησαν σιωπηλά μια γραμματοσειρά χωρίς καμία απολύτως αραβική κάλυψη. Η θεραπεία είναι να σταματήσετε να εμπιστεύεστε τις εγκατεστημένες γραμματοσειρές συστήματος και να καταχωρίσετε μία που διανέμετε μαζί με την εφαρμογή
// Διανείμετε μια γνωστή αραβική γραμματοσειρά και καταχωρίστε την πριν τη σχεδίαση
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
Δύο όρια συνοδεύουν την καταχώριση. Μια γραμματοσειρά που εισάγεται μέσω RegisterUnicodeTTF ενσωματώνεται, και ο χειρισμός ενσωματωμένου Unicode του HotPDF χρειάζεται το έγγραφο σε PDF 1.5 ή νεότερο· αυτό δαγκώνει μόνο αν κάτι πιο κάτω στη ροή επιμένει σε PDF 1.4, αλλά όταν συμβεί η αποτυχία είναι σιωπηλή. Το άλλο είναι νομικό και όχι τεχνικό: τα αρχεία TrueType φέρουν bit άδειας ενσωμάτωσης, και μια γραμματοσειρά που φαίνεται μια χαρά στην οθόνη μπορεί να έχει άδεια χρήσης που απαγορεύει τη διανομή της μέσα σε έγγραφα πελατών. Επιβεβαιώστε την άδεια πριν ενσωματώσετε, όχι μετά από ένα παράπονο
Ένα πλήρες παράδειγμα κονσόλας
Συνθέτοντας τα κομμάτια, ακολουθεί ένα αυτόνομο πρόγραμμα που γράφει μία σελίδα με μια αραβική γραμμή, μια εβραϊκή γραμμή και μια μεικτή γραμμή που περιέχει ένα λατινικό όνομα προϊόντος. Κάθε μπλοκ ορίζει το charset του, έπειτα σχεδιάζει σε λογική σειρά
program RtLTextOutDemo;
{$APPTYPE CONSOLE}
uses
HPDFDoc; // κύρια unit του HotPDF
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'RtLTextOut.pdf';
Pdf.BeginDoc;
// Μια λατινική επικεφαλίδα περνά από τη συνηθισμένη διαδρομή TextOut
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(40, 780, 0, 'Right-to-left text with HotPDF');
// Αραβικά: charset 178, λογική σειρά, η RtLTextOut κάνει την αναδιάταξη
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 720, 0,
'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');
// Εβραϊκά: charset 177
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 680, 0,
'קובץ PDF זה מדגים טקסט עברי הזורם מימין לשמאל.');
// Μεικτή γραμμή: η ενσωματωμένη λατινική λέξη διαβάζεται ακόμη από αριστερά προς δεξιά
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.
Εκτελέστε το και ανοίξτε το αποτέλεσμα. Οι αραβικές και οι εβραϊκές γραμμές διαβάζονται από δεξιά προς αριστερά, τα γράμματα ενώνονται εκεί όπου τα ενώνει η γραφή, και στην τελευταία γραμμή το token HotPDF κάθεται από αριστερά προς τα δεξιά μέσα στην αραβική σειρά. Αυτή η ένθεση είναι το σωστό αμφίδρομο αποτέλεσμα, όχι σφάλμα, παρόλο που οι αξιολογητές που το βλέπουν για πρώτη φορά το καταγράφουν συστηματικά ως τέτοιο· το άρθρο για τη διαμόρφωση που συνδέεται παραπάνω εξηγεί γιατί το απαιτούν οι κανόνες Unicode και πώς να διατυπώσετε τα κριτήρια αποδοχής σας ώστε η αναφορά να μην κατατεθεί ποτέ
Συνηθισμένα σφάλματα και οι διορθώσεις τους
Κάθε αποτυχία παρακάτω έχει εμφανιστεί σε πραγματικό νήμα υποστήριξης, και καθεμία ανάγεται σε μία από τις παραπάνω ενότητες
- Η έξοδος διαβάζεται ανάποδα ή μπερδεύεται σε μεικτές γραμμές — η συμβολοσειρά αντιστράφηκε χειροκίνητα πριν από την κλήση, συνήθως απομεινάρι λύσης από μια προσπάθεια με
TextOut. Διαγράψτε κάθε χειροκίνητη αντιστροφή και περάστε λογική σειρά· ηRtLTextOutαντιστρέφει εσωτερικά - Τα γράμματα εκτυπώνονται ασύνδετα σε απομονωμένες μορφές — το κείμενο πέρασε από την απλή
TextOut, ή ηSetFontκλήθηκε χωρίς charset δεξιά προς αριστερά. Σχεδιάστε με τηνRtLTextOutκαι περάστε 178 για αραβικά ή 177 για εβραϊκά ως τέταρτο όρισμα τηςSetFont - Κενά πλαίσια στον υπολογιστή του πελάτη — τα Windows αντικατέστησαν μια γραμματοσειρά χωρίς αραβική ή εβραϊκή κάλυψη. Σταματήστε να κατονομάζετε εγκατεστημένες γραμματοσειρές· καταχωρίστε μια γραμματοσειρά που διανέμετε μέσω
RegisterUnicodeTTFκαι ορίστε την μεSetFontμε αυτό το όνομα - Η δεύτερη σελίδα αποδίδεται με λάθος γραμματοσειρά — η τρέχουσα γραμματοσειρά δεν επιβιώνει από την
AddPage. Επαναλάβετε την κλήσηSetFont, μαζί με το charset, μετά από κάθε αλλαγή σελίδας - Τα δισέλιδα διπλής όψης εκτυπώνονται κατοπτρικά σε ένα κατά κύριο λόγο LTR έγγραφο — η πρώτη κλήση
RtLTextOutάλλαξε τοDirectionτου εγγράφου ως παρενέργεια. ΟρίστεPdf.Direction := LeftToRightμετά τη σειρά από δεξιά προς αριστερά - Το ενσωματωμένο κείμενο Unicode υποβαθμίζεται σιωπηλά πιο κάτω στη ροή — κάτι στη διοχέτευση επιβάλλει PDF 1.4, και ο χειρισμός ενσωματωμένου Unicode του HotPDF χρειάζεται 1.5 ή νεότερο. Ανεβάστε την έκδοση του εγγράφου ή αφαιρέστε τον περιορισμό στη συνέχεια της ροής
Πριν κυκλοφορήσει η μορφή, επαληθεύστε πέρα από το απλό κοίταγμα: αντιγράψτε το κείμενο πίσω από το πρόγραμμα προβολής, εκτελέστε την αναζήτηση μέσα στο έγγραφο, ανοίξτε το αρχείο σε υπολογιστή χωρίς τις γραμματοσειρές ανάπτυξής σας, και βάλτε ένα γνήσιο έγγραφο μπροστά σε έναν φυσικό ομιλητή. Η πλήρης λίστα ελέγχου επαλήθευσης, ο χάρτης κάλυψης ανά γραφή και το σώμα δοκιμαστικών συμβολοσειρών που αξίζει να χτίσετε βρίσκονται όλα στο συνοδευτικό άρθρο για τη διαμόρφωση αραβικού και RTL κειμένου με το HotPDF
Οι κλήσεις RtLTextOut, SetFont και RegisterUnicodeTTF που παρουσιάζονται εδώ αποτελούν μέρος του HotPDF Delphi Component για Delphi και C++Builder