> For the complete documentation index, see [llms.txt](https://docs.pd4ml.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.pd4ml.com/features/zugferd-and-factur-x-pdf-invoices.md).

# ZUGFeRD and Factur-X PDF Invoices

ZUGFeRD and Factur-X are hybrid electronic-invoice standards, jointly maintained by the German and French tax authorities' respective standardization bodies, that resolve a long-standing tension between two audiences for the same document: a human being who wants to open an invoice and read it, and an accounting system that wants to parse it without OCR or manual data entry. Both standards solve this by embedding a structured XML representation of the invoice's line items, totals, and parties directly inside an otherwise perfectly ordinary, human-readable PDF/A-3 file -- a single artifact serves both purposes, rather than sending a PDF and an XML file as two separate attachments that can drift out of sync with each other. PD4ML generates ZUGFeRD- and Factur-X-conformant PDFs natively, driven from the same HTML-to-PDF conversion used for everything else, with the invoice XML supplied as an attachment declared directly in the source HTML.

## Enabling PDF/A-3b Output

Both standards require the container PDF to conform to PDF/A-3b specifically -- the PDF/A-3 subset that permits arbitrary embedded files, which is what makes carrying the invoice XML inside the PDF legal under the PDF/A rules in the first place. This is requested through the same `writePDF(OutputStream, PdfSpec)` call used for any other conformance target (see the Programmer's Manual's [PDF/A, PDF/UA, and e-invoicing formats](/pd4ml-manual.md) section), either by combining the general-purpose specs explicitly or via two convenience constants that already bundle the correct combination for each standard:

```java
pd4ml.writePDF(os, PdfSpec.PDF_1_7.combine(PdfSpec.PDFA_3B));

// equivalent, pre-combined convenience constants:
pd4ml.writePDF(os, PdfSpec.ZUGFeRD);
pd4ml.writePDF(os, PdfSpec.FacturX);
```

## Embedding the Invoice XML

The invoice XML itself is declared with PD4ML's proprietary `<pd4ml:attachment>` tag, placed anywhere in the source HTML body. Its `type` attribute names the target standard and, optionally, a specific conformance profile suffix; `description` sets the attachment's human-readable label; and `name` overrides the filename the XML is embedded under, which matters because both standards mandate a specific, version-dependent filename for the reader to recognize the attachment as the invoice payload rather than an arbitrary file.

```html
<pd4ml:attachment type="ZUGFeRD-extended" description="ZUGFeRD data" name="factur-x.xml">

<?xml version="1.0" encoding="utf-8"?>
<rsm:CrossIndustryInvoice
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
...
...
</rsm:CrossIndustryInvoice>

</pd4ml:attachment>
```

(The XML body is abbreviated with `...` above, exactly as in the source material -- a `CrossIndustryInvoice` document runs to several hundred lines even for a simple invoice. The [Application Example](#application-example) below shows how PD4ML's own placeholder substitution keeps a real one in sync with the visible HTML content, rather than requiring it to be assembled by hand.)

### Supported types and conformance profiles

The `type` attribute accepts `ZUGFeRD` or `Factur-X`, each optionally suffixed with one of five conformance-profile modifiers: `-minimum`, `-basicwl`, `-basic`, `-comfort`, or `-extended` (as used above: `ZUGFeRD-extended`). These profile names come from the ZUGFeRD/Factur-X specification itself rather than from PD4ML -- in increasing order of how much structured data the XML carries, from `Minimum` (little more than the amount due, intended for invoices that don't need full line-item detail) up through `Extended` (full line-item and tax breakdown, suited to complex B2B and cross-border invoicing); choosing the wrong profile for what your XML actually contains is a common source of downstream validation failures (see [Validating the Output](#validating-the-output)).

### Required filenames by version

The filename requirement differs across versions of the standards, which is precisely what the `name` attribute exists to control when the default doesn't match:

| Standard / version       | Required attachment filename |
| ------------------------ | ---------------------------- |
| ZUGFeRD 1.0              | `ZUGFeRD-invoice.xml`        |
| ZUGFeRD 2.0              | `zugferd-invoice.xml`        |
| ZUGFeRD 2.1 and Factur-X | `factur-x.xml`               |

## Loading XML From an External File

Rather than inlining the XML in the HTML source, `<pd4ml:attachment>` can instead reference an external file via its `src` attribute -- useful when the invoice XML is already generated by a separate billing system and simply needs to be attached as-is:

```html
<pd4ml:attachment type="Factur-X" description="Invoice data" src="invoices/factur-x.xml"/>
```

## Validating the Output

PD4ML itself guarantees the *container* is valid PDF/A-3b -- correct structure, embedded fonts, the right `/AFRelationship` and `/Subtype` metadata on the attached file, and so on. It does not, and cannot, validate that the *content* of the embedded XML itself is a conformant ZUGFeRD or Factur-X invoice, since that's ultimately a question about business data your application supplied, not something PD4ML generated. That validation is the job of a dedicated third-party tool. Adobe Acrobat's own Preflight tool can check basic PDF/A-3 conformance, but its support for validating the ZUGFeRD/Factur-X XML profiles specifically is limited, particularly for older standard versions -- it's not a substitute for a purpose-built validator.

## Validating with the Mustang Project

[Mustang](https://www.mustangproject.org/) is an open-source Java library purpose-built for ZUGFeRD/Factur-X validation, and is a natural fit for a PD4ML-based pipeline since it's invoked with a few lines of Java, right alongside the conversion code that produced the PDF in the first place. It reports specific, actionable diagnostics rather than a bare pass/fail:

```java
System.out.print("zugferd validation...");
ZUGFeRDValidator zfv = new ZUGFeRDValidator();
String report = zfv.validate(pdfPath);
if (!zfv.wasCompletelyValid()) {
    System.out.println(report);
}
System.out.println(" done.");
```

Mustang is published to Maven Central; the `shaded` classifier pulls in a self-contained jar with all of its own dependencies bundled, which is the simplest way to add it to a project that doesn't already manage those dependencies itself:

```xml
<dependency>
    <groupId>org.mustangproject</groupId>
    <artifactId>validator</artifactId>
    <version>2.22.0</version>
    <classifier>shaded</classifier>
</dependency>
```

## Application Example

A realistic invoice needs the same data to appear twice: once laid out for a human reader in the visible HTML/CSS, and once structured as XML for the embedded attachment. Re-typing every amount, date, and address in both places is exactly the kind of duplication that causes the two halves to quietly drift apart. PD4ML's `$[variable]`-style dynamic-data placeholders (see the Programmer's Manual's section on [headers, footers, watermarks, and backgrounds](/pd4ml-manual.md)) solve this cleanly: populate one `HashMap`, call `setDynamicData()` once, and every `$[...]` placeholder -- in the visible table rows and inside the `<pd4ml:attachment>` block alike -- is substituted from that same single source of truth.

```html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ZUGFeRD Invoice</title>
</head>
<body>
	<tr class="style1">
		<td colspan="5">Zwischensumme</td>
		<td>$[curr] $[totalamt]</td>
	</tr>
	<tr>
		<td colspan="5">zzgl. MwSt. ($[vatprc]%)</td>
		<td>$[curr] $[vat]</td>
	</tr>
	<tr class="style1">
		<td colspan="5"><strong>Gesamtsumme</strong></td>
		<td><strong>$[curr] $[grandtotal]</strong></td>
	</tr>
<pd4ml:attachment type="ZUGFeRD" description="ZUGFeRD data">
	<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
		<ram:LineTotalAmount>$[totalamt]</ram:LineTotalAmount>
		<ram:TaxBasisTotalAmount>$[totalamt]</ram:TaxBasisTotalAmount>
		<ram:TaxTotalAmount currencyID="$[curr]">$[vat]</ram:TaxTotalAmount>
		<ram:GrandTotalAmount>$[grandtotal]</ram:GrandTotalAmount>
		<ram:DuePayableAmount>$[grandtotal]</ram:DuePayableAmount>
	</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</pd4ml:attachment>
</body></html>
```

This excerpt shows the pattern rather than a complete invoice layout -- the full source, including the invoice header, seller/buyer address blocks, and the complete `CrossIndustryInvoice` XML, is available as a [complete HTML template](https://pd4ml.com/i/zugferd.htm).

The data map driving both halves of the example above:

```java
HashMap<String, String> map = new HashMap<String, String>();
map.put("invid", "I202602101524");
map.put("product", "Product");
map.put("nettoamt", "10.00");
map.put("totalamt", "10.00");
map.put("vat", "1.90");
map.put("vatprc", "19");
map.put("grandtotal", "11.90");
map.put("curr", "EUR");
map.put("qty", "1");
map.put("seller", "Petra Mustefrau");
map.put("sellerstr", "Augsburger Str. 7");
map.put("sellercity", "Buchloe");
map.put("sellerzip", "86807");
map.put("sellercountry", "DE");
map.put("sellercountryname", "Deutschland");
map.put("sellervatid", "DE129524000");
map.put("selleremail", "mm@firma.net");
map.put("sellerphone", "+49 8241 9690-0");
map.put("customerid", "12345");
map.put("buyer", "Kunde");
map.put("buyerstr", "Heideweg 9");
map.put("buyercity", "Wolfratshausen");
map.put("buyerzip", "82515");
map.put("buyercountry", "DE");
map.put("buyercountryname", "Deutschland");
map.put("buyervatid", "DE129524000");
map.put("buyeremail", "kunde@gmx.de");
map.put("buyerphone", "+49 8171 42110");
map.put("buyercontact", "Max Mustermann");
map.put("invdatecode", "20260211");
map.put("duedatecode", "20260225");
map.put("invdate", "11.02.2026");
map.put("duedate", "25.02.2026");

pd4ml.setDynamicData(map);
pd4ml.readHTML(new ByteArrayInputStream(s.getBytes("UTF-8")), base, "UTF-8");
```

Running the template above through this data map produces this [valid ZUGFeRD PDF](https://pd4ml.com/i/zugferd.pdf) as a reference for what a conformant result looks like.

## See also

* [PD4ML Programmer's Manual](/pd4ml-manual.md) -- §11.2 covers PDF/A, PDF/UA, and `PdfSpec` in general; §8 covers the `$[variable]` dynamic-data mechanism used throughout the invoice example above.
* [Usage Examples](/usage-examples.md) -- the Substitute Placeholders and Add Attachment entries cover the two underlying mechanisms (dynamic data and `<pd4ml:attachment>`) independently of ZUGFeRD/Factur-X.
* [Mustang project](https://www.mustangproject.org/) -- the third-party validator referenced above.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.pd4ml.com/features/zugferd-and-factur-x-pdf-invoices.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
