Τεχνικό Άρθρο

Έγγραφα μηχανικής PDF/E-1 σε Delphi με PDFlibPas

Το PDF/E-1 είναι το archival profile για έγγραφα μηχανικής, και το PDFlibPas το υλοποιεί ως author mode που ενεργοποιείτε με SetPDFEMode μαζί με ένα bounded preflight που διαβάζει τα content streams τελεστή προς τελεστή. Το profile δεν είναι PDF/A με άλλη ετικέτα: έχει δικό του identification namespace, δική του απαίτηση lifecycle metadata, και έναν κανόνα που κάνει την επικύρωση περιεχομένου αυστηρότερη από οποιοδήποτε archival profile έχετε ξαναδεί

Τα παραδοτέα μηχανικής είναι ο λόγος που υπάρχει το profile. Ένα σετ σχεδίων που πρέπει να είναι αναγνώσιμο και αποδείξιμα αμετάβλητο είκοσι χρόνια μετά, με ιστορικό αναθεωρήσεων που επιβιώνει, και με χρώμα που σημαίνει το ίδιο πράγμα στο plotter του διπλανού κτιρίου. Αυτές οι απαιτήσεις παράγουν μια προδιαγραφή με αιτήματα που κάθονται κυρίως έξω από το περιεχόμενο της σελίδας, στα metadata και τη διαχείριση χρώματος, που είναι ακριβώς εκεί όπου ένας generic PDF writer τα κάνει λάθος

Δική του ταυτοποίηση, όχι παραλλαγή του PDF/A

Το πρώτο που πρέπει να κάνετε σωστά είναι η εξής διαπίστωση: η ταυτοποίηση PDF/E-1 δεν παράγεται προσαρμόζοντας το μοτίβο του PDF/A ή του PDF/X. Χρησιμοποιεί ξεχωριστό XMP namespace, http://www.aim.org/pdfe/ns/id/, και η τιμή της έκδοσης πρέπει να εμφανίζεται σε δύο σημεία: ως εγγραφή document information και ως ιδιότητα XMP με πρόθεμα namespace. Έκδοση μόνο της ιδιότητας XMP, ή μόνο της εγγραφής information, παράγει αρχείο που κουβαλά την πρόθεση και αποτυγχάνει στην επικύρωση

Το output intent έχει εξίσου συγκεκριμένη μορφή. Το PDF/E-1 απαιτεί ενσωματωμένο ICC profile με τον αναγνωριστικό subtype ISO_PDFE1, και το profile πρέπει να έχει πλήθος components που ταιριάζει με την οικογένεια device χρωμάτων που το έγγραφο όντως χρησιμοποιεί. Αυτή η τελευταία ρήτρα είναι εκεί όπου οι υλοποιήσεις σφάλουν σιωπηλά, γιατί σημαίνει ότι το intent δεν διαλέγεται εκ των προτέρων και μετά αγνοείται

Γιατί το device χρώμα θέλει σάρωση όλου του εγγράφου;

Γιατί τα color spaces κρύβονται σε resource dictionaries που μια σάρωση επιπέδου σελίδας δεν φτάνει ποτέ. Το PDF/E-1 μεταχειρίζεται τα DeviceRGB και DeviceCMYK ως αμοιβαία αποκλειόμενες οικογένειες για ένα έγγραφο, οπότε η επικύρωση του profile σημαίνει ότι ξέρετε κάθε device color space που χρησιμοποιεί οτιδήποτε στο αρχείο. Ένα form XObject έχει δικά του resources. Ένα pattern το ίδιο, και μια εικόνα το ίδιο. Ένα tiling pattern μέσα σε form XObject μέσα σε σελίδα απέχει τρία επίπεδα βάθος, και ένας validator που ελέγχει μόνο τα resources πρώτου επιπέδου της σελίδας θα περάσει έγγραφο που χρησιμοποιεί και τις δύο οικογένειες

Η σάρωση επομένως καταχωρεί τα color spaces καθώς διαπερνά σελίδες, φόρμες, εικόνες και patterns ως ένα ενιαίο traversal, και μόνο τότε αποφασίζει αν το έγγραφο είναι συνεπές και αν το output intent ταιριάζει. Η ίδια λογική κινεί γενικά την αρχιτεκτονική του preflight: μερικό traversal παράγει ψευδή περάσματα, και ένα ψευδές πέρασμα σε έλεγχο conformance είναι χειρότερο από κανέναν έλεγχο, γιατί καταγράφεται ως απόδειξη

