Creating ZUGFeRD/Factur-X Invoices with iText

Tue - 11/26/2024, by

With the onset of e-invoicing legislation coming into effect across Europe, there is renewed interest in the ZUGFeRD/Factur-X hybrid invoice standard. This article examines how the standard has evolved, and how you can use the dual-licensed iText Core library to easily create ZUGFeRD-compatible files.

Share this article

ZUGFeRD blog banner

Introduction

Electronic invoicing offers numerous benefits over traditional paper-based invoicing, including increased efficiency, reduced costs, and improved accuracy. However, e-invoicing is swiftly becoming a necessity, not just a convenience. For example, EN 16931 (the European standard for e-invoicing) will come into effect on 1st January 2025. This standard specifies what invoices must contain to comply with the EU Directive on electronic invoicing in public procurement. Compliance with EN 16931 is crucial as it ensures uniformity, accuracy, and legal validity of invoices across the EU, facilitating smoother cross-border transactions and reducing administrative burdens.

Moreover, the landscape of electronic invoicing is evolving, with several countries moving towards making e-invoicing mandatory for nearly all transactions. In Germany, electronic invoices will be required for all B2B transactions starting in 2028, with transitional rules taking effect from January 1, 2025. Meanwhile in France, larger companies are required to issue e-invoices from September 2026, with Small and Medium Enterprises (SMEs) following by September 2027. All companies in France must be able to receive electronic invoices by September 2026.

With these deadlines quickly approaching, we’ve seen a sharp increase in customer queries about using iText to create ZUGFeRD e-invoices, where the electronic invoice data is embedded into a PDF. This makes sense, since such hybrid invoices are also compliant with the EN 16931 standard and so can be a great solution for companies of any size. They also adhere to GDPR requirements, ensuring that sensitive customer and transaction data is handled securely.

The usual question is “Can I create ZUGFeRD-compliant PDF documents with iText?” The short answer is yes, and this article will show you to easily produce valid ZUGFeRD (and its French equivalent Factur-X) using the dual-licensed iText Core PDF library. The longer answer is since these documents are essentially PDF/A-3 files with embedded XML invoice data, this is technically possible with all versions of iText since version 5. However, achieving PDF/A conformance is significantly improved in the latest versions of iText, and so that is our focus for this article. 

First of all, though, we should probably explain what EN 16931 defines as an e-invoice. We’ll then take a closer look at how the ZUGFeRD/Factur-X standard has evolved, and its relationship to EN 16931.

What are EN 16931 E-invoices?

Firstly, an “e-invoice” is not simply an invoice which is sent electronically. The EN 16931 European Standard defines an e-invoice as a structured electronic invoice adhering to a unified semantic data model. EN 16931 was established to ensure common interoperability and a common understanding of e-invoice content across the European Union. Countries outside the EU have also adopted the standard for their own e-invoicing systems, such as Norway, Switzerland , and the United Kingdom.

In addition to the semantic data model which defines the common structure and business terms used in e-invoices, another core element is the syntax binding. This specifies how the data model is applied to different syntaxes, such as ISO 19845 UBL (Universal Business Language) and UN/CEFACT CII (Cross Industry Invoice). While both are XML-based standards, CII was designed specifically for exchanging invoices electronically, while UBL is a more generalized format for electronic business documents. However, e-invoices using either standard can be compliant with EN 16931.

This means many businesses will need to be able to both send and receive XML e-invoice data. Below is a screenshot of example invoice data in CII format:

A view of XML e-invoice data
An example of machine-readable e-invoice data.

Of course, such e-invoices are designed to be processed by machines, rather than humans, However, the PDF Association has recently released a useful e-invoice viewer tool which will render the invoice data as HTML. Here’s what that looks like:

An HTML rendering of the e-invoice data
The same e-invoice data, but rendered into a more human-readable format.

This tool is free to use, and could be very helpful for small businesses which suddenly start receiving e-invoices,  without any way to easily visualize them. The tool supports conversion of e-invoices in both UBL and CII formats.

For those businesses needing to send e-invoices though, there is a better way—especially if they already generate PDF invoices. 

ZUGFeRD and Factur-X Hybrid Invoices

ZUGFeRD was created by the Forum for Electronic Invoicing in Germany (FeRD), while Factur-X was the brainchild of the Forum National de la Facture Electronique et des Marchés Publics Electroniques (FNFE-MPE) in France. While both standards were developed independently, they share many similarities and a common goal, which led to the two standards being unified in 2020.

