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

Έξοδος PDF 2.0, PDF/A-4 και PDF/UA-2 με HotPDF σε Delphi

Το HotPDF γράφει native έγγραφα PDF 2.0 από Delphi και C++Builder, συμπεριλαμβανομένων των τριών αρχειακών προφίλ PDF/A-4 και προσβάσιμης εξόδου PDF/UA-2 με στοιχεία δομής σε χώρο ονομάτων. Η επιλογή τους είναι θέμα δύο ιδιοτήτων, αλλά τα πρότυπα πίσω από αυτές τις ιδιότητες άλλαξαν περισσότερο από όσο υποδηλώνει ο αριθμός έκδοσης: το PDF/A-4 παράτησε τα γράμματα συμμόρφωσης που έμαθε ο καθένας με το PDF/A-2, και το PDF/UA-2 εισήγαγε χώρους ονομάτων δομής που ένα έγγραφο μέρους 1 δεν είχε ποτέ

Αυτό το άρθρο καλύπτει τι αλλάζει πραγματικά στο παραγόμενο αρχείο, και ποια λάθη το HotPDF μετατρέπει σε εξαίρεση στο EndDoc παρά σε έγγραφο που αποτυγχάνει σε επικύρωση στον πελάτη

Πώς διαφέρει η ταυτοποίηση PDF/A-4 από τα μέρη 2 και 3

Το PDF/A-4 ταυτίζει τον εαυτό του με αριθμό μέρους και έτος αναθεώρησης, χωρίς γράμμα συμμόρφωσης για το βασικό μέρος. Ορίστε την PDFACompliance σε '4' και το HotPDF εκπέμπει pdfaid:part=4 με pdfaid:rev=2020 και καμία καταχώριση pdfaid:conformance. Το γράμμα δεν χάθηκε — το μέρος 4 δεν έχει επίπεδα A/B/U, επειδή οι απαιτήσεις που παλιά τα χώριζαν αναδιπλώθηκαν μέσα στο βασικό μέρος

Δύο επεκτάσεις κρατούν γράμμα. Το '4E' επιλέγει PDF/A-4e για μηχανικά έγγραφα και εκπέμπει συμμόρφωση E, που επιτρέπει τις διαδρομές σχολιασμού 3D και RichMedia που απαγορεύουν τα άλλα προφίλ. Το '4F' επιλέγει PDF/A-4f και εκπέμπει συμμόρφωση F, που επιτρέπει ένα ενσωματωμένο αρχείο οποιασδήποτε μορφής. Και τα τρία επιβάλλουν κεφαλίδα PDF 2.0, απαιτούν τους συνήθεις ελέγχους output intent και μεταδεδομένων του PDF/A, και απαγορεύουν την κρυπτογράφηση — ένα κρυπτογραφημένο αρχειακό αρχείο είναι μια αντίφαση που το πρότυπο δεν επιτρέπει

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-archive.pdf';
    Pdf.PDFACompliance := '4F';   // PDF/A-4f: associated files of any format
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 760, 0, 'Invoice 2026-0731');
    Pdf.AddPDFAssociatedFile('invoice.xml', 'text/xml',
      'Structured invoice data', 'Data', LoadInvoiceBytes);
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Η AddPDFAssociatedFile ενσωματώνει το αρχείο, κατασκευάζει το FileSpec του με μια /AFRelationship, και το καταχωρεί και στον πίνακα /AF του Καταλόγου και στο δέντρο ονομάτων EmbeddedFiles. Και οι δύο καταχωρήσεις απαιτούνται· ένα αρχείο που αναγράφεται μόνο στη μία είναι ο πιο συνηθισμένος λόγος που ένα υβριδικό τιμολόγιο περνά έναν γρήγορο οπτικό έλεγχο και αποτυγχάνει σε έναν πραγματικό validator. Η συμβολοσειρά σχέσης δέχεται Source, Data, Alternative, Supplement ή Unspecified, και το ενεργό προφίλ πρέπει να είναι PDF/A-3, PDF/A-4e ή PDF/A-4f — το βασικό προφίλ μέρους 4 δεν δέχεται συνοδευτικά αρχεία. Το παλιό όνομα AddPDFA3AssociatedFile εξακολουθεί να λειτουργεί για υπάρχοντα κώδικα

