Το PDFium Component για Delphi επικυρώνει έγγραφα PDF/X έτοιμα για εκτύπωση μέσω της TPdf.ValidatePdfX, η οποία υλοποιεί τον έλεγχο κατά το πρότυπο ISO 15930 σε δύο επίπεδα: οκτώ ελέγχους περιεχομένου σε επίπεδο byte (απαγορευμένη συμπίεση LZW, JavaScript, πεδία φόρμας, αναφορές OPI, ελλείπον TrimBox, μη ορισμένο Trapped κ.ά.) συν ένα πέρασμα στο μοντέλο αντικειμένων του PDFium που χρησιμοποιεί την FPDFFont_GetIsEmbedded για να επαληθεύσει την ενσωμάτωση γραμματοσειρών σε κάθε αντικείμενο κειμένου κάθε σελίδας. Το αποτέλεσμα είναι μια εγγραφή TPdfXValidationResult που ονομάζει το ανιχνευμένο επίπεδο συμμόρφωσης και παραθέτει κάθε παραβίαση ως καταχωρισμένο enum, ώστε η Delphi εφαρμογή σας να μπορεί να πει στον πελάτη ακριβώς γιατί το αρχείο θα απορριφθεί στο τυπογραφείο προτού κανείς εκθέσει πλάκα εκτύπωσης
Εάν έχετε στείλει ποτέ μια εργασία σε εμπορικό εκτυπωτή και την λάβατε πίσω με μια μονολεκτική απόρριψη — "δεν υπάρχει TrimBox", "οι γραμματοσειρές δεν είναι ενσωματωμένες", "το Trapped δεν έχει οριστεί" — γνωρίζετε το κόστος του να το μάθετε αργά. Το PDF/X είναι το prepress αντίστοιχο του PDF/A: όπου το αρχειακό PDF/A εγγυάται ότι ένα έγγραφο θα αποδίδεται πανομοιότυπα μετά από δεκαετίες, το PDF/X εγγυάται ότι ένα έγγραφο θα διαχωρίζεται, θα εικονογραφείται και θα ξακρίζεται πανομοιότυπα στο RIP κάποιου άλλου αύριο το πρωί. Τα δύο πρότυπα μοιράζονται κοινό μηχανισμό (ταυτοποίηση XMP, OutputIntents, ενσωματωμένα προφίλ ICC) αλλά απαντούν σε διαφορετικά ερωτήματα, γι' αυτό και το συστατικό αποστέλλει ξεχωριστούς επικυρωτές για το καθένα — η πλευρά του PDF/A καλύπτεται στο άρθρο σχετικά με την επικύρωση preflight PDF/A με το PDFium Component
Τι απαιτεί στην πραγματικότητα το ISO 15930 από ένα έτοιμο προς εκτύπωση PDF;
Το ISO 15930 υπάρχει για να καταστήσει δυνατή την τυφλή ανταλλαγή (blind exchange): ένας σχεδιαστής παραδίδει ένα αρχείο σε έναν εκτυπωτή με τον οποίο δεν έχει μιλήσει ποτέ, και ο εκτυπωτής μπορεί να παράγει σωστό αποτέλεσμα χωρίς τηλεφώνημα, χωρίς email για ελλείπουσα γραμματοσειρά, και χωρίς συνδεδεμένη εικόνα που ξεχάστηκε στον φορητό υπολογιστή του σχεδιαστή. Κάθε κανόνας στο πρότυπο εξυπηρετεί αυτόν το στόχο. Οι γραμματοσειρές πρέπει να είναι ενσωματωμένες επειδή δεν μπορεί να υποτεθεί ότι το RIP λήψης τις κατέχει. Οι εξωτερικές αναφορές απαγορεύονται επειδή το αρχείο πρέπει να είναι πλήρες από μόνο του. Τα διαδραστικά χαρακτηριστικά απαγορεύονται επειδή το μελάνι δεν διαθέτει handler για κλικ (onclick)
Το PDFium Component αναγνωρίζει τρεις οικογένειες συμμόρφωσης και τις αναφέρει μέσω του enum TPdfXConformance στο αποτέλεσμα της επικύρωσης: pxc1a για το PDF/X-1a:2001 (ISO 15930-1, η αυστηρή βάση CMYK-plus-spot σε PDF 1.3/1.4), pxc3 για το PDF/X-3:2002 (ISO 15930-3, το οποίο δέχεται RGB, Lab και χρώματα διαχειριζόμενα με ICC), και pxc4 για το PDF/X-4:2010 (ISO 15930-7, το οποίο τελικά επιτρέπει ζωντανή διαφάνεια και επίπεδα (layers) σε μια βάση PDF 1.6). Ένα αρχείο που δεν φέρει καθόλου ταυτοποίηση PDF/X επιστρέφει ως pxcNone, το οποίο είναι από μόνο του μια χρήσιμη απάντηση: το έγγραφο δεν ισχυρίστηκε ποτέ ότι είναι έτοιμο για εκτύπωση, και όλα τα άλλα που αναφέρει ο επικυρωτής εξηγούν τι θα χρειαζόταν για να φτάσει εκεί
Οι απαγορεύσεις βγάζουν νόημα μόλις σκεφτείτε σαν προμηθευτής RIP. Η /LZWDecode απαγορεύεται σε κάθε παραλλαγή PDF/X, έτσι ώστε ένας συμμορφούμενος καταναλωτής να μην εξαρτάται ποτέ από ένα φίλτρο με ιστορικό συμβατότητας και αδειοδότησης· το Flate κάνει την ίδια δουλειά χωρίς αυτό το βάρος. Η JavaScript, τα πεδία AcroForm και τα λεξικά πρόσθετων ενεργειών /AA απαγορεύονται επειδή ένα αρχείο εκτύπωσης πρέπει να είναι μια σταθερή περιγραφή σημαδιών στο χαρτί — οτιδήποτε μπορεί να αλλάξει την εμφάνιση κατά το άνοιγμα σπάει την εγγύηση ότι αυτό που ελέγχθηκε είναι αυτό που εκτυπώνεται. Τα placeholders OPI (Open Prepress Interface) απαγορεύονται επειδή αποτελούν, από σχεδιασμό, αναφορές σε εικόνες υψηλής ανάλυσης αποθηκευμένες κάπου αλλού, και το "κάπου αλλού" είναι ακριβώς αυτό που απαγορεύει η τυφλή ανταλλαγή
Γιατί τα τυπογραφεία απορρίπτουν PDF χωρίς TrimBox;
Το TrimBox είναι η τελική σελίδα — το ορθογώνιο που απομένει μετά την κοπή της λαιμητόμου (guillotine). Το MediaBox, το οποίο έχει κάθε σελίδα PDF, είναι απλώς το φύλλο: περιλαμβάνει ξάκρισμα (bleed), σημάδια κοπής (crop marks), στόχους σύμπτωσης (registration targets) και χρωματικές μπάρες. Λογισμικό imposition τοποθετεί τις σελίδες σε ένα φύλλο πιεστηρίου βάσει των TrimBoxes τους· χωρίς αυτό, ο χειριστής πρέπει να μαντέψει πού τελειώνει πραγματικά η επαγγελματική σας κάρτα, και μια λάθος μαντεψιά κόβει το bleed σας ή αφήνει μια λευκή λωρίδα σε μια άκρη. Γι' αυτό το ISO 15930 απαιτεί ένα TrimBox (ή ένα ArtBox) σε κάθε σελίδα, και γι' αυτό η ValidatePdfX εγείρει το pvxiMissingTrimBox όταν δεν βρίσκεται κλειδί /TrimBox σε καμία σελίδα του εγγράφου
Το κλειδί /Trapped απαντά σε μια διαφορετική ερώτηση παραγωγής. Trapping είναι η τεχνική προεκτύπωσης ελαφράς επικάλυψης γειτονικών χρωμάτων, έτσι ώστε μια μικρή απόκλιση σύμπτωσης του πιεστηρίου να μην ανοίγει λευκά κενά μεταξύ τους. Ο εκτυπωτής πρέπει να γνωρίζει εάν αυτή η εργασία έχει ήδη γίνει: το trapping ενός ήδη επεξεργασμένου αρχείου διπλασιάζει τις επικαλύψεις, ενώ η παράλειψη trapping σε ένα μη επεξεργασμένο αρχείο εγκυμονεί κίνδυνο εμφάνισης ορατών κενών. Το PDF/X απαιτεί επομένως από το λεξικό Info να δηλώνει ρητά /Trapped /True ή /Trapped /False — ένα κλειδί που λείπει ή η τιμή /Unknown αναγκάζει έναν άνθρωπο να επιθεωρήσει το αρχείο, που είναι ακριβώς η συζήτηση που η τυφλή ανταλλαγή είχε σκοπό να εξαλείψει. Το συστατικό επισημαίνει αυτό ως pvxiTrappedNotSet
Εκτέλεση της επικύρωσης δύο επιπέδων με την TPdf.ValidatePdfX
Η TPdf.ValidatePdfX δεν δέχεται ορίσματα και επιστρέφει μια εγγραφή TPdfXValidationResult με τρία μέλη: Conformance (τη ανιχνευθείσα παραλλαγή PDF/X), Issues (ένα σύνολο Pascal τιμών TPdfXValidationIssue), και έναν βοηθό IsCompliant. Εσωτερικά σειριοποιεί το φορτωμένο έγγραφο σε μια ροή μνήμης, εκτελεί τον επιθεωρητή επιπέδου byte πάνω του, και στη συνέχεια διατρέχει το μοντέλο αντικειμένων του PDFium για τον έλεγχο ενσωμάτωσης ανά γραμματοσειρά. Μια ελάχιστη πύλη preflight μοιάζει με αυτό:
uses PDFium, FPdfPdfx;
procedure CheckPrintReadiness(const FileName: string);
var
Pdf: TPdf;
Res: TPdfXValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True;
Res := Pdf.ValidatePdfX;
Writeln('Detected conformance: ',
Ord(Res.Conformance)); // pxc1a, pxc3, pxc4, pxcNone...
if Res.IsCompliant then
Writeln('PDF/X checks passed')
else
begin
if pvxiMissingTrimBox in Res.Issues then
Writeln('REJECT: no /TrimBox on the pages');
if pvxiTrappedNotSet in Res.Issues then
Writeln('REJECT: /Trapped missing or /Unknown');
if pvxiPdfiumFontNotEmbedded in Res.Issues then
Writeln('REJECT: a page uses a non-embedded font');
if pvxiLzwForbidden in Res.Issues then
Writeln('REJECT: LZWDecode filter present');
end;
finally
Pdf.Free;
end;
end;
Επειδή το Issues είναι ένα συνηθισμένο σύνολο (set) Pascal, μπορείτε να το χωρίσετε όπως απαιτεί η ροή εργασίας σας — αντιμετωπίστε τα δομικά προβλήματα ως σκληρές απορρίψεις, αντιμετωπίστε το pvxiMissingTitle (μια σύσταση "SHOULD" στο πρότυπο, όχι υποχρέωση "MUST") ως προειδοποίηση, και καταγράψτε τα υπόλοιπα. Ο ίδιος τύπος εγγραφής τροφοδοτεί επίσης τη γεννήτρια αναφορών του συστατικού, οπότε αν προτιμάτε να εκπέμψετε ένα αναγνώσιμο από τον άνθρωπο έγγραφο αντί να κάνετε διακλαδώσεις σε enums, το μοτίβο στο άρθρο για τη δημιουργία μιας CLI αναφοράς preflight με το PDFium Component εφαρμόζεται στο PDF/X χωρίς αλλαγές
Tι πιάνει το επίπεδο byte — και τι χάνει
Το επίπεδο byte είναι μια σάρωση tokens πάνω στα δομικά bytes του εγγράφου με σβησμένα τα σώματα των ροών, οπότε ένα JPEG που τυχαίνει να περιέχει το μοτίβο byte /JavaScript δεν μπορεί να προκαλέσει ψευδώς θετικό αποτέλεσμα. Πέρα από τους ελέγχους δεικτών (XMP pdfxid:GTS_PDFXVersion, OutputIntent με ενσωματωμένο προφίλ ICC, trailer /ID, την απαγόρευση κρυπτογράφησης), το πέρασμα περιεχομένου προσθέτει οκτώ ελέγχους, καθένας με τη δική του τιμή enum:
pvxiLzwForbidden— ένα φίλτρο/LZWDecodeεμφανίζεται οπουδήποτε στο αρχείο (απαγορευμένο σε όλες τις παραλλαγές PDF/X)pvxiJavaScriptForbidden— μια ενέργεια ή δέντρο ονομάτων/JavaScriptείναι παρόνpvxiFormFieldsForbidden— υπάρχει λεξικό/AcroFormή καταχώριση/XFApvxiAdditionalActions— υπάρχει λεξικό πρόσθετων ενεργειών/AApvxiEmbeddedFilesForbidden— υπάρχει καταχώριση/EmbeddedFilesή σχολιασμός/FileAttachmentpvxiOpiForbidden— μια καταχώριση/OPIή/Alternatesαναφέρεται σε αντικαταστάσιμο περιεχόμενο εικόναςpvxiMissingTrimBox— δεν βρέθηκε/TrimBoxσε καμία σελίδαpvxiTrappedNotSet— το/Trappedαπουσιάζει ή έχει οριστεί σε/Unknown
Η σάρωση byte είναι γρήγορη και δεν χρειάζεται μηχανή απόδοσης, αλλά έχει ένα εγγενές τυφλό σημείο με τις γραμματοσειρές: σε αυτό το επίπεδο ο επιθεωρητής μπορεί να εφαρμόσει μόνο μια αδρή ευρετική μέθοδο — επισημαίνει ένα έγγραφο όταν δεν βρίσκει καθόλου ενσωματωμένο πρόγραμμα γραμματοσειράς. Ένα αρχείο με εννέα ενσωματωμένες γραμματοσειρές και μία γραμματοσειρά συστήματος που παρεισέφρησε φαίνεται μια χαρά σε μια σάρωση byte. Αυτό το μοναδικό κενό είναι ο λόγος που υπάρχει το δεύτερο επίπεδο
Ενσωμάτωση ανά γραμματοσειρά μέσω του μοντέλου αντικειμένων του PDFium
Το επίπεδο μοντέλου αντικειμένων του PDFium Component απαντά στην ερώτηση για τις γραμματοσειρές με ακρίβεια. Μετά το πέρασμα σε επίπεδο byte, η TPdf.ValidatePdfX διατρέχει κάθε σελίδα, ζητά από την FPDFPage_CountObjects τη λίστα αντικειμένων, και για κάθε αντικείμενο κειμένου επιλύει τη λαβή της γραμματοσειράς μέσω της FPDFTextObj_GetFont και ρωτά την FPDFFont_GetIsEmbedded. Μία μη ενσωματωμένη γραμματοσειρά οπουδήποτε στο έγγραφο προσθέτει το pvxiPdfiumFontNotEmbedded στο σύνολο των προβλημάτων. Η διαδρομή βραχυκυκλώνει σε δύο επίπεδα — σταματά να σαρώνει αντικείμενα σε μια σελίδα, και σταματά να φορτώνει άλλες σελίδες, τη στιγμή που επιβεβαιώνεται το πρόβλημα — οπότε σε έναν κατάλογο 300 σελίδων με παραβίαση, η ετυμηγορία φτάνει συχνά από την πρώτη σελίδα
Δύο οριακές σημειώσεις που αξίζει να γνωρίζετε. Πρώτον, αυτό το επίπεδο απαιτεί τη βιβλιοθήκη PDFium φορτωμένη και εκδόσεις που εξάγουν την FPDFFont_GetIsEmbedded· όταν η εξαγωγή απουσιάζει, ο έλεγχος παρακάμπτεται αντί να αποτύχει, οπότε ένα παλαιότερο DLL δεν παράγει ποτέ ψευδείς απορρίψεις. Δεύτερον, ο έλεγχος απαντά στο "ενσωματωμένη ή όχι" και τίποτα περισσότερο — δεν διακρίνει την πλήρη ενσωμάτωση από το subsetting, ούτε επιθεωρεί την κάλυψη γλυφών. Όταν ένα αρχείο αποτυγχάνει και πρέπει να γνωρίζετε ποια γραμματοσειρά σε ποια σελίδα φταίει, οι τεχνικές απαρίθμησης στο άρθρο σχετικά με την ανάλυση ιδιοτήτων γραμματοσειρών PDF με το PDFium στο Delphi συνεχίζουν ακριβώς από εκεί που σταματά η boolean τιμή του επικυρωτή
Επικύρωση ροών χωρίς φόρτωση εγγράφου — ή του DLL
Ο επιθεωρητής επιπέδου byte εκτίθεται επίσης ως αυτόνομη συνάρτηση, η ValidatePdfXCompliance(Source: TStream) στη μονάδα FPdfPdfx, και είναι καθαρή Object Pascal χωρίς εξάρτηση από το DLL του PDFium. Αυτό την καθιστά κατάλληλη για ανάπτυξη σε μέρη όπου μια μηχανή απόδοσης δεν είναι επιθυμητή: μια ελαφριά πύλη μεταφόρτωσης σε έναν διακομιστή ιστού, μια εργασία CI που ελέγχει δημιουργικά έργα, ή μια υπηρεσία Lazarus σε μια πλατφόρμα όπου προτιμάτε να μην στείλετε εγγενή δυαδικά αρχεία (native binaries). Τροφοδοτήστε την με οποιαδήποτε ροή που υποστηρίζει αναζήτηση (seekable stream):
uses Classes, FPdfPdfx;
function QuickPdfXGate(const FileName: string): Boolean;
var
Fs: TFileStream;
Res: TPdfXValidationResult;
begin
Fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfXCompliance(Fs);
Result := Res.IsCompliant and (Res.Conformance <> pxcNone);
finally
Fs.Free;
end;
end;
Η ανταλλαγή είναι ξεκάθαρη: η αυτόνομη διαδρομή εκτελεί τους ελέγχους δεικτών και όλους τους οκτώ ελέγχους περιεχομένου, αλλά όχι το επίπεδο του PDFium ανά γραμματοσειρά, οπότε η ετυμηγορία της για τις γραμματοσειρές βασίζεται στην αδρή ευρετική μέθοδο. Μια λογική αρχιτεκτονική χρησιμοποιεί τη ValidatePdfXCompliance ως την οικονομική πρώτη πύλη και κρατά την πλήρη TPdf.ValidatePdfX για αρχεία που περνούν επιτυχώς αυτόν τον έλεγχο
Πού τελειώνει αυτός ο επικυρωτής και πού αρχίζει ένα πλήρες preflight
Η ειλικρίνεια έχει σημασία στα εργαλεία preflight, οπότε ορίστε το όριο. Η ValidatePdfX επαληθεύει δείκτες ταυτοποίησης, δομικές απαγορεύσεις, κλειδιά γεωμετρίας σελίδας, τη δήλωση Trapped, και την ενσωμάτωση γραμματοσειρών μέχρι το επίπεδο μεμονωμένων αντικειμένων κειμένου. Δεν μετρά τη συνολική κάλυψη μελανιού (total ink coverage), δεν επικυρώνει ότι κάθε χρωματικός χώρος είναι νόμιμος για την επικαλούμενη παραλλαγή (για παράδειγμα, τον κανόνα μόνο CMYK του X-1a), δεν ελέγχει την ανάλυση της εικόνας έναντι της οθόνης γραμμών (line screen), ούτε αξιολογεί τη συμπεριφορά overprint και transparency flattening — αυτά απαιτούν μια μηχανή preflight με διαχείριση χρωμάτων, και η τεκμηρίωση της ίδιας της μονάδας αναφέρει ότι πρέπει να συνδυάζεται με μια τέτοια για την τελική πιστοποίηση. Αυτό που σας δίνει ο έλεγχος δύο επιπέδων είναι το 80% των απορρίψεων που είναι δομικές και ανιχνεύσιμες νωρίς, εντοπισμένες σε χιλιοστά του δευτερολέπτου μέσα στον δικό σας Delphi κώδικα αντί για το αυριανό email από το τυπογραφείο
Αμφότερα τα επίπεδα επικύρωσης, τα API εισαγωγής δεικτών PDF/X για την παραγωγή συμμορφούμενης εξόδου, καθώς και οι επικυρωτές PDF/A, PDF/UA, PDF/E και PDF/VT που μοιράζονται την ίδια αρχιτεκτονική αποστέλλονται στο PDFium Component για Delphi και C++Builder — ένα συστατικό, από την απόδοση έως τον έλεγχο preflight