The aim was to aid small and medium businesses to implement electronic invoice processes. Selecting PDF to form the basis for these standards makes sense since PDFs are already commonly used to share digital invoices between businesses.

However, both ZUGFeRD and Factur-X specifically chose the PDF/A-3 archiving standard as a basis. This makes sense, since PDF/A is designed for the long-term archival of electronic documents and ensures they can be stored and read reliably in the future.

The reason for using PDF/A-3 rather than earlier revisions is its “associated files” feature, which allows the machine-readable XML data to be embedded into the human-readable PDF invoice document. This hybrid approach ensures that invoices can be both easily read by humans and processed by machines.

While ZUGFeRD and Factur-X invoices only allow a single XML invoice file attachment, additional documents may also be embedded which may be related to the invoice or used for verification purposes. These could be spreadsheets, CAD drawings, images, or even other PDF documents such as delivery notes or reports. For additional attachments you need to specify the appropriate MIME (Multipurpose Internet Mail Extension) type in the PDF/A document, though no additional XMP metadata is required.

The standard supports different profiles which can incorporate CIUS (Core Invoice Usage Specifications) to ensure the data meets specific national, regional, or sectoral requirements while still remaining compliant with EN 16931. For example, XRechnung is a CIUS used in Germany for public sector invoicing.

Finally, a digital signature can be applied to the PDF/A-3 container to ensure the document cannot be altered without invalidating the signature. This may be needed to comply with certain legal and tax regulations.

The History of ZUGFeRD and Factur-X

ZUGFeRD stands for "Zentraler User Guide Forum elektronische Rechnung Deutschland", or “Central User Guide of the German Electronic Invoice Forum” in English. Factur-X on the other hand is derived from the French word “facture”, meaning invoice. The “X” represents the cross-border nature of the standard, indicating it can be used across different countries and systems. 

Since version 2.1, the ZUGFeRD and French Factur-X standard have been completely compatible, and both countries worked together to further develop the standard. Officially, both are also unified under the Factur-X name, although it is still commonly referred to as ZUGFeRD. 

ZUGFeRD 1.0

  • Release Date: June 2014
  • Format: Custom XML flavor
  • Features: Introduced the hybrid invoice format combining PDF/A-3 and XML. The XML part was based on a custom schema.
  • Profiles: Introduced profiles (BASIC, COMFORT, EXTENDED) to cater to various business needs
  • Compliance: Not compliant with the European standard EN 16931.

ZUGFeRD 2.0

  • Release Date: March 2019
  • Format: Based on UN/CEFACT Cross Industry Invoice (CII) schema
  • Features: Improved interoperability and compliance with the European standard EN 16931. The XML part was standardized to align with international standards.
  • Profiles: Introduced different profiles (MINIMUM, BASIC WL) for compatibility with the French Factur-X standard. These should not be used in Germany since they don't contain all required invoicing information.

ZUGFeRD 2.1

  • Release Date: March 2020
  • Format: Continued use of UN/CEFACT CII schema
  • Features: Minor updates and corrections to improve usability and compliance. Fully compatible with Factur-X 1.0.05.
  • Profiles: Added a new profile to support the German CIUS “XRechnung”.

ZUGFeRD 2.2

  • Release Date: February 2022
  • Format: Technically identical to Factur-X 1.0.06
  • Features: Further alignment with European standards and additional profiles to support specific use cases in France and Germany.
  • Profiles: Enhanced profiles to address specific needs, such as the EXTENDED_B2B_FR for French use cases.

ZUGFeRD 2.3

  • Release Date: September 2024
  • Format: Technically identical to Factur-X 1.0.07
  • Features: Uses the CII D22B standard instead of D16B. Improved interoperability with the French B2B e-invoicing mandate, introduction of allowable rounding inaccuracies in the EXTENDED profile.
  • Profiles: Updated EXTENDED profile to support diverse business cases, including compliance with the French e-invoicing requirements.

Valid XML Schemas for EN 16931

As we mentioned earlier, compliance with EN 16931 requires the structured XML data to use one of the approved formats: UBL or CII. The ZUGFeRD 1.0 custom XML format was derived from the CII standard, however, it is not compliant with EN 16931. ZUGFeRD 2.0 and Factur-X were specifically designed to meet the requirements of EN 16931 and were based upon the CII D16B version.