Τι ζητά το PDF/UA-2 που δεν ζητούσε το PDF/UA-1

Το PDF/UA-2 επιβάλλει PDF 2.0 και εκπέμπει pdfuaid:part=2 με pdfuaid:rev=2024, και εισάγει χώρους ονομάτων στο δέντρο δομής. Ένα έγγραφο μέρους 1 είχε ένα επίπεδο λεξιλόγιο τυπικών ρόλων. Ένα έγγραφο μέρους 2 μπορεί να φέρει προσαρμοσμένους ρόλους όσο ο καθένας ανήκει σε έναν δηλωμένο χώρο ονομάτων, που είναι αυτό που κάνει την ειδική για τον τομέα επισήμανση ευανάγνωστη στις βοηθητικές τεχνολογίες αντί για εικασία

Δύο μέθοδοι υλοποιούν αυτό. Η RegisterStructureNamespace δημιουργεί ή επαναχρησιμοποιεί ένα έμμεσο λεξικό /Type /Namespace και το καταχωρεί στο StructTreeRoot /Namespaces, επιστρέφοντας το λεξικό ώστε να μπορείτε να το επαναχρησιμοποιήσετε. Η AddStructureElementNS δημιουργεί ένα στοιχείο δομής του οποίου η καταχώριση /NS δείχνει σε αυτό το λεξικό, που είναι αυτό που αδειοδοτεί ένα όνομα ρόλου εκτός του τυπικού συνόλου. Επαναλαμβανόμενες κλήσεις με το ίδιο URI επαναχρησιμοποιούν ένα λεξικό αντί να στοιβάζουν αντίγραφα

var
  Root: THPDFDictionaryObject;
begin
  Pdf.PDFUACompliance := True;
  Pdf.PDFUAPart := 2;             // part 2 forces PDF 2.0
  Pdf.Lang := 'en-US';
  Pdf.BeginDoc;
  Root := Pdf.AddStructureElement('Document', nil);
  Pdf.AddStructureElementNS('WidgetGroup',
    'https://example.com/ns/widgets', Root);
  Pdf.EndDoc;
end;

Η Lang δεν είναι εδώ διακόσμηση. Ένα tagged έγγραφο χωρίς δηλωμένη φυσική γλώσσα αφήνει έναν αναγνώστη οθόνης να μαντεύει την προφορά, και το PDF/UA αντιμετωπίζει την παράλειψη ως ελάττωμα παρά ως προτίμηση

Ποια σφάλματα δομής πιάνει το EndDoc;

Τέσσερα, και το καθένα αντιστοιχεί σε ένα έγγραφο που αλλιώς θα έφτανε σπασμένο σε έναν validator. Η ρίζα δομής πρέπει να περιέχει ακριβώς ένα στοιχείο Document ανωτέρου επιπέδου. Κάθε λεξικό χώρου ονομάτων πρέπει να είναι έμμεσο, με τύπο Namespace, και να φέρει ένα μοναδικό μη κενό URI. Κάθε αναφορά /NS σε στοιχείο δομής πρέπει να επιλύεται σε ένα λεξικό που όντως αναγράφεται στον πίνακα /Namespaces της ρίζας. Και ένας ρόλος χωρίς χώρο ονομάτων πρέπει να είναι τυπικός ρόλος PDF 2.0 ή να επιλύεται μέσω του RoleMap

Αυτά εκπυρσοκροτούν στο EndDoc επειδή αυτή είναι η τελευταία στιγμή που ολόκληρο το δέντρο υπάρχει στη μνήμη και η πρώτη στιγμή που είναι πλήρες. Το να τα πιάσει νωρίτερα θα σήμαινε απόρριψη έγκυρων ενδιάμεσων καταστάσεων· το να τα πιάσει αργότερα θα σήμαινε να μην τα πιάσει καθόλου. Η πρακτική συνέπεια για τον κώδικά σας είναι ότι ένα σφάλμα δομής αναδύεται στο τέλος της παραγωγής με ένα μήνυμα που ονομάζει το πρόβλημα, αντί να αναδυθεί εβδομάδες αργότερα ως μια αναφορά veraPDF που προωθεί κάποιος από πελάτη