var
  Lib: TPDFlib;
  Diag: WideString;
begin
  Lib := TPDFlib.Create(nil);
  try
    Lib.LoadFromFile('assembly-drawings.pdf');

    if Lib.SetPDFEMode(1) = 0 then
      raise Exception.Create('PDF/E author mode was refused');

    // Το author mode κρατά συγχρονισμένα τα lifecycle metadata σε κάθε αποθήκευση.
    // Ρωτήστε πριν την αποθήκευση αν το έγγραφο θα περνούσε τη δική του πύλη
    if not Lib.PDFEReadyForSave then
    begin
      Diag := Lib.GetPDFEDiagnostics;
      Writeln('PDF/E blockers: ', Diag);
      Exit;
    end;

    Lib.SaveToFile('assembly-drawings-pdfe.pdf');
  finally
    Lib.Free;
  end;
end;

Τα lifecycle metadata είναι υποχρέωση ανά αποθήκευση

Το PDF/E-1 ζητά περισσότερα από ένα αναγνωριστικό εγγράφου. Το ελάχιστο σετ περιλαμβάνει το media management document identifier, ένα version identifier, μια rendition class, χρόνο δημιουργίας, χρόνο τροποποίησης, χρόνο metadata και τίτλο. Είναι ένα λεξιλόγιο παρακολούθησης αναθεωρήσεων, και υπάρχει επειδή από ένα παραδοτέο μηχανικής αναμένεται να επανεκδοθεί και όχι να γραφτεί μία φορά

Η συνέπεια για μια υλοποίηση είναι ότι αυτά τα πεδία δεν ορίζονται κατά τη δημιουργία του εγγράφου. Αν ο χρόνος τροποποίησης γραφτεί όταν ενεργοποιείτε το mode και το έγγραφο επεξεργάζεται κατόπιν, το XMP snapshot και η πραγματική κατάσταση του εγγράφου έχουν αποκλίνει, και ένας validator που τα συγκρίνει αναφέρει ασυνέπεια που δεν ήθελε κανείς. Το author mode επομένως συγχρονίζει τα πεδία αμέσως πριν από κάθε αποθήκευση, ώστε τα metadata να περιγράφουν τα bytes που πρόκειται να γραφτούν και όχι τα bytes που υπήρχαν όταν άναψε το mode

Αυτή είναι μια γενική αρχή για τα conformance metadata και αξίζει να διατυπωθεί χωριστά από το PDF/E: τα παράγωγα metadata ανήκουν στο μονοπάτι αποθήκευσης, όχι στο μονοπάτι επεξεργασίας. Κάθε πεδίο που υπολογίζεται από την κατάσταση του εγγράφου πρέπει να ξαναϋπολογίζεται τη στιγμή που η κατάσταση παγώνει, αλλιώς είναι cache χωρίς invalidation

Διάγραμμα PDF/E-1 του PDFlibPas για τη σάρωση device χρώματος όλου του εγγράφου που διαπερνά τα resource dictionaries σελίδας, form XObject, tiling pattern και εικόνας συλλέγοντας τις οικογένειες DeviceRGB και DeviceCMYK προτού κρίνει συνέπεια, δίπλα στα πεδία lifecycle metadata που ο author mode ξανασυγχρονίζει αμέσως πριν από κάθε αποθήκευση ώστε το XMP snapshot να ταιριάζει με τα bytes που πρόκειται να γραφτούν
Η συνέπεια χρώματος κρίνεται μόνο αφού ένα traversal φτάσει σε κάθε resource dictionary, και τα παράγωγα lifecycle metadata ξαναϋπολογίζονται τη στιγμή που η κατάσταση του εγγράφου παγώνει, όχι όταν ανάβει το mode

Ο κανόνας που κάνει την επικύρωση περιεχομένου αυστηρή