The XML syntax for ZUGFeRD 2.3 and Factur-X 1.0.07 is instead based on the CII D22B standard which includes updates to be better aligned for EN 16931 requirements.

ZUGFeRD/Factur-X and PEPPOL

If you’re at all familiar with the standards for e-invoicing across Europe, you’ve probably heard of the PEPPOL e-invoicing platform. Developed to facilitate cross-border electronic procurement within the European Union, PEPPOL (which stands for Pan-European Public Procurement Online) is both a network and a set of standards for e-procurement and e-invoicing. 

ZUGFeRD/Factur-X invoices meeting the standards for cross-border transactions can be transmitted via the PEPPOL network. PEPPOL is broadly used across Europe, and beyond with the network being adopted by countries such as Australia, Canada, Singapore, Japan, and the US. 

As noted above, ZUGFeRD 2.1 and later support CIUS “XRechnung”, which is compatible with PEPPOL’s BIS (Business Interoperability Specification) Billing 3.0 profile; which itself is based on EN 16931. For other countries, the embedded CII can be adapted using CIUS to meet specific local or industry requirements.  

How ZUGFeRD and Factur-X Hybrid Invoices Work

As mentioned earlier, ZUGFeRD and Factur-X make use of an important feature of the PDF/A-3 standard: attachments (or associated files). This feature was introduced in PDF/A-2, although attachments were restricted to other PDF/A documents. PDF/A-3 allows files of any type to be attached, and so the machine-readable XML invoice data can be embedded in the PDF container. 

In PDF/A-3 you define this with the /AF (Associated Files) key in the document catalog. A fixed file name for the XML attachment is required; however, the file name differs depending on the revision of the standard:

  • ZUGFeRD 1.0: ZUGFeRD-invoice.xml
  • ZUGFeRD 2.0: zugferd-invoice.xml
  • ZUGFeRD 2.1 (and newer) and Factur-X: factur-x.xml

If we open a hybrid invoice document in iText’s debugging tool RUPS, we can see how this structure looks. We see the /AF array, and also that the /AFRelationship is "Alternative" This means the PDF document and the attached XML are alternative presentations of the same content.

By selecting the Stream tab in the lower-right pane, we can also view the embedded XML invoice data. Neat, huh?

A view of a ZUGFeRD PDF in RUPS
Examining the structure of a ZUGFeRD-compatible PDF in RUPS.

Since the /AFRelationship key specifies the relationship between the embedded file and the PDF document, this value depends upon certain factors such as the profile, and how the visual representation was created. The current specifications (ZUGFeRD 2.3/Factur-X 1.0.07 at the time of writing) state the "Data" value is valid for the MINIMUM and BASIC WL profiles in both France and Germany. However, for other profiles, such as BASIC and EN 16931, the value must be set to "Alternative" to be legally valid in Germany.

Conversely, in France the /AFRelationship key value can be "Data", "Source", or "Alternative", for these profiles, depending on how the PDF part was created.

These values may change in future revisions of the specification, so we advise checking the official documentation for more information.

Creating ZUGFeRD-Compatible Invoices with iText

We’ve created a simple code sample showing how you can use iText Core to create a PDF conforming to the latest ZUGFeRD and Factur-X standards. You can find the complete Java and C# examples on GitHub: Java/.NET.

In order to create ZUGFeRD files you need an XML file containing the machine-readable invoice data. For the purposes of this example, we’ve used some invoice data provided with the current specification available from the official site. If you have a CRM or invoice solution, most likely it allows you to produce the required invoice data in the correct schema. If not, then we may have a solution later in this article.

We won’t go over the requirements for PDF/A-3 creation here, since that was comprehensively covered in a recent article. To summarize, we initialize a PDF/A-3b compliant document with an output intent for color management, and embed the font we’re using for the visualized invoice data. We must also add metadata to the PDF in the eXtensible Metadata Platform (XMP) format since this is a PDF/A requirement.

The key part for ZUGFeRD is embedding the factur-x.xml file into the document as an attachment. As we saw in the previous section, this is defined in the /AF (Associated Files) key.

Attaching the XML File

XMPMeta xmp = createValidXmp(pdfDoc);
pdfDoc.setXmpMetadata(xmp);
document.setFont(font);

File xmlFile = new File(ZUGFERD_XML);