Οι ρόλοι PDF 2.0 που αξίζει να γνωρίζετε

Η απαρίθμηση τυποποιημένων ρόλων κερδίζει DocumentFragment, Aside, Title, FENote, Sub, Em, Strong και Artifact. Τρεις από αυτούς αλλάζουν πώς επισημαίνετε κοινά επιχειρησιακά έγγραφα. Το Aside επιτέλους δίνει στις πλευρικές μπάρες και τα pull quotes μια θέση που δεν είναι ένα κακοποιημένο Sect. Το FENote μαρκάρει υποσημειώσεις και ενδημικές σημειώσεις ως αυτό που είναι, ώστε ένας αναγνώστης να μπορεί να τις προσφέρει παρά να τις ανακατεύει με το κείμενο σώματος. Τα Em και Strong αντικαθιστούν τη σημασιολογική εικασία που προερχόταν από την επισήμανση έμφασης ως μορφοποίηση επιπέδου span

Η υπερφόρτωση συμβολοσειράς επιπλέον δέχεται την ανοιχτή μορφή Hn, συμπεριλαμβανομένου του H7 και πέρα. Το PDF 1.7 σταματούσε στο H6, που ανάγκαζε τα βαθιά τεχνικά έγγραφα να ισοπεδώσουν το περίγραφό τους ή να επαναχρησιμοποιήσουν επίπεδα. Αν παράγετε έγγραφα προτύπων, νομικούς κώδικες ή κατάλογους ανταλλακτικών, αυτό μόνο του μπορεί να είναι ο λόγος να μετακινήσετε την έξοδο σε PDF 2.0

Τι να ελέγξετε πριν αλλάξετε την παραγωγική έξοδο

Το PDF 2.0 είναι μια αλλαγή κεφαλίδας με μακρά ουρά. Παλαιά εργαλεία εισαγωγής αρχείων, κάποια RIP εκτύπωσης και ένα εκπληκτικό πλήθος αναγνωστών line-of-business δέχονται μόνο μέχρι PDF 1.7, και αποτυγχάνουν στην κεφαλίδα παρά σε ό,τι κάνατε λάθος. Πριν αλλάξετε, επιβεβαιώστε τα συστήματα κατανάλωσης, και θυμηθείτε ότι η επιλογή προφίλ PDF/A-4 επιλέγει PDF 2.0 είτε το ζητήσατε είτε όχι

Μια ασφαλής σειρά είναι να κρατήσετε PDF/A-3 για έγγραφα που πάνε προς τα έξω σε άγνωστους αναγνώστες, να χρησιμοποιείτε PDF/A-4f για εσωτερικά αρχεία όπου ελέγχετε την εισαγωγή, και να υιοθετείτε PDF/UA-2 μόνο όπου η πολιτική προσβασιμότητας το ονομάζει. Αν δουλεύετε πρώτα την αρχειακή πλευρά, οι οδηγοί για την επικύρωση PDF/A, PDF/X και PDF/UA και για τα υβριδικά τιμολόγια ZUGFeRD και Factur-X σε PDF/A-3 καλύπτουν τις επιλογές προφίλ που έχουν σημασία πριν τον αριθμό έκδοσης, και οι σημειώσεις για την αυτοματοποιημένη αναφορά preflight δείχνουν πώς να κάνετε την ετυμηγορία μέρος του build σας παρά ένα χειροκίνητο βήμα

Το HotPDF διανέμει ολόκληρη την επιφάνεια συγγραφής PDF 2.0 ως native VCL κώδικα για Delphi και C++Builder, οπότε η έξοδος PDF/A-4 και PDF/UA-2 δεν χρειάζεται εξωτερική μηχανή ή redistributable — η σελίδα του HotPDF συστατικού παραθέτει τα υποστηριζόμενα προφίλ και τις εκδόσεις RAD Studio