Sie haben eine Factur-X-Rechnung erstellt und jede Container-Prüfung ist erfolgreich. Der Katalog enthält ein /AF-Array, der EmbeddedFiles-Namensbaum verweist auf die richtige Dateispezifikation, die eingebettete factur-x.xml hat die korrekte /AFRelationship von Alternative, und die integrierte Funktion ValidateFacturXInvoice gibt 1 zurück. Dann lassen Sie dieselbe Datei durch veraPDF laufen, den Referenzprüfer, den Steuerportale verwenden, und dieses entscheidet, dass das gesamte Dokument kein gültiges PDF/A-3 ist. Die Struktur ist korrekt. Die Metadaten sind das Problem, und dieser Fehler ist einer der am leichtesten zu übersehenden im gesamten E-Rechnungs-Workflow
Es lohnt sich, den Grund dafür vollständig zu verstehen, denn er erklärt eine Klasse von PDF/A-Fehlern, die nichts mit der sichtbaren Seite oder dem Anhang zu tun haben, sondern ausschließlich damit, wie XMP sich selbst beschreibt. Dies ist die Falle, die sich hinter einer grünen Container-Prüfung verbirgt
Die vier Eigenschaften, an denen die Datei scheitert
Eine Factur-X-Rechnung schreibt vier benutzerdefinierte Eigenschaften in ihr XMP-Paket, damit nachgelagerte Software das Rechnungsprofil lesen kann, ohne die eingebettete XML-Datei parsen zu müssen. Sie befinden sich im Factur-X-Namensraum unter dem Präfix fx: fx:DocumentFileName, fx:DocumentType, fx:Version und fx:ConformanceLevel. Dies sind genau die Metadaten, die ein Leseprogramm benötigt, um zu wissen, dass dieses PDF eine EN 16931-Rechnung namens factur-x.xml in der Version 1.0 enthält
Keine dieser vier Eigenschaften ist Teil eines von PDF/A vordefinierten XMP-Schemas. Die Identifikationsschemata Dublin Core, XMP Basic, PDF und PDF/A sind einem konformen Leseprogramm bekannt, fx: jedoch nicht. Wenn veraPDF das XMP durchläuft und auf eine Eigenschaft stößt, deren Namensraum es nicht erkennt, sucht es nach einer Deklaration, die ihm die Bedeutung der Eigenschaft erklärt. Fehlt diese Deklaration, meldet es einen Fehler gegen ISO 19005-3 Klausel 6.6.2.3.1, die vorschreibt, dass jede Eigenschaft, die nicht aus einem vordefinierten Schema stammt, in einem PDF/A-Erweiterungsschema beschrieben werden muss. Vier undeklarierte Eigenschaften, vier Möglichkeiten für die Ablehnung der Datei, und keine einzige davon ist für eine Container-Prüfung sichtbar
Warum PDF/A eine bloße benutzerdefinierte Eigenschaft ablehnt
Die Regel wirkt pedantisch, bis man sich daran erinnert, wofür PDF/A gedacht ist. Das Format existiert, damit eine Datei auch in Jahrzehnten noch geöffnet und verstanden werden kann, und zwar von Software, die nie von den Konventionen des Jahres 2026 gehört hat. Von einem konformen Leseprogramm wird erwartet, dass es das Dokument aus sich heraus versteht, ohne eine externe Registrierung konsultieren zu müssen
Benutzerdefinierte Metadaten brechen dieses Versprechen, es sei denn, die Datei bringt ihre eigene Beschreibung mit. Bei einer bloßen fx:ConformanceLevel-Eigenschaft kann ein zukünftiges Leseprogramm nicht wissen, an welchen Namensraum-URI das fx-Präfix gebunden ist, ob der Wert Text, ein Datum oder eine Ganzzahl ist, oder ob die Eigenschaft das Dokument selbst oder eine externe Ressource beschreibt. Der Mechanismus des PDF/A-Erweiterungsschemas schließt diese Lücke. Er ermöglicht es der Datei, in einer festen XMP-Struktur den Namensraum, das Präfix und für jede Eigenschaft einen Werttyp und eine Kategorie von internal oder external zu deklarieren. Sobald diese Deklaration vorhanden ist, ist die Eigenschaft selbsterklärend und Klausel 6.6.2.3.1 ist erfüllt. Ohne sie hat der Validator keine andere Wahl, als die Eigenschaft als unverständlich zu behandeln und die Datei abzulehnen. Die Unterscheidung der Kategorien ist hier wichtig: Rechnungseigenschaften wie diese beschreiben Daten, die von außerhalb des PDF-Prozessors stammen, daher werden sie als external und nicht als internal deklariert
Was die Deklaration des Erweiterungsschemas enthält
Die Deklaration ist eine rdf:Description im XMP-Paket, die die drei von AIIM definierten Namensräume pdfaExtension, pdfaSchema und pdfaProperty verwendet. Innerhalb einer pdfaExtension:schemas-Bag befindet sich ein Schemaeintrag, der das Factur-X-Schema benennt, seinen pdfaSchema:namespaceURI und sein pdfaSchema:prefix angibt und dann die vier Eigenschaften in einer pdfaSchema:property-Sequenz auflistet. Jede Eigenschaft trägt einen Namen, einen pdfaProperty:valueType von Text und eine pdfaProperty:category von external. Das folgende illustrative Markup zeigt den Aufbau dieses Blocks
<rdf:Description rdf:about=""
xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">
<pdfaExtension:schemas>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>Factur-X PDFA Extension Schema</pdfaSchema:schema>
<pdfaSchema:namespaceURI>urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>
<pdfaSchema:prefix>fx</pdfaSchema:prefix>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>DocumentFileName</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>
</rdf:li>
<!-- DocumentType, Version, ConformanceLevel declared the same way -->
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
</rdf:Bag>
</pdfaExtension:schemas>
</rdf:Description>
Der Namensraum-URI und das Präfix sind keine festen Zeichenfolgen. Sie richten sich nach dem Profil. Ein Factur-X-Dokument verwendet urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0# mit dem Präfix fx, während eine ZUGFeRD 2.0-Datei, die über zugferd-invoice.xml ausgewählt wird, zu einem anderen URI unter ihrem eigenen Schemanamen aufgelöst wird. Das Erweiterungsschema muss denselben Namensraum-URI deklarieren, den der Eigenschaftenblock tatsächlich verwendet, andernfalls kann der Validator die beiden immer noch nicht miteinander verbinden. PDFlibPas leitet beide Werte aus dem von Ihnen übergebenen Dateinamen und der Version ab, sodass die Deklaration und der Eigenschaftenblock stets übereinstimmen
Wie die Hilfsfunktion beide Hälften zusammen schreibt
In PDFlibPas stellen Sie dieses XML nicht von Hand zusammen. Sie versetzen das Dokument in einen PDF/A-3-Modus und rufen eine einzige Methode auf. Zunächst muss das Konformitäts-Flag festgelegt werden, da Factur-X PDF/A-3 erfordert. Der Aufruf von SetPDFAMode(7) wählt die Stufe PDF/A-3u, die im Identifikationsschema pdfaid:part auf 3 und pdfaid:conformance auf U setzt. Das XMP-Paket trägt nun den richtigen Teil und die richtige Konformität, bevor irgendwelche Rechnungsmetadaten hinzugefügt werden
var
FileID: Integer;
begin
PDF.SetPDFAMode(7); // PDF/A-3u: pdfaid:part=3, conformance=U
PDF.NewDocument;
// draw the human-readable invoice page here
FileID := PDF.AddFacturXAssociatedFileFromString(
InvoiceXML, // raw UTF-8 XML bytes
'EN16931', // ConformanceLevel
'factur-x.xml', // embedded file name
'Factur-X invoice XML', // /Desc text
'Alternative', // /AFRelationship
'1.0', // profile version
''); // optional country code
if FileID = 0 then
Exit; // not PDF/A-3, or XML/profile mismatch
PDF.SaveToFile('factur-x.pdf');
end;
Ein einziger Aufruf von AddFacturXAssociatedFileFromString erledigt die Arbeit, die der fehlerhaften Datei fehlte. Er bettet das XML als zugehörige PDF/A-3-Datei mit der von Ihnen benannten Beziehung ein und zeichnet die vier fx-Eigenschaften zusammen mit dem Schemanamen, dem Namensraum-URI und dem Präfix für das gewählte Profil auf. Wenn das Dokument gespeichert wird, fügt ein interner Schritt namens ApplyFacturXMetadata sowohl den Eigenschaftenblock als auch die passende pdfaExtension:schemas-Deklaration in das XMP-Paket ein, sodass die benutzerdefinierten Eigenschaften bereits beschrieben ankommen. Die Methode gibt 0 zurück, wenn sich das Dokument nicht in einem PDF/A-3-Modus befindet oder wenn das XML nicht dem deklarierten Profil entspricht, was genau der Schutzmechanismus ist, der verhindert, dass eine fehlerhafte Rechnung überhaupt erst in die Datei gelangt
Der blinde Fleck, den die Container-Prüfung nicht sieht
Dies ist der Punkt, den man klar benennen muss, denn er ist der Grund, warum sich der Fehler versteckt. ValidateFacturXInvoice prüft den Container. Es bestätigt, dass der Katalog einen /AF-Eintrag hat, der EmbeddedFiles-Namensbaum vorhanden ist, das Rechnungs-XML existiert, der Name der eingebetteten Datei mit dem Profil übereinstimmt, die Richtlinien-ID im XML mit der Konformitätsstufe übereinstimmt und die /AFRelationship eine ist, die PDF/A-3 zulässt. Das sind echte Prüfungen, und sie fangen echte Fehler ab. GetFacturXValidationIssues meldet diese namentlich mit Bezeichnern wie MissingCatalogAF, NotPDFA3, ConformanceGuidelineMismatch, InvalidAFRelationship und InvalidFileNameProfile
Was jedoch nicht geprüft wird, ist, ob das XMP-Erweiterungsschema vorhanden und korrekt ist. Eine Datei, deren Container fehlerfrei ist, deren fx-Eigenschaften aber undeklariert sind, besteht jede Fehlerprüfung und gibt 1 zurück, da nichts in dieser Liste den pdfaExtension:schemas-Block untersucht. Das ist genau der Grund, warum eine manuell erstellte Rechnung oder eine, die durch eine Pipeline erzeugt wurde, die den Eigenschaftenblock ohne die Deklaration geschrieben hat, den integrierten Validator problemlos passieren und dennoch bei veraPDF an Klausel 6.6.2.3.1 scheitern kann. Der Container-Validator und der PDF/A-Metadaten-Validator beantworten unterschiedliche Fragen, und nur der vollständige PDF/A-Prüfer beantwortet die zweite
Fehler lesen, um zu wissen, welche Ebene gebrochen ist
Da die beiden Ebenen unabhängig voneinander fehlschlagen, besteht die richtige Diagnosegewohnheit darin, zuerst die Container-Fehler zu lesen und ein fehlerfreies Ergebnis nur als Aussage über den Container zu behandeln, niemals über PDF/A-Metadaten. Führen Sie die integrierte Validierung aus, sammeln Sie die Fehlerliste und reagieren Sie darauf, bevor Sie zu einem externen Werkzeug greifen
var
Issues: WideString;
begin
if PDF.ValidateFacturXInvoice = 0 then
begin
Issues := PDF.GetFacturXValidationIssues('|');
// container-level identifiers, for example:
// MissingCatalogAF, NotPDFA3, MissingEmbeddedFilesNameTree,
// ConformanceGuidelineMismatch, InvalidAFRelationship
WriteLn('Container issues: ', Issues);
end
else
WriteLn('Container OK; verify XMP extension schema with a PDF/A checker.');
end;
Wenn dieser Aufruf einen Fehlernamen zurückgibt, liegt der Fehler im Container und die Meldung sagt Ihnen, an welcher Stelle. Wenn er ein fehlerfreies Ergebnis liefert und veraPDF die Datei dennoch ablehnt, liegt der Fehler fast immer beim XMP-Erweiterungsschema, und die Lösung besteht darin, AddFacturXAssociatedFileFromString die Metadaten schreiben zu lassen, anstatt den Eigenschaftenblock selbst zu konstruieren. Wenn Sie diese beiden Fragen gedanklich trennen, wird aus einer rätselhaften Ablehnung eine einzeilige Diagnose: Containerprobleme tauchen über die Fehlerliste auf, Schemadeklarationsprobleme zeigen sich nur über einen PDF/A-Validator, und die Verwechslung der beiden ist der Grund, warum sich der Fehler verstecken kann
Das breitere Bild der PDF/A- und PDF/UA-Konformität, einschließlich der Durchführung eines Preflight-Durchlaufs, bevor eine Datei Ihren Build verlässt, wird in der Einführung in den PDF/A- und PDF/UA-Preflight behandelt. Wenn Ihre Rechnung zudem barrierefrei sein muss, ist der Strukturbaum, auf den PDF/A-3a und Tagged PDF angewiesen sind, das Thema des Artikels über Barrierefreiheit bei Tagged PDF. Die hier beschriebene Handhabung von Erweiterungsschemata wird als Teil der PDFlibPas Delphi PDF Library ausgeliefert, zusammen mit der in diesem Blog dokumentierten Profilunterstützung für Factur-X, ZUGFeRD und XRechnung