PdfDictionary parameters = new PdfDictionary();
parameters.put(PdfName.ModDate, new PdfDate().getPdfObject());
PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(pdfDoc, new FileInputStream(xmlFile),
    "ZUGFeRD invoice", "factur-x.xml", new PdfName("application/xml"), parameters, PdfName.Alternative);
pdfDoc.addFileAttachment("ZUGFeRD invoice", fileSpec);
PdfArray array = new PdfArray();
array.add(fileSpec.getPdfObject().getIndirectReference());
pdfDoc.getCatalog().put(PdfName.AF, array);

Parsing the XML to Create the Visual Representation

Since we also want to create a visual representation of the invoice, we then parse the XML file to extract the structured invoice data. We can make use of iText’s powerful layout engine to format the text and populate a table with the line items: 

    private static void fillDocumentFromXml(Document document, File xmlFile) throws ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        org.w3c.dom.Document xmlDocument = builder.parse(xmlFile);

       document.add(new Paragraph("Invoice with ZUGFeRD").setFontSize(18));
        document.add(new Paragraph("Ensuring the readability of XML data of electronic invoices").setBackgroundColor(ColorConstants.GRAY));
        Table xmlInfoTable = new Table(UnitValue.createPercentArray(2)).useAllAvailableWidth();
       xmlInfoTable.addCell("Stylesheet-Version:");
       xmlInfoTable.addCell("2.5 vom 10.01.2023");
        xmlInfoTable.startNewRow();
       xmlInfoTable.addCell("Invoice Standard:");
       xmlInfoTable.addCell("Factur-x 1.0 Profil BASIC");
        xmlInfoTable.startNewRow();
       xmlInfoTable.addCell("URN ID");
       xmlInfoTable.addCell(xmlDocument.getElementsByTagName("ram:ID").item(0).getTextContent());
        xmlInfoTable.startNewRow();
       xmlInfoTable.addCell("The XML input file:");
       xmlInfoTable.addCell("factur-x.xml");
        document.add(xmlInfoTable);

        document.add(new Paragraph("Seller").setBackgroundColor(ColorConstants.GRAY));
        Table sellerTable = new Table(UnitValue.createPercentArray(2)).useAllAvailableWidth();
       sellerTable.addCell("Name:");
       sellerTable.addCell(xmlDocument.getElementsByTagName("ram:Name").item(1).getTextContent());
        sellerTable.startNewRow();
       sellerTable.addCell("Address:");
       sellerTable.addCell(xmlDocument.getElementsByTagName("ram:LineOne").item(0).getTextContent());
        sellerTable.startNewRow();
       sellerTable.addCell("Tax number:");
       sellerTable.addCell(xmlDocument.getElementsByTagName("ram:ID").item(2).getTextContent());
        document.add(sellerTable);

        document.add(new Paragraph("Buyer / Beneficiary").setBackgroundColor(ColorConstants.GRAY));
        Table buyerTable = new Table(UnitValue.createPercentArray(2)).useAllAvailableWidth();
       buyerTable.addCell("Name:");
       buyerTable.addCell(xmlDocument.getElementsByTagName("ram:Name").item(2).getTextContent());
        buyerTable.startNewRow();
       buyerTable.addCell("Address:");
       buyerTable.addCell(xmlDocument.getElementsByTagName("ram:LineOne").item(1).getTextContent());
        document.add(buyerTable);
        document.add(new Paragraph("Position data").setBackgroundColor(ColorConstants.GRAY));

        Table positionTable = new Table(UnitValue.createPercentArray(8)).useAllAvailableWidth();
       positionTable.addCell("Pos number");
       positionTable.addCell("Reference");
       positionTable.addCell("Description");
       positionTable.addCell("Net price");
       positionTable.addCell("Quantity");
       positionTable.addCell("Name");
       positionTable.addCell("Tax rate");
       positionTable.addCell("Net amount");
        positionTable.startNewRow();
       positionTable.addCell(xmlDocument.getElementsByTagName("ram:LineID").item(0).getTextContent());
       positionTable.addCell(xmlDocument.getElementsByTagName("ram:GlobalID").item(0).getTextContent());
       positionTable.addCell(xmlDocument.getElementsByTagName("ram:Name").item(0).getTextContent());
       positionTable.addCell(xmlDocument.getElementsByTagName("ram:ChargeAmount").item(0).getTextContent());
       positionTable.addCell(xmlDocument.getElementsByTagName("ram:BilledQuantity").item(0).getTextContent());
        positionTable.addCell("Stk");
       positionTable.addCell(xmlDocument.getElementsByTagName("ram:RateApplicablePercent").item(0).getTextContent());
       positionTable.addCell(xmlDocument.getElementsByTagName("ram:LineTotalAmount").item(0).getTextContent());
        document.add(positionTable);
    } 

