ConvertToPDFA macht aus einem gewöhnlichen Dokument mit einem einzigen Aufruf ein archivfähiges: Es entfernt, was der gewählte Part verbietet, fügt hinzu, was der Part verlangt, deklariert den Part, den das Dokument beansprucht, und prüft anschließend das Ergebnis. Die Deklaration wird nur dann als erfüllt gemeldet, wenn die Prüfung durchläuft, und GetPDFAConversionReport listet auf, was getan wurde und was noch im Wege steht
Genau diese letzte Eigenschaft ist die Entwurfsentscheidung, der Beachtung verdient. Ein Konverter, der die Deklaration ohne Prüfung stempelt, ist schlimmer als gar kein Konverter, denn eine Datei, die archivfähig sein will und es nicht ist, wandert gerade durch jene Systeme hindurch, die sie sonst abgefangen hätten. Der Fehl schlägt Jahre später zu, in einer Prüfung, an einem Dokument, das niemand mehr neu erzeugen kann
Warum scheitert ein gültig aussehendes PDF an einer PDF/A-Prüfung?
Zumeist deshalb, weil die beiden Stellen, an denen ein PDF sagt, wer es geschrieben hat, voneinander abweichen. Ein Validator liest sowohl das Dokument-Informations-Dictionary als auch das XMP-Paket und lehnt eine Datei ab, in der sich beide unterscheiden — und die meisten Dateien, die an diesem Punkt scheitern, hatten schlicht nie eine XMP-Hälfte geschrieben bekommen
RepairDocumentMetadata bringt beide überein und gibt zurück, wie viele Einträge repariert wurden. Wo nur eine Hälfte einen Wert trägt, wird die andere aus ihr gefüllt, sodass bereits Aufgezeichnetes nicht verworfen wird. Niemand muss entscheiden, welche Kopie maßgeblich ist, denn in der Praxis ist eine Kopie leer
Eine zweite Reparatur im selben Aufruf fängt einen subtileren Fall ab. Ein Dokument, das in einen PDF/A-Modus gesetzt wurde, erhält seine Standards-Identifikation zurück, falls sie verloren war — was immer dann passiert, wenn ein Aufrufer ein eigenes XMP-Paket liefert. Ohne diese Identifikation liest ein Validator die Datei als gewöhnliches PDF und meldet jede Regel des beanspruchten Parts als unerfüllt — ein spektakulär aussehender Fehlschlag mit einer einzigen kleinen Ursache
var
Lib: TPDFlib;
Repaired: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('incoming.pdf', '');
Repaired := Lib.RepairDocumentMetadata;
Log(Format('%d metadata entries brought into agreement', [Repaired]));
Lib.SaveToFile('incoming-fixed.pdf');
finally
Lib.Free;
end;
end;
Den Part wählen, bevor konvertiert wird
SetPDFAMode und ConvertToPDFA verwenden dieselbe Modus-Nummerierung, und drei der Werte sind neueren Datums. Modus 9 ist PDF/A-4, der auf PDF 2.0 aufbauende Part. Modus 10 ist PDF/A-4e, der zusätzlich 3D und Rich Media erlaubt, und Modus 11 ist PDF/A-4f, der eine eingebettete Datei beliebigen Formats erlaubt
Part 4 identifiziert sich anders als die Teile vor ihm: über Part-Nummer und das Jahr, in dem sein Part veröffentlicht wurde, ohne Konformitätsbuchstaben bei schlichtem PDF/A-4 und mit dem Buchstaben E oder F bei den beiden Erweiterungen. Die Prüfung erkennt Part 4, beurteilt dessen Dateien anhand von PDF 2.0 statt 1.7 und meldet eine Part-4-Datei, die ihr Revisionsjahr nicht angibt
Jede eingebettete Datei in einem Part-4-Dokument gibt an, wie sie zum Dokument gehört, wie sowohl Part 3 als auch Part 4 verlangen. Das ist die Regel, die früher gewöhnliche Anhänge aufs Korn nahm: Die Beziehung wurde nur für Anhänge nach dem ersten geschrieben und nie für den letzten, sodass ein Dokument mit einem einzigen Anhang — der häufigste Fall — gar keine Beziehung enthielt und genau deshalb an der Validierung scheiterte
var
Verdict: Integer;
begin
Lib.LoadFromFile('report.pdf', '');
Verdict := Lib.ConvertToPDFA(9); // 9 = PDF/A-4, 10 = 4e, 11 = 4f
Memo1.Lines.Text := Lib.GetPDFAConversionReport;
if Verdict = 1 then
Lib.SaveToFile('report-pdfa4.pdf')
else
Log('conversion incomplete - see the report for what stands in the way');
end;
Wozu der Konvertierungs-Bericht dient
Dazu, über das weitere Vorgehen zu entscheiden. Eine Konvertierung, die gelingt, braucht keinen Bericht; eine, die misslingt, ist der ganze Grund, warum es den Bericht gibt. Manche Hindernisse sind durch einen Konverter behebbar, andere nicht — Verschlüsselung, verbotener Inhalt mit Bedeutung, ein Schriftenprogramm, das schlicht nirgends auf der Maschine vorhanden ist. Der Bericht unterscheidet das Getane vom Verbliebenen und macht aus „Konvertierung fehlgeschlagen" eine Aufgabe
Man sollte das Urteil als Gate in einer Batch-Pipeline betrachten. Konvertieren, das Urteil lesen, die Datei routen: die durchgelassenen archivieren, den Rest mit dem angehängten Bericht für einen Menschen einreihen. Was man nicht tun sollte, ist die Ausgabe einer gescheiterten Konvertierung deshalb ins Archiv zu legen, weil sie besser aussieht als die Eingabe — sie trägt nun eine Deklaration, die die Prüfung abzulehnen sich geweigert hat
Die Marke lesen, die eine Datei bereits trägt
Bevor irgendetwas konvertiert wird, sollte man wissen, was das Dokument über sich selbst sagt. Eine PDF/A-Prüfung, die die bestehende Standards-Marke nicht lesen kann, beurteilt jede Datei anhand von Part 1, was immer sie deklariert, was bedeutet, dass ein vollkommen gültiges PDF/A-2- oder PDF/A-3-Dokument als ohne Marke und als versionstechnisch zu hoch gemeldet wird — das Gegenteil der Wahrheit
Die Marke wird gelesen, gleich ob der Produzent sie als XMP-Element oder als Attribut geschrieben hat. Beide Formen sind gewöhnliches XMP, und nur eines davon zu akzeptieren lässt Dateien anderer Produzenten unmarkiert wirken. Wer sich jemals gefragt hat, warum ein Dokument, das anderswo validiert, in der eigenen Pipeline scheitert, hat hier einen guten ersten Anlaufpunkt
Vor dem Archivieren bereinigen, und der Fehler, den man kennen sollte
Archiv-Konvertierung und Bereinigung laufen oft gemeinsam, weil der Inhalt, den eine Sicherheits-Policy entfernt haben will, sich stark mit dem Inhalt überschneidet, den PDF/A verbietet. SanitizeDocument entfernt JavaScript, und das Entfernen des letzten Skripts beseitigt auch den leeren Namensbaum, den es hinterlässt — ein Baum, der sonst einem Reader weiterhin mitteilen würde, dass das Dokument Skripte trägt
Die zweite Hälfte wurde auf die harte Tour gelernt: Ein Off-by-one in der Paketliste führte dazu, dass die Bereinigung das Entfernen von Skripten meldete, ohne welche zu entfernen, sodass ein bereinigtes Dokument seine Skripte beim Öffnen dennoch ausführte. Das ist ein gutes Argument für jenen allgemeinen Grundsatz, auf dem dieser gesamte Artikel ruht — das Ergebnis verifizieren statt dem Vorgang vertrauen, in der eigenen Pipeline ebenso wie in der Bibliothek
Für die umliegende Archivarbeit siehe die Erläuterungen zu PDF/A- und PDF/UA-Preflight, echter Redaktion und Inhaltsentfernung und PDF/A-3-XMP-Erweiterungs-Schemata für Factur-X, die die Metadaten-Seite abdeckt, wenn das archivierte Dokument zusätzlich strukturierte Rechnungsdaten trägt
PDFlibPas ist eine native Pascal-PDF-Bibliothek für Delphi, C++Builder und Lazarus, sodass Konvertierung, Reparatur und Validierung sämtlich im eigenen Prozess ablaufen ohne externes Werkzeug in der Kette — siehe die PDFlibPas-Produktseite für die unterstützten PDF/A-Parts und Plattformen