E-invoicing (ZUGFeRD/Factur-X)
ZUGFeRD (Germany) and Factur-X (France) are the same standard: a hybrid e-invoice that is a human-readable PDF and a machine-readable XML invoice in one file. The PDF is a PDF/A-3 document, the invoice data is embedded as factur-x.xml (xrechnung.xml for the XRechnung profile), and XMP metadata declares what is embedded so validators and accounting systems find it.
Papyra produces the PDF side of this completely: the PDF/A-3 container, the embedded file, and the Factur-X XMP extension schema. The invoice XML itself is yours - Papyra embeds it as-is and does not generate or validate invoice data.
Attaching a Factur-X / ZUGFeRD invoice
Section titled “Attaching a Factur-X / ZUGFeRD invoice”byte[] invoiceXml = File.ReadAllBytes("invoice-cii.xml"); // your EN 16931 CII XML
byte[] pdf = document.RenderAsPdf(new PdfRenderOptions{ Standard = PdfStandard.PdfA3, // required - see below FacturX = new FacturXOptions(invoiceXml, FacturXProfile.En16931),});That single option takes care of everything the standard mandates:
- the XML is embedded as
factur-x.xml(xrechnung.xmlfor the XRechnung profile, as its spec section mandates) with MIME typetext/xmland/AFRelationship /Alternative - the file is registered in the PDF’s embedded-files name tree and the catalog’s
/AFarray (ISO 19005-3) - the XMP packet carries the Factur-X properties (
fx:DocumentType,fx:DocumentFileName,fx:Version,fx:ConformanceLevel) plus the PDF/A extension schema declaring thefxnamespace - without which PDF/A validators reject the file
Profiles
Section titled “Profiles”The profile must match what your XML actually conforms to - it is written into the XMP as fx:ConformanceLevel:
FacturXProfile |
Conformance level | Embedded as | Meaning |
|---|---|---|---|
Minimum |
MINIMUM |
factur-x.xml |
Booking aids only; not a full invoice under EU law |
BasicWl |
BASIC WL |
factur-x.xml |
Header/footer data without invoice lines |
Basic |
BASIC |
factur-x.xml |
Simple invoices, subset of EN 16931 |
En16931 |
EN 16931 |
factur-x.xml |
The complete European semantic model (a.k.a. COMFORT) |
Extended |
EXTENDED |
factur-x.xml |
EN 16931 plus trade-specific extras |
XRechnung |
XRECHNUNG |
xrechnung.xml |
German CIUS for invoices to public authorities |
Generic attachments
Section titled “Generic attachments”Any file can be embedded, with or without the Factur-X option:
byte[] pdf = document.RenderAsPdf(new PdfRenderOptions{ Standard = PdfStandard.PdfA3, Attachments = [ new PdfAttachment("source-data.csv", csvBytes, "text/csv", PdfAttachmentRelationship.Data, "Raw figures behind the report"), ],});PdfAttachment takes the file name (ASCII, unique per document), the raw bytes, the MIME type, the /AFRelationship (DATA, SOURCE, ALTERNATIVE, SUPPLEMENT, UNSPECIFIED), and an optional description shown by PDF viewers. Attachment streams are Flate-compressed in the file; viewers extract the original bytes.
Standard = PdfStandard.PdfA3 isn’t the only option here - plain PdfStandard.Pdf20 also permits attachments (see Encryption). Reach for PDF/A-3 when the output also needs to be an archival PDF/A file; reach for PDF 2.0 otherwise.
A PDF/A-3 (or PDF 2.0) standard is required
Section titled “A PDF/A-3 (or PDF 2.0) standard is required”PDF/A-2 (the default) forbids arbitrary embedded files, and Papyra’s PDF/A-4 output has no attachment support (that would be the separate A-4f flavour) - so rendering with Attachments throws unless Standard is PdfStandard.PdfA3 or PdfStandard.Pdf20. FacturX is stricter: its XMP extension schema needs the PDF/A machinery, so it requires PdfStandard.PdfA3 specifically - Pdf20 is not enough.
The Papyra NuGet package ships a Roslyn analyzer that flags the typical mistake at compile time: PAPY101 warns when a PdfRenderOptions initializer sets Attachments without Standard being PdfStandard.PdfA3 or PdfStandard.Pdf20, or sets FacturX without Standard = PdfStandard.PdfA3. The analyzer only sees literal initializers - options flowing through variables or methods are checked at render time.
What is validated, what is not
Section titled “What is validated, what is not”The repository’s test suite validates the PDF/A-3 conformance of attachment-bearing output (including the Factur-X XMP extension schema) against veraPDF. veraPDF checks PDF/A only - whether the embedded XML is a valid invoice for the declared profile is a separate concern; validate the XML with a Factur-X/ZUGFeRD validator (e.g. Mustang) before embedding it.
See also
Section titled “See also”- PDF/A output - the standards themselves
- PDF render options - the full options surface
© 2026 Stefan Nikolei. Payments are processed by Paddle.com as merchant of record.