Adding the ZUGFeRD XMP Data

So far, we have embedded our XML into a standard PDF/A-3b document, and created a visual representation of the e-invoice data. For ZUGFeRD/Factur-X compliance though, we need to add the all-important XMP metadata which identifies it as such. In addition to the usual XMP requirements for PDF/A, you must include four entries from the ZUGFeRD schema:

  • Document type (always INVOICE)
  • File name of the embedded XML
  • Version of the corresponding XML schema 
  • Name of the applicable profile/conformance level (e.g. BASIC, COMFORT, or EXTENDED).

We’re using the BASIC example XML invoice data supplied with the specification, since this is compatible with the EN 16931 requirements. Depending on the version of ZUGFeRD/Factur-X or the conformance level you’re targeting, some configuration may be required to adapt our code example for your purposes.

For example, earlier versions of the ZUGFeRD standard used zf for the XML namespace prefix, while from ZUGFeRD 2.1 it changed to fx for compatibility with Factur-X. In addition, the name of the embedded XML changed from zugferd-invoice to factur-x. We highly recommend downloading the current version of the specification from the official site to confirm the exact XMP requirements.

    private static XMPMeta createValidXmp(PdfADocument pdfDoc) throws XMPException {
        XMPMeta xmp = pdfDoc.getXmpMetadata(true);
        String zugferdNamespace = "urn:ferd:pdfa:CrossIndustryDocument:invoice:1p0#";
        String zugferdPrefix = "fx";
       XMPMetaFactory.getSchemaRegistry().registerNamespace(zugferdNamespace, zugferdPrefix);

       xmp.setProperty(zugferdNamespace, "DocumentType", "INVOICE");
        xmp.setProperty(zugferdNamespace, "Version", "1.0");
       xmp.setProperty(zugferdNamespace, "ConformanceLevel", "BASIC");
       xmp.setProperty(zugferdNamespace, "DocumentFileName", "factur-x.xml");

        PropertyOptions bagOptions = new PropertyOptions(PropertyOptions.ARRAY);
       xmp.setProperty(XMPConst.NS_PDFA_EXTENSION, "schemas", null, bagOptions);

        String bagPath = "pdfaExtension:schemas";

        int newItemIndex = xmp.countArrayItems(XMPConst.NS_PDFA_EXTENSION, bagPath) + 1;
        String newItemPath = bagPath + "[" + newItemIndex + "]";

        PropertyOptions structOptions = new PropertyOptions(PropertyOptions.STRUCT);
       xmp.setProperty(XMPConst.NS_PDFA_EXTENSION, newItemPath, null, structOptions);

       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, newItemPath, XMPConst.NS_PDFA_SCHEMA, "schema", "Factur-X PDFA Extension Schema");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, newItemPath, XMPConst.NS_PDFA_SCHEMA, "namespaceURI", zugferdNamespace);
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, newItemPath, XMPConst.NS_PDFA_SCHEMA, "prefix", "fx");

        String seqPath = newItemPath + "/pdfaSchema:property";
        PropertyOptions seqOptions = new PropertyOptions(PropertyOptions.ARRAY_ORDERED);
       xmp.setProperty(XMPConst.NS_PDFA_EXTENSION, seqPath, null, seqOptions);

        String firstSeqItemPath = seqPath + "[1]";
        String secondSeqItemPath = seqPath + "[2]";
        String thirdSeqItemPath = seqPath + "[3]";
        String fourthSeqItemPath = seqPath + "[4]";

       xmp.setProperty(XMPConst.NS_PDFA_EXTENSION, firstSeqItemPath, null, structOptions);
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, firstSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "name", "DocumentFileName");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, firstSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "valueType", "Text");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, firstSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "category", "external");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, firstSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "description", "The name of the embedded XML document");

       xmp.setProperty(XMPConst.NS_PDFA_EXTENSION, secondSeqItemPath, null, structOptions);
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, secondSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "name", "DocumentType");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, secondSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "valueType", "Text");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, secondSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "category", "external");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, secondSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "description", "The type of the hybrid document in capital letters, e.g. INVOICE or ORDER");

       xmp.setProperty(XMPConst.NS_PDFA_EXTENSION, thirdSeqItemPath, null, structOptions);
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, thirdSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "name", "Version");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, thirdSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "valueType", "Text");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, thirdSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "category", "external");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, thirdSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "description", "The actual version of the standard applying to the embedded XML document");

       xmp.setProperty(XMPConst.NS_PDFA_EXTENSION, fourthSeqItemPath, null, structOptions);
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, fourthSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "name", "ConformanceLevel");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, fourthSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "valueType", "Text");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, fourthSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "category", "external");
       xmp.setStructField(XMPConst.NS_PDFA_EXTENSION, fourthSeqItemPath, XMPConst.NS_PDFA_PROPERTY, "description", "The conformance level of the embedded XML document");

        return xmp;
    }

