PDFPipe

Signatures, encryption and attachments / Embedded files in PDF/A-3

Embedding a file in a PDF/A-3 hybrid invoice

The mechanism behind hybrid invoices: a machine-readable XML payload carried inside an archivable PDF, so one file serves both the person and the system.

Where this API stands

This API does not produce this standard, and there is no option that would. What follows is what the requirement actually is and where a rendered document stands against it. This API has no attachment or embedded file support of any kind. There is no option to attach a file, nothing that writes /Names /EmbeddedFiles or /AF, and the `pdf_a` option writes a PDF/A-1b identification packet, which is the wrong part in any case since attachments are a PDF/A-3 permission. So the human-readable page is what this API produces, and the XML and the embedding are a separate step. Note the version conflict too: leaving `pdf_a` on while building a hybrid invoice puts a part 1 conformance claim in a file that is trying to be part 3.

What the standard requires

The requirements as a checker enforces them, rather than as a procurement document paraphrases them. A file satisfies all of these or it satisfies none of them.

  • The XML is an embedded file stream, reachable from /Names /EmbeddedFiles, and also referenced from the catalogue's /AF array as an associated file.
  • /AFRelationship on the file specification says what the attachment is to the document: /Alternative for a hybrid invoice, meaning the XML and the page are two views of the same thing.
  • The embedded file specification carries /Subtype naming the MIME type, and a /Desc describing what it is.
  • PDF/A-3 permits arbitrary attachments, which is the whole difference from PDF/A-2, and PDF/A-4f is the equivalent permission in the PDF 2.0 generation.
  • The XML filename is fixed by the profile being used, and a receiver looking for a particular name will not find a file that carries a different one.

Who asks for this

Anyone doing e-invoicing in a market where the hybrid formats are used. Factur-X in France and ZUGFeRD in Germany are the same underlying idea, a PDF/A-3 carrying a CII XML invoice, and the receiver's system reads the XML while the person reads the page. The requirement usually arrives from a customer rather than a regulator: a large buyer specifies the format and every supplier complies.

What it looks like in a file

The concrete form of the thing being described, so you can recognise it in a document you have been handed rather than only in a specification.

text
% What makes an attachment an associated file rather than just an
% attachment. Both the /AF array and the relationship are required.

<<
  /Type /Catalog
  /AF [ 18 0 R ]                    % associated files array
  /Names << /EmbeddedFiles 19 0 R >>
>>

% 18 0 obj, the file specification
<<
  /Type /Filespec
  /F (factur-x.xml)
  /UF (factur-x.xml)
  /AFRelationship /Alternative      % the XML and the page are one thing
  /Desc (Factur-X invoice data)
  /EF << /F 20 0 R >>
>>

What to do about it

Render the human-readable invoice here, generate the XML from the same source data rather than from the rendered page, and combine them downstream with a library that understands the profile. The mustangproject is the usual open-source answer for ZUGFeRD and Factur-X, and it handles the PDF/A-3 conversion, the /AF wiring and the XMP extension schema that the profiles require. Validate the XML against EN 16931 before embedding it, because a hybrid invoice with invalid XML is rejected by the receiver's system while looking perfect on screen.

How to check a file rather than assume

veraPDF checks the PDF/A-3 conformance. The Mustang validator checks the whole hybrid: the PDF/A level, the presence and relationship of the attachment, and the XML against the profile's rules. `mutool show file.pdf trailer/Root/Names/EmbeddedFiles` will tell you quickly whether anything is attached at all.

The mistake people make here

Attaching the XML and stopping there. An attachment that is not also listed in /AF with the right /AFRelationship is a file that happens to be inside a PDF, and a compliant receiver will not treat it as the invoice data. The relationship is the part that makes it a hybrid document rather than a document with something stapled to it.

Frequently asked

Does this API produce Embedded files in PDF/A-3?

Not produced by this API. This API has no attachment or embedded file support of any kind. There is no option to attach a file, nothing that writes /Names /EmbeddedFiles or /AF, and the `pdf_a` option writes a PDF/A-1b identification packet, which is the wrong part in any case since attachments are a PDF/A-3 permission. So the human-readable page is what this API produces, and the XML and the embedding are a separate step. Note the version conflict too: leaving `pdf_a` on while building a hybrid invoice puts a part 1 conformance claim in a file that is trying to be part 3.

Can I turn on the pdf_a option and be done?

No. The option writes an XMP packet declaring PDF/A-1b into the document catalogue, and the render response comes back carrying a warning that says in as many words that this is best-effort and that full conformance requires tagged structure and an embedded ICC profile. It is a claim written into the file, not a certificate earned by it, and a validator checks the claim against the bytes. Read the warning array in the response rather than taking the absence of an error as a pass.

Should I convert before rendering or after?

After, always. A conformance converter rewrites colour, embeds profiles, sets boxes and stamps metadata on a finished PDF. Trying to satisfy a conformance target from the source side means guessing at what the converter would have done, and the guesses that go wrong are expensive: flattening transparency you did not need to flatten changes what the page looks like.

Standards this one is confused with

The neighbours that matter are the ones people mistake for this, not the ones that sort next to it.

Conformance is decided after a file exists. Render the document, then check it, then convert only what the checker actually flags.