Technischer Artikel

PDF/A- und PDF/UA-Preflight in Delphi

PDF/A und PDF/UA beantworten zwei Fragen, die nichts miteinander zu tun haben, und sie als ein einziges Kontrollkästchen für Barrierefreiheit und Archivierung zu behandeln, ist der Weg, auf dem defekte Dateien mit einem Konformitätsetikett in ein Archiv gelangen. PDF/A fragt, ob eine Datei in zwanzig Jahren noch originalgetreu gerendert wird. PDF/UA fragt, ob assistive Technologie sie heute lesen kann. Ein Dokument kann das eine mühelos bestehen und am anderen scheitern, daher kommt das einzig ehrliche Urteil davon, beide auszuführen, und zwar bevor die Datei herausgeschrieben wird, nicht nachdem ein nachgelagertes System der in die Metadaten eingebrannten Konformitätskennung vertraut. Diese Kennung ist eine Selbstauskunft. Nichts im Format verlangt, dass sie wahr ist, und eine Anwendung, die „PDF/A-1b“ ins XMP schreibt, ohne gegen den Standard zu validieren, erzeugt eine Datei, die jedem Konsumenten konform erscheint, der nur das Etikett liest. losLab PDF Library (PDF Library for Delphi) schließt diese Lücke für Delphi und C++Builder, indem beide Validatoren in die Bibliothek eingebaut sind, sodass die Prüfung im Prozess läuft und kein externer Dienst aufgesetzt werden muss

Zwei Standards, die Dateien aus entgegengesetzten Gründen durchfallen lassen

ISO 19005 (PDF/A) ist ein Reproduktionsvertrag. Eine konforme Datei muss in Jahrzehnten auf Software identisch gerendert werden, die das erzeugende System nie gesehen hat, daher zielen die Regeln auf externe Abhängigkeiten: jede Schrift eingebettet, Farbe an einen eingebetteten ICC-OutputIntent gebunden oder in einem geräteunabhängigen Farbraum ausgedrückt, keine Verschlüsselung in PDF/A-1, kein JavaScript, XMP-Metadaten, die mit dem Document-Information-Dictionary übereinstimmen. ISO 14289 (PDF/UA) ist dagegen ein Semantikvertrag. Assistive Technologie muss das Dokument durchlaufen und mit Bedeutung herauskommen, und die lebt in einer völlig anderen Schicht: ein vollständiger Strukturbaum, Alternativtext an Abbildungen, ein zur Anzeige gesetzter Dokumenttitel, Überschriftenebenen ohne Sprünge, Tabellenkopf-Beziehungen, die erhalten bleiben, sobald die Seite nicht mehr auf dem Bildschirm ist

Weil die beiden Standards unterschiedliche Schichten überwachen, sind die Dateien, die Ihnen Probleme bereiten, jene, die dazwischen liegen. Ein archivperfektes Dokument kann für einen Screenreader stumm sein. Ein wunderbar getaggtes kann auf eine Desktop-Schrift verweisen, die es in zehn Jahren nicht mehr gibt. Im öffentlichen Sektor treffen üblicherweise beide Anforderungen zugleich ein, und eine Pipeline dort kann sie nicht zu einem einzigen Gate zusammenfassen. Die Befunde gehen an verschiedene Personen. Nicht eingebettete Schriften sind ein Defekt im Code, der die PDF erzeugt, während fehlender Alternativtext demjenigen gehört, der die Inhaltsvorlagen verantwortet, und ein Bericht, der beides mischt, wird einfach zweimal weitergeleitet

Welchen Teil von PDF/A Sie anpeilen, ist ebenso wichtig wie die Frage, ob Sie ihn treffen. PDF/A-1 ist auf PDF 1.4 eingefroren und lehnt Transparenz und JPEG2000 ab, nach denen moderne Reporting-Ausgabe ganz selbstverständlich greift. PDF/A-2 (ISO 19005-2, aufbauend auf ISO 32000-1) akzeptiert beides und ist die vernünftige Vorgabe für ein neues Archiv. PDF/A-3 geht weiter und erlaubt eingebettete Dateien beliebigen Typs, worauf regulierte E-Rechnungsformate angewiesen sind. Ein Team, das 2026 noch auf PDF/A-1b standardisiert, trägt meist eine Anforderung mit sich, die jemand vor fünfzehn Jahren geschrieben hat, und den Zielteil neu zu verhandeln ist oft günstiger, als aus jedem Diagramm, das das System ausgibt, die Transparenz zu entfernen