With that, all the hard work is over. You now have a valid ZUGFeRD e-invoice which is compliant with the EN 16931 standard!

Let’s take a look at our creation by viewing it in the Xodo PDF Studio application. On the right is the human-readable PDF invoice we created from the e-invoice data. We’ve also selected the “Attachments” pane to show the XML file embedded in the PDF. 

You can also see the metadata we added by clicking the “Properties” button from the “Document” menu, selecting “Additional Metadata”, and then the “Advanced” tab:

Examining the XMP properties of a ZUGFeRD e-invoice
Displaying the ZUGFeRD XMP alongside the human-readable PDF invoice.

Validating ZUGFeRD and Factur-X Invoices

If you want to ensure the hybrid invoices you generate are valid and conform to the specifications, there are a few options available. When creating the PDF/A-3 side, recent versions of iText can automatically inform you if conformance issues are detected. However, to ensure we’ve created a valid document we ran our invoice through the industry-standard veraPDF Conformance Checker:

Validating our PDF/A in the veraPDF Conformance Checker in the
Checking our PDF conforms to the PDF/A-3b requirements.

As for validating the XML e-invoice data contained within the PDF, we used the officially sanctioned Mustang validator which confirms the data is compliant to EN 16931:

Validating the embedded XML with the Mustang validator
Validating the embedded XML e-invoice against the EN 16931 requirements.

Taking Things Further with pdfInvoice

Our example assumes you need to generate ZUGFeRD/Factur-X PDFs from existing XML, which is already in the correct schema. However, if you want to generate and validate compliant XML as well as embed it, you can take a look at an example implementation on GitHub called pdfInvoice. While this implementation is based on ZUGFeRD 1.0 and would need to be adapted to account for the changes in later versions of the specification, it serves as a great starting point since it extends the iText Core library in some useful ways.

As noted, the ZUGFeRD specification supports multiple profiles for the XML invoice data which are designed for specific applications. Built into pdfInvoice are two interfaces which implement the ZUGFeRD BASIC and COMFORT profiles, which retrieve invoice data from your database or CRM. There are also classes for data handling and validation, meaning you can construct the XML e-invoice data, and validate it before attaching it to the PDF/A-3 document with iText Core.

Final Thoughts

In this article, we’ve shown how iText Core can create e-invoices which conform to EN 16931 and the current ZUGFeRD/Factur-X standards, by correctly embedding the XML e-invoice data into compliant PDF/A-3 documents.

The ZUGFeRD/Factur-X e-invoice standard is a remarkably elegant solution for electronic invoicing, seamlessly integrating the structured XML data within a PDF/A-3 container. This hybrid approach ensures both human readability and machine-processable data, facilitating efficient and accurate invoice processing for businesses of any size.

As businesses continue to adopt electronic invoicing, the ZUGFeRD/Factur-X standard is a compelling way to both streamline operations and achieve compliance with regulatory requirements such as EN 16931. Utilizing powerful tools like iText (or the Apryse SDK) can help simplify this process, enabling businesses both large and small to reap the benefits of digital transformation in their invoicing workflows.

If after reading this you want to use iText to produce ZUGFeRD-compliant invoices in your business, we can help. Don’t hesitate to reach out if you need advice or assistance with your implementation.



Profile picture of Ian Morris

Ian Morris

Product Management

Ian joined iText in 2019 as Technical Writer. Originally from the UK, he now lives in Ghent, Belgium.

Contact

Still have questions? 

We're happy to answer your questions. Reach out to us and we'll get back to you shortly.

Contact us
Stay updated

Join 11,000+ subscribers and become an iText PDF expert by staying up to date with our new products, updates, tips, technical solutions and happenings.

Subscribe Now