Το PDF/E-1 δεν επιτρέπει στους τελεστές των compatibility sections να απορροφούν άγνωστο περιεχόμενο. Στο κοινό PDF, τα BX και EX περικλείουν μια περιοχή όπου ο καταναλωτής πρέπει να αγνοεί τελεστές που δεν αναγνωρίζει, που είναι η καταφύγια που επιτρέπουν σε έναν παραγωγό να εκπέμψει νεότερες κατασκευές χωρίς να σπάσει older readers. Κάτω από PDF/E-1 αυτή η διέξοδος είναι κλειστή, οπότε κάθε τελεστής που το preflight δεν αναγνωρίζει αναφέρεται άνευ όρων, είτε κάθεται μέσα σε compatibility section είτε όχι

Η επίδραση σε έναν validator είναι σημαντική. Δεν μπορεί να παραλείπει περιοχές που δεν καταλαβαίνει, που σημαίνει ότι ο parser των operands πρέπει όντως να αναλύει κάθε τελεστή σε κάθε content stream. Εδώ μπαίνουν τα όρια. Το traversal περικόπτεται στα 128 επίπεδα εμφώλευσης, ένα εκατομμύριο αντικείμενα και 64 MiB περιεχομένου, και αυτά τα όρια δεν είναι tuning απόδοσης. Ένα εχθρικό ή απλώς χαλασμένο αρχείο μπορεί να παρουσιάσει object graph με κύκλους ή βάθος εμφώλευσης που γυρνά έναν αναδρομικό validator σε stack overflow, και τα όρια είναι όσα εμποδίζουν ένα πέρασμα επικύρωσης από το να γίνει διάνθρωση denial-of-service. Η ίδια αμυντική στάση περιγράφεται στο ασφαλές parsing μη έμπιστων PDF

// Αυτόνομη επικύρωση ενός αρχείου που δεν παράξατε εσείς, χωρίς
// να το φορτώσετε σε στιγμιότυπο εγγράφου
var
  Issues: TStringList;
  Stream: TFileStream;
  I: Integer;
begin
  Issues := TStringList.Create;
  Stream := TFileStream.Create('incoming.pdf', fmOpenRead or fmShareDenyWrite);
  try
    if CheckCompliancePDFE(Stream, '', 0, Issues) = 0 then
      for I := 0 to Issues.Count - 1 do
        Writeln('PDF/E: ', Issues[I]);
  finally
    Stream.Free;
    Issues.Free;
  end;
end;

Τι επιδιορθώνει η πύλη αποθήκευσης και τι αρνείται

Η πύλη χωρίζει τη δουλειά της σε δύο στάδια, και ο διαχωρισμός είναι από μόνο του μια χρήσιμη σχεδιαστική ιδέα. Πρώτα κανονικοποιεί όσα επισκευάζονται με ασφάλεια: τα print flags των annotations, τα no-zoom και no-rotate flags των text annotations, και το appearance-generation flag στο λεξικό της φόρμας. Αυτά είναι ρυθμίσεις με μία σωστή τιμή κάτω από το profile και χωρίς πληροφοριακό περιεχόμενο, οπότε η σιωπηλή διόρθωσή τους είναι σωστή και η άρνηση επ αυτών θα ήταν σχολαστικότητα

Μετά ελέγχει τους περιορισμούς που δεν επιδιορθώνονται χωρίς να αλλάξει αυτό που σημαίνει το έγγραφο: έκδοση, ταυτοποίηση, κρυπτογράφηση, output intent, συνέπεια device χρώματος και την παρουσία δυναμικού περιεχομένου φόρμας. Έγγραφο που αποτυγχάνει σε όποιον από αυτούς απορρίπτεται, γιατί η επινόηση output intent ή η εκλογή οικογένειας χρωμάτων για λογαριασμό του συγγραφέα θα παρήγαγε αρχείο που περνά την επικύρωση και παρερμηνεύει το περιεχόμενο

Διάγραμμα πύλης αποθήκευσης PDF/E-1 του PDFlibPas για Delphi που δείχνει το bounded preflight που σαρώνει κάθε τελεστή content stream κάτω από όρια 128 επιπέδων εμφώλευσης, ενός εκατομμυρίου αντικειμένων και 64 MiB, επιδιορθώνει σιωπηλά τα print, zoom και rotate flags των annotations, αρνείται λάθος έκδοση, ταυτοποίηση, κρυπτογράφηση, output intent, device χρώμα ή δυναμικό περιεχόμενο φόρμας, και αναφέρει blockers μέσω GetPDFEDiagnostics
Η πύλη επιδιορθώνει σιωπηλά μόνο όσα δεν κουβαλούν πληροφορία, αρνείται κάθε περιορισμό που μια επισκευή θα παραμόρφωνε, και μετατρέπει την άρνηση σε λίστα blockers μέσω GetPDFEDiagnostics προτού φτάσει οποιοδήποτε byte στον δίσκο