Diagramm zu PDF Library for Delphi, das den PDF/A-Reproduktionsvertrag mit dem PDF/UA-Semantikvertrag vergleicht, samt einer Bestanden/Durchgefallen-Matrix von Dokumenten, die das eine erfüllen und am anderen scheitern
PDF/A sichert originalgetreues Rendern über Jahrzehnte, während PDF/UA assistives Lesen heute sichert, und keines der Urteile impliziert das andere

Strukturierte Befunde zum Zeitpunkt der Aufnahme

Der Einstiegspunkt der flachen API ist CheckFileCompliance, mit dem Testselektor 1 für PDF/A und 2 für PDF/UA. Er liefert ein String-Listen-Handle zurück, dessen Einträge einzelne Befunde sind, einer pro Zeile, und genau diese Form will ein automatisiertes Gate durchlaufen:

Diagramm zu PDF Library for Delphi für skalierte Preflight-Aufnahme mit einer streamenden Bibliotheksinstanz pro Worker, Öffnungen erst nach abgeschlossenen Uploads und freigegebenen Befundlisten
Gates öffnen erst nach abgeschlossener Übertragung, jeder Worker streamt die Eingabe schreibgeschützt durch eine private Bibliotheksinstanz, und jedes zurückgegebene Handle schuldet eine Freigabe
function GateArchiveUpload(Pdf: TPDFlib; const FileName: string): Boolean;
var
  ListId, I: Integer;
begin
  ListId := Pdf.CheckFileCompliance(FileName, '', 1, 0);  // 1 = PDF/A
  if ListId = 0 then
  begin
    // 0 bedeutet "keine Befunde" ODER "Datei nicht lesbar" -- vor dem Durchlassen unterscheiden
    Result := Pdf.LastErrorCode = 0;
    Exit;
  end;
  for I := 0 to Pdf.GetStringListCount(ListId) - 1 do
    LogFinding(FileName, Pdf.GetStringListItem(ListId, I));
  Pdf.ReleaseStringList(ListId);
  Result := False;
end;

Zwei Details entscheiden, ob das unbeaufsichtigt läuft. Das erste ist ein Rückgabewert, der zwei gegensätzliche Dinge bedeutet. CheckFileCompliance gibt 0 zurück, wenn die Datei vollständig konform ist, und ebenso, wenn die Datei gar nicht geöffnet werden konnte, weil intern eine leere Ergebnisliste in beiden Fällen zu 0 zusammenfällt. Ein Gate, das 0 als Bestanden liest, winkt beschädigte Uploads direkt ins Archiv durch; unterscheiden Sie daher mit LastErrorCode, bevor Sie der Null vertrauen, wie es das Gate oben tut. Das zweite betrifft die Stelle, an der sich die Datei in ihrem Lebenszyklus befindet. Der Prüfer läuft auf dem streamenden Reader der Bibliothek statt auf dem vollständigen Dokumentmodell, öffnet die Datei direkt mit Lesefreigabe und ruft nie LoadFromFile auf, weshalb er Eingaben von mehreren Gigabyte durcharbeiten kann, ohne einen Objektbaum aufzubauen. Dasselbe streamende Öffnen scheitert, solange ein anderer Prozess die Datei noch zum Schreiben hält, und ein laufender Upload ist genau dieser Zustand. Prüfen Sie erst, nachdem die Übertragung abgeschlossen ist

