Electronic Invoices (ZUGFeRD / Factur-X) | |
A hybrid electronic invoice carries the same invoice twice: the page a person reads, and the xml a machine reads, embedded inside the same pdf. ZUGFeRD (Germany) and Factur-X (France) are the same standard under two names, and both are what most European e-invoicing rules ask for.
SelectPdf turns a document into such an invoice with a single call: AddZugferdInvoice(Stream, ZugferdProfile, PdfAttachmentRelationship) embeds the invoice xml as an associated file and writes the metadata that invoice software looks for. The name of the embedded file is derived from the profile rather than taken from you - the standards fix it and recipients look it up by name.
The carrier must be a PDF/A-3 document (PdfStandard.PdfA3B, PdfStandard.PdfA3U or PdfStandard.PdfA3A), which the invoice standards require. A document that is not PDF/A-3 is refused with a message naming the fix, rather than producing a file no invoice software will accept. |
Electronic invoices are available only in the full commercial SelectPdf library. They are not available in the free SelectPdf community edition. |
SelectPdf embeds and identifies the invoice xml you supply. Producing that xml - and making sure its content matches the visible invoice - is your application's job; SelectPdf does not generate or validate it. |
Convert the invoice page as PDF/A-3, then attach the xml to the converted document before saving it. Use PdfStandard.PdfA3A together with AccessibilityStandard if the visible invoice should also be accessible, which the standards recommend.
// the carrier: a PDF/A-3 document produced from the invoice page HtmlToPdf converter = new HtmlToPdf(); converter.Options.PdfStandard = PdfStandard.PdfA3B; PdfDocument doc = converter.ConvertUrl("https://www.example.com/invoice/2026-001"); // embed the invoice xml and mark the document as an electronic invoice using (FileStream xml = File.OpenRead("factur-x.xml")) { doc.AddZugferdInvoice(xml, ZugferdProfile.En16931, PdfAttachmentRelationship.Alternative); } // save pdf document doc.Save("invoice-2026-001.pdf"); // close pdf document doc.Close();
There is an overload that takes the path of the xml file instead of a stream. Either way the xml is read immediately, so the file or stream is yours to release as soon as the call returns.
The ZugferdProfile value says how much of the EN 16931 semantic model the embedded xml carries. It is recorded in the invoice metadata as the conformance level, so it must describe the xml you actually embed.
Profile | Meaning |
|---|---|
Minimum | Accounting information only ("Buchungshilfe"). Not a complete invoice. |
BasicWL | Header and footer data without invoice lines ("without lines"). |
Basic | A subset of EN 16931 covering simple invoices, with lines. |
En16931 | The full European semantic standard (formerly COMFORT). The usual choice. |
Extended | EN 16931 plus additional business terms for more complex processes. |
XRechnung | The German public-sector reference profile. The embedded file is named xrechnung.xml instead of factur-x.xml. |
The PdfAttachmentRelationship value records that relationship, and recipients rely on it:
Alternative - the xml and the visible page carry exactly the same invoice content. This is mandatory in Germany for the BASIC, EN 16931, EXTENDED and XRECHNUNG profiles, and is the usual value.
Data - the visible page carries more than the xml does. Always the case for the MINIMUM and BASIC WL profiles, which are not complete invoices.
Source - the visible page was generated from the xml.
An invoice that already exists as a pdf can be converted to PDF/A-3 first with ConvertToPdfA(PdfStandard) and then given the xml. A document that is already PDF/A-3 needs no conversion.
// load the existing invoice and make it an archival PDF/A-3 document PdfDocument doc = new PdfDocument("invoice.pdf"); doc.ConvertToPdfA(PdfStandard.PdfA3B); // embed the invoice xml and mark the document as an electronic invoice doc.AddZugferdInvoice("factur-x.xml", ZugferdProfile.En16931, PdfAttachmentRelationship.Alternative); doc.Save("invoice-hybrid.pdf"); doc.Close();
Some recipients still require the older ZUGFeRD 2.0 metadata schema. An overload taking ZugferdSchema writes that instead of the current Factur-X 1.0 schema; the embedded file is then named zugferd-invoice.xml. Use it only when a recipient asks for it.
A PDF/A document may only carry metadata from the schemas the standard predefines, unless the schema itself is described inside the document. That is the mechanism the invoice metadata is built on, and it is available for metadata a recipient's system needs to find in your documents: AddXmpExtensionSchema(String, String, String, IDictionaryString, String) declares the schema and writes the values of its properties, so the document stays conformant.