Η ανάγνωση των διαγνωστικών μέσω GetPDFEDiagnostics πριν την αποθήκευση μετατρέπει αυτή την άρνηση σε λίστα με ενέργειες αντί για αποτυχημένη πράξη. Σε ένα batch pipeline, καλέστε το σε κάθε έγγραφο, καταγράφετε τους blockers ανά αρχείο, και προωθήστε τις αποτυχίες σε ουρά που κοιτάζει άνθρωπος. Είναι πολύ πιο χρήσιμο από μια αποθήκευση που σηκώνει exception, γιατί οι blockers συνήθως ομαδοποιούνται: σαράντα έγγραφα που αποτυγχάνουν για το ίδιο λείπον output intent είναι μία διόρθωση, όχι σαράντα

Επιλογή ανάμεσα στα archival profiles

Το PDF/E-1 είναι ο σωστός στόχος όταν το παραδοτέο είναι τεχνική τεκμηρίωση με κύκλο ζωής αναθεωρήσεων, και ειδικά όταν η συνέπεια device χρώματος μετράει επειδή η έξοδος πάει σε plotters και μεγάλου μεγέθους εκτυπωτές. Το PDF/A είναι ο σωστός στόχος όταν ο σκοπός είναι η μακροχρόνια αναγνωσιμότητα εγγράφων γενικά, και είναι το profile με την πιο πλατιά υποστήριξη validators. Τα δύο δεν είναι εναλλάξιμα, και ένα έγγραφο μπορεί να ικανοποιεί το ένα και να αποτυγχάνει στο άλλο

Διάγραμμα αποφάσεων του PDFlibPas που συγκρίνει τα archival profiles PDF/E-1 και PDF/A για Delphi: PDF/E-1 για παραδοτέα μηχανικής με κύκλους ζωής αναθεωρήσεων, χρώμα plotter και συμβατική επικύρωση κάτω από το δικό του XMP namespace με output intent ISO_PDFE1, PDF/A για γενική μακροχρόνια αναγνωσιμότητα με την πιο πλατιά υποστήριξη validators
Ξεκινήστε από το ποιος επικυρώνει το αρχείο στην άκρη: τα profiles απαιτούν διαφορετικές εγγυήσεις ταυτοποίησης, metadata και χρώματος, και ένα έγγραφο μπορεί να ικανοποιεί το ένα αποτυγχάνοντας στο άλλο

Αν διαλέγετε, ξεκινήστε από το ποιος επικυρώνει το αρχείο στην άκρη. Εργαλεία επικύρωσης PDF/A υπάρχουν παντού, και το αντίστοιχο preflight στο PDFlibPas περιγράφεται στο PDF/A και PDF/UA preflight. Η επικύρωση PDF/E είναι πιο εξειδικευμένη και συνήθως είναι συμβατική απαίτηση και όχι προεπιλογή. Όταν ένα υπάρχον αρχείο πρέπει να ανέβει σε profile για το οποίο δεν γράφτηκε ποτέ, το μονοπάτι επιδιόρθωσης metadata στη μετατροπή σε PDF/A με επιδιόρθωση metadata είναι το μοτίβο που ακολουθείτε, και το ίδιο σχήμα ισχύει εδώ: ταυτοποίηση, επιδιόρθωση όσων είναι ασφαλή, άρνηση των υπολοίπων με λίστα

Το author mode, το bounded preflight περιεχομένου και ο αυτόνομος έλεγχος compliance έρχονται όλα με το PDFlibPas Delphi PDF library, οπότε ένα έγγραφο μπορεί να παραχθεί κάτω από το profile και να επαληθευτεί ανεξάρτητα μετά μέσω ξεχωριστού μονοπατιού κώδικα, που είναι η μόνη διάταξη που αξίζει εμπιστοσύνη για έναν ισχυρισμό conformance