Das Streaming-Design zahlt sich unter Last erneut aus. Jede Prüfung öffnet ihre Eingabe schreibgeschützt und gibt sie zum Lesen frei, sodass ein Korpus-Audit über Worker-Threads oder -Prozesse skaliert, mit einer TPDFlib-Instanz pro Worker und ohne Konkurrenz zwischen ihnen. Die Ressource, die Disziplin verlangt, ist das Handle selbst. Jedes Ergebnis ungleich null von CheckFileCompliance bleibt belegt, bis Sie ReleaseStringList aufrufen, und ein lang laufendes Gate, das die Freigabe vergisst, stürzt nicht ab, es verliert nur langsam Speicher, bis jemand nach dem Grund sucht

Reports für Menschen, Diffs für Build-Gates

Eine Befundliste ist die richtige Form für ein Gate und die falsche für eine E-Mail an das Vorlagenteam. CreatePreflightReport gibt dieselbe Analyse als lesbaren Text aus, CreatePreflightReportEx fügt einen Selektor für das Report-Format hinzu, und SavePreflightReport schreibt ihn auf die Platte, damit der Report im ausgelieferten Dokumentpaket mitreisen kann. Viele Archivierungsverträge machen diesen Report zu einem eigenständigen Liefergegenstand, nicht nur zu einem internen Artefakt

Das Mitglied dieser Familie, das sich seinen Platz still verdient, ist ComparePreflightReports. Konformität ist eine Regressionsfläche wie jedes andere Verhalten. Eine Vorlagenänderung, eine neu lizenzierte Unternehmensschrift oder ein Bibliotheks-Upgrade können jeweils einen Befund einführen, der beim letzten Release nicht da war, und keines davon kündigt sich an. Halten Sie Referenz-Reports für einen Satz repräsentativer Dokumente unter Versionskontrolle, erzeugen Sie sie nach jeder Änderung neu und führen Sie ComparePreflightReports aus, um das Delta zu berechnen. Ein leerer Diff ist ein Release-Artefakt, das sich aufzuheben lohnt. Ein überraschender Befund lässt den Build scheitern, und das ist ein weit günstigerer Ort für die Entdeckung als das Audit

Ausgabe erzeugen, die beim ersten Durchlauf besteht

Preflight lohnt sich bei Dateien, die von anderswo eintreffen. Bei Dokumenten, die Ihr eigener Code erzeugt, ist es der langsame Umweg, Verstöße nach der Erzeugung zu finden und nachträglich zu beheben. PDF Library for Delphi bringt für jeden Standard einen Modus auf der Erzeugungsseite mit, und Sie können beide für dasselbe Dokument einschalten:

var
  Pdf: TPDFlib;
  Diag: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.NewDocument;
    Pdf.SetPDFAMode(1);
    Pdf.LoadOutputIntentProfile('sRGB-IEC61966-2.1.icc', 'RGB');
    Pdf.SetPDFUAMode('en-US');
    Pdf.SetInformation(1, 'Quarterly Statement');  // /Title: für PDF/UA erforderlich
    // ... hier getaggten Inhalt zeichnen ...
    Diag := Pdf.GetPDFUADiagnostics;
    if Diag <> '' then
      Writeln('fix before shipping: ', Diag);
    Pdf.SaveToFile('statement.pdf');
    // der Preflight, der zählt, läuft auf der gespeicherten Datei:
    Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
  finally
    Pdf.Free;
  end;
end;

Die Falle versteckt sich beim Speichern. Mehrere der Konformitätsreparaturen geschehen während der Serialisierung des Dokuments und nicht beim Einschalten des Modus: das Erzwingen des Print-Flags an Anmerkungen, das Schreiben des Standard-AFRelationship für eingebettete PDF/A-3-Dateien, das Normalisieren von Tab-Reihenfolge und Formularfeld-Beschreibungen für PDF/UA. Das Dokument im Speicher ist nicht byteidentisch mit dem, das auf der Platte landet, daher ist das einzige Preflight-Urteil, das etwas bedeutet, das aus der gespeicherten Datei berechnete. Validieren Sie statement.pdf selbst. Leiten Sie Konformität nicht aus dem noch im Speicher liegenden Objekt ab, denn die Bytes, die Sie beurteilen würden, sind nicht die Bytes, die Sie ausgeliefert haben

