Το PDFlibPas επιλύει χαρακτήρες που η επιλεγμένη γραμματοσειρά δεν μπορεί να σχεδιάσει αναζητώντας μια αλυσίδα fallback εγκατεστημένων faces, cluster προς cluster, διατηρώντας παράλληλα το shaping και τη σειρά διμεθεύθυνσης run. Το ενεργοποιείτε με την SetAutomaticFontFallback, επεκτείνετε την αλυσίδα με την AddFontFallback, και μόνο οι γραμματοσειρές fallback που πράγματι χρησιμοποιήθηκαν για έξοδο ενσωματώνονται στο αρχείο
Το πρόβλημα που λύνει είναι ένα που κάθε γεννήτρια εγγράφων συναντά την πρώτη φορά που φτάνει το όνομα ενός πελάτη σε γραφή που η γραμματοσειρά προτύπου δεν είχε ποτέ προβλέψει. Η αποτυχία είναι σιωπηλή, κάτι που την κάνει δαπανηρή
Γιατί εξαφανίζεται το μη υποστηριζόμενο κείμενο αντί να εγείρει σφάλμα;
Επειδή το PDF δεν έχει καμία έννοια γραμματοσειράς που δεν μπορεί να σχεδιάσει έναν χαρακτήρα. Μια απλή γραμματοσειρά αντιστοιχίζει byte codes σε ονόματα γλυφών μέσω μιας κωδικοποίησης· μια σύνθετη γραμματοσειρά αντιστοιχίζει codes μέσω ενός CMap σε δείκτες γλυφών. Ζητήστε μια γλυφή που το face δεν περιέχει και παίρνετε δείκτη γλυφής μηδέν, .notdef, που τα περισσότερα faces σχεδιάζουν ως τίποτα ή ως κενό κουτί. Το αρχείο είναι δομικά έγκυρο, ο τελεστής κειμένου είναι καλά σχηματισμένος, και η σελίδα αποδίδεται. Απλώς είναι κενή εκεί όπου θα έπρεπε να είναι το όνομα
Τίποτα στο ISO 32000-1 δεν απαιτεί από έναν παραγωγό να το προσέξει. Μια γεννήτρια που γράφει κείμενο χωρίς να ελέγχει κάλυψη παράγει ένα τεχνικά συμμορφούμενο PDF που έχει σιωπηλά χάσει περιεχόμενο, και η απώλεια αναδύεται στην οθόνη ενός πελάτη εβδομάδες αργότερα. Γι' αυτό το χαρακτηριστικό fallback και η αναφορά γλυφών που λείπουν διανέμονται μαζί: η επίλυση όσων μπορούν να επιλυθούν είναι μόνο η μισή δουλειά, και η αναφορά όσων δεν μπόρεσαν να επιλυθούν είναι η άλλη μισή
Το fallback συμβαίνει ανά cluster, όχι ανά code point
Η κοκκομετρία είναι η λεπτομέρεια που ξεχωρίζει μια υλοποίηση που δουλεύει από μια εύλογη. Το κείμενο δεν είναι μια ακολουθία ανεξάρτητων χαρακτήρων. Μια συλλαβή Devanagari, ένα emoji με τροποποιητή τόνου δέρματος, ένα βασικό γράμμα με συνδυαστικά σημάδια: το καθένα είναι ένα cluster που πρέπει να αποδοθεί από μία γραμματοσειρά, επειδή οι αποφάσεις shaping μέσα του εξαρτώνται από πίνακες σε αυτό το face
Το PDFlibPas επιλύει clusters, οπότε ένα cluster που καλύπτει ένα fallback face σχεδιάζεται εξ ολοκλήρου από αυτό το face. Το διαχωρισμό στη μέση ενός cluster και το σχέδιασμα του μισού από την κύρια γραμματοσειρά και του μισού από ένα fallback θα παρήγαγε ένα τεχνικά παρόν αλλά ορατά κατεστραμμένο αποτέλεσμα, το οποίο είναι υποστηρίξιμα χειρότερο από το κενό με το οποίο ξεκινήσατε. Η σειρά run διατηρείται επίσης, οπότε ένα fallback μέσα σε ένα run δεξιά-προς-αριστερά δεν αναδιατάσσει το γύρω κείμενο· ο ίδιος μηχανισμός υποστηρίζει την κάθετη διάταξη που περιγράφεται στο κάθετη γραφή για ιαπωνικά και κινεζικά
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetOrigin(1);
Lib.SetAutomaticFontFallback(1);
// Σειρά αναζήτησης: κερδίζει το πρώτο ταίριασμα, οπότε βάλτε τα ευρύτερα faces τελευταία
Lib.AddFontFallback('Microsoft YaHei'); // Απλοποιημένα κινεζικά
Lib.AddFontFallback('Meiryo'); // Ιαπωνικά
Lib.AddFontFallback('Segoe UI Symbol');
Lib.AddFontFallback('Segoe UI Emoji');
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_REPORT);
Lib.AddTrueTypeFont('Arial', 1); // 1 = ενσωμάτωση του face
Lib.SetTextSize(11);
Lib.DrawText(72, 720, 'Invoice for 北京示例科技有限公司');
Lib.DrawText(72, 700, 'Delivery status: on time');
Lib.SaveToFile('invoice.pdf');
finally
Lib.Free;
end;
end;
Τακτοποιήστε την αλυσίδα σκόπιμα. Η επίλυση παίρνει το πρώτο face που καλύπτει το cluster, οπότε μια ευρεία pan-Unicode γραμματοσειρά τοποθετημένη πρώτη θα κερδίσει σχεδόν τα πάντα και τα προσεκτικά επιλεγμένα faces συγκεκριμένης γραφής σας δεν θα συμβουλευτούν ποτέ. Βάλτε τα συγκεκριμένα faces πρώτα και το catch-all τελευταίο
Αναφορά ή ματαίωση: ποια αποτυχία θέλετε;
Η SetMissingGlyphPolicy δέχεται PDF_MISSING_GLYPH_REPORT, τη συμβατή προεπιλογή, ή PDF_MISSING_GLYPH_ABORT. Στην πολιτική αναφοράς η λειτουργία κειμένου προχωρά, code points που δεν επιλύονται απορρίπτονται όπως πριν, και το καθένα καταγράφεται. Στην πολιτική ματαίωσης η λειτουργία κειμένου απορρίπτεται πριν γραφτεί οποιοδήποτε περιεχόμενο και το LastErrorCode ορίζεται σε 521
Επιλέξτε βάσει του σκοπού του εγγράφου. Ένα σύνολο εσωτερικών αναφορών θα έπρεπε να συνεχίζει να αποδίδεται και να καταγράφει τα κενά, επειδή μια ελαφρώς ελλιπής αναφορά σήμερα είναι καλύτερη από καμία αναφορά καθόλου. Ένα δεσμευτικό συμβόλαιο, ένα τιμολόγιο, ή οτιδήποτε φέρει ένα όνομα θα έπρεπε να ματαιώνεται, επειδή ένας σιωπηλά χαμένος χαρακτήρας σε όνομα μέρους είναι ένα ελάττωμα που θέλετε να ανακαλύψετε στη δική σας διαδικασία και όχι σε μια διαφορά. Η πολιτική ματαίωσης αποτυγχάνει πριν την εγγραφή, οπότε δεν μένει πίσω κανένα μισοσχηματισμένο content stream
var
Lib: TPDFlib;
Report: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.SetMissingGlyphPolicy(PDF_MISSING_GLYPH_ABORT);
// ... δημιουργήστε το έγγραφο ...
if Lib.DrawText(72, 660, CustomerName) <> 1 then
if Lib.LastErrorCode = PDFLIB_ERROR_MISSING_GLYPH then
begin
Report := Lib.GetMissingGlyphReportJSON;
// {"valid":false,"policy":1,"eventCount":1,"events":[
// {"sequence":1,"documentIndex":0,"page":1,"utf16Index":12,
// "codePoint":21271,"unicode":"U+5317","fontName":"Arial",
// "fontType":"TrueType","operation":"DrawText"}]}
EscalateToOperator(Report);
end;
finally
Lib.Free;
end;
end;
Η αναφορά είναι σκόπιμα αναγνώσιμη από μηχανή και οριοθετημένη. Κάθε συμβάν φέρει τη σελίδα, τη θέση UTF-16 μέσα στο string, το code point σε αριθμητική μορφή και σε μορφή U+XXXX, τη γραμματοσειρά που επιλέχθηκε, τον τύπο της και τη λειτουργία που χτύπησε το πρόβλημα, ώστε ένα ticket υποστήριξης να μπορεί να ονομάσει τον ακριβή χαρακτήρα αντί να περιγράφει ένα σύμπτωμα. Ο tracker κρατά τα πιο πρόσφατα 256 συμβάντα, αρκετά για να διαγνωστεί ένα έγγραφο και αρκετά μικρά ώστε μια παθολογική εκτέλεση να μην μπορεί να μετατρέψει τα diagnostics σε πρόβλημα μνήμης
Μέτρηση και σχέδιαση πρέπει να συμφωνούν
Η μέτρηση πλάτους χρησιμοποιεί τις ίδιες αποφάσεις fallback με επίγνωση cluster όπως το σχέδιασμα. Αυτό ακούγεται προφανές και είναι αυτό που τα περισσότερα σπιτικά επίπεδα fallback κάνουν λάθος: επιδιορθώνουν τη διαδρομή σχεδίασης, αφήνουν τη μέτρηση στην κύρια γραμματοσειρά, και κάθε πλαίσιο κειμένου, δεξιά στοίχιση και στήλη πίνακα καταλήγει να υπολογίζεται από πλάτη που δεν ταιριάζουν με ό,τι αποδόθηκε
Επειδή και οι δύο διαδρομές μοιράζονται την επίλυση, ένα string που μετρήθηκε πριν σχεδιαστεί καταλαμβάνει το πλάτος στο οποίο μετρήθηκε, συμπεριλαμβανομένων των runs fallback. Αυτό είναι που κάνει το fallback ασφαλές να ενεργοποιηθεί καθολικά αντί μόνο στα σημεία που ελέγξατε με το χέρι
Μόνο ό,τι χρησιμοποιήσατε ενσωματώνεται
Οι γραμματοσειρές fallback ενσωματώνονται τεμπέλικα: ένα face στην αλυσίδα που δεν επέλυσε ποτέ κάποιο cluster δεν συνεισφέρει τίποτα στην έξοδο. Ένα έγγραφο που περιέχει έναν κινεζικό χαρακτήρα και 5.000 λατινικούς δεν φέρει ένα πλήρες face CJK· φέρει ό,τι παρήγαγε το πέρασμα subsetting για αυτή τη μία γλυφή, κάτι που είναι η συμπεριφορά που περιγράφεται στο βελτιστοποίηση μεγέθους αρχείου και font subsetting
Αυτή η τεμπελιά κάνει μια ευρεία αλυσίδα φθηνή να ρυθμιστεί. Καταχωρίστε τα faces που το σύνολο εγγράφων σας μπορεί να χρειαστεί σε κάθε γλώσσα που εξυπηρετείτε, και κάθε ξεχωριστό PDF πληρώνει μόνο για ό,τι πράγματι χρησιμοποίησε. Για έγγραφα που δεν παράξατε εσείς, όπου οι γραμματοσειρές που λείπουν βρίσκονται ήδη μέσα σε ένα υπάρχον αρχείο, η διαδρομή επιδιόρθωσης είναι διαφορετική και καλύπτεται στο ενσωμάτωση γραμματοσειρών που λείπουν σε υπάρχον PDF
Μια προειδοποίηση ανάπτυξης αξίζει να ειπωθεί ρητά: το fallback επιλύεται έναντι faces εγκατεστημένων στη μηχανή που τρέχει τον κώδικα. Ένας server χωρίς εγκατεστημένες γραμματοσειρές CJK δεν έχει τίποτα ώστε να πέσει πίσω, και η αναφορά θα σας το πει από το πρώτο έγγραφο αντί μετά το πρώτο παράπονο. Παραδώστε τις γραμματοσειρές που εξαρτάστε, και επιβεβαιώστε τη νομική δυνατότητα ενσωμάτωσής τους
Το PDFlibPas είναι μια βιβλιοθήκη PDF για Delphi, C++Builder και Lazarus με αντίστοιχα interfaces DLL και ActiveX, οπότε τα API fallback και missing-glyph είναι διαθέσιμα και από callers μη Pascal. Πλήρης τεκμηρίωση βρίσκεται στη σελίδα του PDFlibPas Delphi PDF library