Το αρχείο ανοίγει καθαρά στον υπολογιστή σας. Το Acrobat το εμφανίζει, η προεπισκόπηση εκτύπωσης φαίνεται σωστή, κάθε σελίδα υπάρχει. Έπειτα πηγαίνει στο τυπογραφείο, ή στο σύστημα αρχειοθέτησης που απορροφά τη μηνιαία παρτίδα σας, και επιστρέφει απορριφθέν: εικόνες RGB σε μια εργασία CMYK, χωρίς κλειδί /Trapped, ένα output intent που δεν ταιριάζει με το πιεστήριο. Τίποτα δεν ήταν λάθος στο έγγραφο που θα μπορούσε να δει κανείς. Ήταν λάθος έναντι ενός προφίλ, και το προφίλ ελέγχθηκε κάπου όπου εσείς δεν ήσασταν. Preflight είναι το όνομα prepress για αυτόν τον έλεγχο, και η πραγματική ερώτηση είναι πού ανήκει όταν τα PDF βγαίνουν από τον δικό σας κώδικα Delphi αντί από την επιφάνεια εργασίας ενός σχεδιαστή
Το HotPDF δεν σας δίνει μια συνάρτηση preflight για να καλέσετε. Το στοιχείο φέρει ένα παράθυρο αναφοράς preflight στο GUI demo του, αλλά δεν υπάρχει API από πίσω του που μια υπηρεσία ή ένα build script να μπορεί να καλέσει, και το να προσποιείστε το αντίθετο θα σας έστελνε να ψάχνετε μια μέθοδο που δεν υπάρχει. Αυτό ακούγεται σαν κενό μέχρι να παρατηρήσετε ότι για αρχεία που παράγετε εσείς οι ίδιοι, το να καλείτε έναν επικυρωτή στη δική σας έξοδο είναι έτσι κι αλλιώς το λάθος σχήμα. Ήδη ελέγχετε κάθε ιδιότητα που θα επιθεωρούσε ένας επικυρωτής. Ο χρήσιμος διαχωρισμός είναι να κάνετε τη γεννήτρια ανίκανη να εκπέμψει ένα κακό αρχείο, και έπειτα να το αποδείξετε με ένα εργαλείο που δεν γράψατε εσείς
Γιατί ελέγχετε τη δική σας έξοδο διαφορετικά
Το παραδοσιακό preflight υποθέτει ένα άγνωστο αρχείο. Κάποιος σχεδιαστής, κάποια άλλη εφαρμογή, κάποια άγνωστη αλυσίδα επεξεργασιών το παρήγαγε, και το επιθεωρείτε επειδή δεν έχετε ιδέα τι υπάρχει μέσα. Ένα έγγραφο που παρήγαγε ο κώδικάς σας δεν είναι άγνωστο. Ενσωμάτωση γραμματοσειρών, χρωματικός χώρος, output intent, το μπλοκ μεταδεδομένων: το πρόγραμμά σας τα αποφάσισε όλα αυτά λίγα χιλιοστά του δευτερολέπτου προτού το αρχείο φτάσει στον δίσκο. Το να το επιθεωρείτε αργότερα για να ανακαλύψετε επιλογές που μόλις κάνατε είναι άσκοπη δουλειά. Η φθηνότερη κίνηση είναι να περιορίσετε αυτές τις επιλογές ώστε ένα μη συμμορφούμενο αρχείο να μην υπάρχει ποτέ για να πιαστεί
Υπάρχει επίσης ένας λόγος αξιοπιστίας για να κρατάτε την επαλήθευση εξωτερική. Μια βιβλιοθήκη που ευλογεί τη δική της έξοδο βαθμολογεί τη δική της εξέταση. Όταν το σύστημα αρχειοθέτησης ενός πελάτη ή το RIP ενός τυπογραφείου απορρίπτει το αρχείο σας, το "το στοιχείο μας λέει ότι είναι εντάξει" δεν έχει καμία βαρύτητα. Μια κρίση από το veraPDF ή το Acrobat έχει, επειδή η άλλη πλευρά τρέχει τα ίδια εργαλεία
Κάντε τη συμμόρφωση ρύθμιση, όχι λίστα ελέγχου
Το επίπεδο πρόληψης είναι απλώς διαμόρφωση. Ορίστε το PDFACompliance ή το PDFXCompliance πριν το BeginDoc και το HotPDF κρατά τους αντίστοιχους κανόνες για ολόκληρο το πέρασμα δημιουργίας: ενσωματώνει γραμματοσειρές, παρακολουθεί τη χρήση DeviceRGB και DeviceCMYK έναντι του output intent που δηλώσατε, και αρνείται χαρακτηριστικά που απαγορεύει το προφίλ. Οι αντιφάσεις αναδύονται στο EndDoc, όπου οι πύλες συμμόρφωσης εγείρουν εξαίρεση αντί να κυκλοφορήσουν σιωπηλά κάτι που θα αποτύχει παρακάτω στη ροή. Μόλις αποθηκευτεί το αρχείο, οι ίδιες ιδιότητες διαβάζουν πίσω τι πράγματι επιβλήθηκε, που είναι το ένα γεγονός που το log του αγωγού σας χρειάζεται περισσότερο:
// Μετά το EndDoc: καταγραφή των επιβεβλημένων προφίλ μαζί με τα μεταδεδομένα εκτέλεσης
if Pdf.PDFACompliance <> '' then
Log('Generated as PDF/A level ' + Pdf.PDFACompliance);
if Pdf.PDFXCompliance <> '' then
Log('Generated as PDF/X profile ' + Pdf.PDFXCompliance);
Βάλτε αυτές τις σημαίες στην ίδια γραμμή log με το hash δεδομένων εισόδου και την έκδοση του HotPDF. Την ημέρα που ένας επικυρωτής και η γεννήτριά σας διαφωνούν για ένα αρχείο, αυτή η γραμμή σας λέει ποιο template το παρήγαγε και ποιο build της βιβλιοθήκης ήταν φορτωμένο, και η συζήτηση που διαφορετικά θα έτρωγε ένα απόγευμα γίνεται ένα grep. Τα output intent, τα προφίλ ICC, και η ετικετοποίηση που κρύβονται πίσω από αυτές τις σημαίες αναλύονται στον οδηγό για έξοδο PDF/A, PDF/X και PDF/UA με το HotPDF
Μια φθηνή πρώτη πύλη για αρχεία που δεν παράγατε εσείς
Δεν είναι κάθε αγωγός καθαρά γεννητικός. Πελάτες ανεβάζουν PDF, σαρωτές τα αφήνουν σε έναν φάκελο, συνεργάτες τα επισυνάπτουν σε email. Το να περνάτε καθένα από αυτά μέσα από έναν πλήρη δομικό επικυρωτή σπαταλά χρόνο ουράς σε αρχεία που δεν θα ανοίξουν καν. Το Direct File API του HotPDF διαβάζει αρκετή δομή ενός αρχείου για να απαντήσει "είναι αυτό καν χρησιμοποιήσιμο PDF" χωρίς να φορτώνει ολόκληρο το δέντρο αντικειμένων, κάτι που το κάνει καλό μέρος για να αποτύχετε γρήγορα:
function TriagePdf(Pdf: THotPDF; const FileName: string): Boolean;
var
Handle, Pages: Integer;
begin
Result := False;
Handle := Pdf.DAOpenFileReadOnly(FileName, '');
if Handle <= 0 then
Exit; // δομικά μη αναγνώσιμο: καραντίνα, μην επικυρώνετε
try
Pages := Pdf.DAGetPageCount(Handle);
Result := Pages > 0;
finally
Pdf.DACloseFile(Handle);
end;
end;
Δύο γεγονότα για αυτό το API καθορίζουν πώς το τυλίγετε. Η συντόμευση επίπεδης μνήμης ισχύει μόνο για μη κρυπτογραφημένη είσοδο· δώστε στο DAOpenFileReadOnly έναν κωδικό πρόσβασης και πέφτει σιωπηλά σε πλήρη ανάλυση, οπότε ένα αρχείο που γνωρίζετε ότι είναι κρυπτογραφημένο πρέπει να περάσει μέσω του DecryptFile σε ένα απλό αντίγραφο εργασίας πριν τη διαλογή. Και το DAGetPageCount δεν σημαίνει τίποτα σε ένα handle που δεν άνοιξε καθαρά, οπότε ο έλεγχος handle παραμένει αυστηρός και ένα μη θετικό αποτέλεσμα είναι απόρριψη, όχι επανάληψη. Περισσότερα από αυτά τα μοτίβα βρίσκονται στο άρθρο για το Direct File API για ροές εργασίας μεγάλων PDF
veraPDF, εκτελούμενο ως μέρος του build
Για οτιδήποτε διεκδικείτε ως PDF/A ή PDF/UA, το veraPDF είναι ο επικυρωτής που πρέπει να συνδέσετε. Τρέχει headless, δέχεται μια παρτίδα, εκπέμπει XML ή JSON, και ονομάζει κάθε αποτυχία κατά τη ρήτρα ISO της, οπότε μια αποτυχία κανόνα έναντι της ρήτρας 6.2.2 του ISO 19005-1 δείχνει κατευθείαν πίσω σε μια ρύθμιση της γεννήτριας αντί να σας αφήνει να μαντεύετε. Το να το οδηγήσετε από το Delphi είναι απλός έλεγχος διεργασιών:
function RunVeraPdf(const PdfFile, ReportFile: string): Cardinal;
var
Cmd: string;
SI: TStartupInfo;
PI: TProcessInformation;
begin
Cmd := Format('cmd /c verapdf.bat --format xml "%s" > "%s"',
[PdfFile, ReportFile]);
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(SI);
if not CreateProcess(nil, PChar(Cmd), nil, nil, False,
CREATE_NO_WINDOW, nil, nil, SI, PI) then
RaiseLastOSError;
try
WaitForSingleObject(PI.hProcess, 120000); // περιόρισε την αναμονή ανά αρχείο
GetExitCodeProcess(PI.hProcess, Result);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end;
Αυτό το timeout δικαιολογεί τον εαυτό του. Ένα κακοδιατυπωμένο αρχείο μπορεί να οδηγήσει οποιονδήποτε parser σε μια γωνία από την οποία ποτέ δεν βγαίνει, και μια απεριόριστη αναμονή μέσα σε έναν worker ουράς σέρνει μαζί της τον υπόλοιπο της ουράς. Περιορίστε την αναμονή, δώστε σε ένα timeout τον δικό του κωδικό αποτυχίας, και βάλτε το αρχείο στην άκρη για έναν άνθρωπο. Όταν διαβάζετε το αποτέλεσμα, αναλύστε το XML για αναγνωριστικά κανόνων, όχι για το ευανάγνωστο κείμενο. Τα ID κανόνων επιβιώνουν από αναβαθμίσεις επικυρωτή· η διατύπωση των μηνυμάτων όχι, και ένας σταθερός κωδικός είναι κάτι που ένας μηχανικός υποστήριξης μπορεί να αναζητήσει σε παλιά tickets
Το πώς τρέχετε την παρτίδα έχει τόση σημασία όση το αν περνά κάθε αρχείο. Μία διεργασία ανά αρχείο, όχι μία ανά παρτίδα, ώστε μια δηλητηριώδης είσοδος να σας κοστίζει μόνο το timeout εκείνου του αρχείου και τίποτα άλλο. Περιορίστε τον αριθμό διεργασιών επικυρωτή στον αριθμό πυρήνων, επειδή η κατασκευή της αναφοράς XML είναι CPU-bound και η υπερδέσμευση απλά τα καθυστερεί όλα. Και βάλτε ένα ανώτατο όριο μεγέθους στην είσοδο, επειδή ένα σαρωμένο βιβλίο δύο gigabyte θα μονοπωλήσει την ουρά όσο υπομονετικός κι αν είναι ο parser. Τίποτα από αυτά δεν είναι preflight με την αυστηρή έννοια. Είναι η διαφορά ανάμεσα σε μια πύλη που επιβιώνει τον όγκο τέλους μήνα και μία που απενεργοποιείται το πρώτο βράδυ που μπλοκάρει τον αγωγό στις 2 τα ξημερώματα
Το PDF/X είναι εκεί όπου αυτό αστοχεί. Το veraPDF δεν το επικυρώνει, οπότε ο πρακτικός έλεγχος παραμένει το Preflight του Acrobat με το προφίλ ISO 15930 που κατονόμασε το τυπογραφείο σας. Το Acrobat θέλει έναν άνθρωπο, κάτι που σημαίνει δειγματοληψία αντί για πλήρη κάλυψη: το πρώτο αρχείο από ένα νέο template, συν μια μικρή τυχαία κλήρωση από κάθε παρτίδα, ενώ η αυτοματοποιημένη πύλη χειρίζεται ό,τι μπορεί να χειριστεί χωρίς έναν. Ένας δειγματοληπτικός έλεγχος που πράγματι τρέχει νικά μια πλήρη αυτοματοποίηση που παραμένει μισοτελειωμένη για πάντα
Μια αναφορά που θα θέλετε ακόμη σε έναν χρόνο
Μια πύλη preflight αποδίδει δύο φορές. Μία όταν σταματά ένα κακό αρχείο στην είσοδο, και ξανά πολύ αργότερα όταν κάποιος ρωτά γιατί ένα συγκεκριμένο αρχείο πέρασε. Αυτή η δεύτερη στιγμή είναι αυτή που πρέπει να καθορίζει τη μορφή, επειδή είναι αυτή όπου μια αδύναμη αναφορά σας αφήνει αβοήθητους. Για κάθε αρχείο που ελέγχεται, κρατήστε το hash εισόδου, τις σημαίες συμμόρφωσης της γεννήτριας και την έκδοση της βιβλιοθήκης από τη γραμμή log παραπάνω, το όνομα και την έκδοση του επικυρωτή, το προφίλ έναντι του οποίου ελέγχθηκε, την επιτυχία ή αποτυχία, και τα ID αποτυχημένων κανόνων με αριθμούς σελίδων όπου τα δίνει ο επικυρωτής. Αποθηκεύστε αυτή την αναφορά δίπλα στο αρχείο που περιγράφει. Βάλτε την σε ένα ξεχωριστό σύστημα και αυτό το σύστημα θα αποσυρθεί προτού αποσυρθεί το αρχείο που τεκμηριώνει
Οι εξαιρέσεις πρέπει επίσης να καταγράφονται. Όταν ένας πελάτης επιμένει να κυκλοφορήσει ένα αρχείο που η πύλη δεν εγκρίνει, η απάντηση δεν είναι να χαλαρώσετε τον κανόνα για όλους. Καταγράψτε ποιος ενέκρινε αυτό το αρχείο, σε ποια βάση, και μέχρι ποια ημερομηνία, έπειτα επισυνάψτε αυτή την παρέκκλιση στην αναφορά του. Μια παρέκκλιση με όνομα και λήξη είναι μια απόφαση που κάποιος την κατέχει. Ένας έλεγχος που σχολιάστηκε "προσωρινά" είναι ένα περιστατικό που περιμένει την ημερομηνία του
Μία ακόμη συνήθεια αποσβένεται από μόνη της: όταν ένα αρχείο αποτυγχάνει, αντιγράψτε το σε έναν επώνυμο φάκελο οπισθοδρόμησης προτού το αγγίξει κανείς. Σχεδόν κάθε πρόβλημα preflight που αξίζει τον κόπο του debugging εντοπίζεται πίσω σε μία συγκεκριμένη είσοδο, και οι ομάδες που κρατούν αυτές τις εισόδους διορθώνουν την επανεμφάνιση μέσα σε μία ώρα αντί να περιμένουν να ξαναεμφανιστεί στην παραγωγή. Οι ιδιότητες συμμόρφωσης και το Direct File API που παρουσιάζονται εδώ είναι μέρος του HotPDF Delphi Component για Delphi και C++Builder, του οποίου η τεκμηρίωση καλύπτει κάθε κλήση πλήρως