Diagramm zu PDF Library for Delphi über Konformitätskorrekturen, die beim Speichern während der Serialisierung angewendet werden, weshalb der Preflight auf die gespeicherte PDF-Datei und nicht auf das Modell im Speicher gehört
Die Serialisierung erzwingt Print-Flags an Anmerkungen, setzt AFRelationship auf den Standard und normalisiert die Tab-Reihenfolge, sodass eine Speicherinspektion Bytes beurteilt, die niemand je ausliefert

Rechnungsszenarien, die maschinenlesbares XML neben dem visuellen Dokument mitführen, folgen dem ZUGFeRD- und Factur-X-Muster, das auf PDF/A-3 aufbaut. Diese sollten die Anhangsbeziehung explizit mit SetPDFA3DefaultAFRelationship setzen, da ISO 19005-3 verlangt, dass jede eingebettete Datei ihre Rolle gegenüber dem Dokument deklariert. Bleibt sie ungesetzt, ist das eingebettete XML nur ein Blob ohne erklärten Zweck, was der Validator bemerkt

Unabhängige Schiedsrichter: veraPDF und Acrobat

Ein Erzeuger sollte nicht der einzige Richter über seine eigene Ausgabe sein. Die Prüfer von PDF Library for Delphi liefern Ihnen schnelle, strukturierte Urteile im Prozess, was Sie auf dem heißen Pfad wollen, doch das Release-Gate für einen Archivierungsstapel sollte die Ausgabe trotzdem durch einen Validator schicken, den niemand in Ihrem Team geschrieben hat. veraPDF ist die von der Community gepflegte Referenzimplementierung für PDF/A und das Werkzeug, das die meisten Archive in ihren Abnahmekriterien nennen, also dasjenige, dem man entsprechen muss. Die Preflight-Profile von Acrobat sind ein nützlicher Schiedsrichter, wenn veraPDF und die prozessinterne Prüfung uneins sind. Halten Sie den Namen des Validators und seine Version neben jedem gespeicherten Report fest. Die Behauptung, eine Datei habe veraPDF bestanden, sagt ohne die Build-Nummer, die sie bestanden hat, sehr wenig aus, da das Werkzeug seine Regeln zwischen den Releases verschärft

Validatoren sind an den Rändern der Standards tatsächlich uneins, und wenn das passiert, lautet die Antwort nicht, das Werkzeug zu wählen, das Ihnen gefällt. Verkleinern Sie die Datei auf ein minimales Beispiel, das die Uneinigkeit noch auslöst, und lesen Sie es gegen den Standardtext. Eine Stunde davon bringt meist eines von zwei Dingen ans Licht: einen echten Werkzeugfehler, den es sich upstream zu melden lohnt, oder eine Klausel, die Ihr Team falsch gelesen hat und in den Compliance-Notizen festhalten sollte, damit die nächste Person sie nicht erneut verhandelt

Verschlüsselte Eingaben bekommen eine Abkürzung. Beide Prüfer nehmen ein Passwortargument entgegen, doch eine PDF/A-1-Datei mit Encryption-Dictionary ist bereits nicht konform, weil ISO 19005-1 Verschlüsselung rundheraus verbietet, sodass eine verschlüsselte Einreichung abgewiesen werden kann, bevor eine tiefere Analyse läuft. Herauszufinden, was ein Encryption-Dictionary tatsächlich gewährt, ist eine eigene Aufgabe, behandelt in PDF-Verschlüsselung und Berechtigungen prüfen

PDF/UA-Befunde gehen fast immer darauf zurück, wie der Strukturbaum ursprünglich erstellt wurde, und die Tagging-Techniken dahinter finden Sie in Tagged-PDF-Strukturbäume in Delphi aufbauen. Archive, die auch digitale Signaturen verlangen, sollten dieses Gate mit dem Workflow aus PAdES-Signierung und -Validierung kombinieren. Die vollständige Preflight-API-Referenz finden Sie auf der Produktseite von losLab PDF Library for Delphi