ZUGFeRD / Factur-X e-Invoice
Create a ZUGFeRD / Factur-X electronic invoice -- a hybrid PDF that humans can read and machines can parse automatically.
What is ZUGFeRD / Factur-X?
ZUGFeRD (Germany) and Factur-X (France / EU) are the same standard under two names. A compliant invoice contains:
- Visual PDF -- a human-readable invoice rendered as PDF/A-3 (or PDF/A-4f)
- Embedded XML -- machine-readable invoice data in the UN/CEFACT Cross Industry Invoice (CII) format, attached inside the PDF
This enables fully automated processing while keeping a familiar visual document for accountants and auditors.
Profiles
| Profile | Typical Use |
|---|---|
| Minimum | Invoice number, date, totals, tax |
| Basic WL | + Line items, payment terms |
| Basic | + Detailed line items |
| EN 16931 (Comfort) | EU public procurement compliance |
| Extended | Complex multi-tax invoices |
| XRechnung | German public-sector invoicing |
PDF/A Requirement
ZUGFeRD requires the host PDF to be PDF/A-3 or PDF/A-4f (ISO 19005). The "f" variant allows embedded files. TCPDF-Next handles the conformance metadata, output intent, and file attachment flags automatically.
Full Code Example
php
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use TcpdfNext\Document;
use TcpdfNext\Enums\PdfALevel;
use TcpdfNext\Archive\OutputIntent;
use TcpdfNext\Archive\EmbeddedFile;
use TcpdfNext\Archive\AFRelationship;
// ── 1. Create a PDF/A-4f document ──────────────────────────────────────
$pdf = Document::create()
->setPdfALevel(PdfALevel::A4F)
->setOutputIntent(OutputIntent::sRGB())
->setTitle('Invoice INV-2026-0042')
->setAuthor('Acme GmbH')
->setSubject('Invoice for Order PO-2026-0815');
// ── 2. Render the visual invoice ───────────────────────────────────────
$invoiceHtml = <<<'HTML'
<h1 style="color:#1a2634;">INVOICE</h1>
<table style="width:100%; margin-bottom:20px;">
<tr>
<td><strong>Acme GmbH</strong><br>Friedrichstr. 123<br>10117 Berlin, DE</td>
<td style="text-align:right;"><strong>INV-2026-0042</strong><br>Date: 2026-02-15<br>Due: 2026-03-17</td>
</tr>
</table>
<table border="1" cellpadding="6" style="width:100%; border-collapse:collapse;">
<thead>
<tr style="background:#1a2634; color:#fff;">
<th>#</th><th>Description</th><th style="text-align:right;">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Professional Services -- February 2026</td>
<td style="text-align:right;">EUR 1,000.00</td>
</tr>
</tbody>
<tfoot>
<tr><td colspan="2" style="text-align:right;">Net</td><td style="text-align:right;">EUR 1,000.00</td></tr>
<tr><td colspan="2" style="text-align:right;">VAT 19%</td><td style="text-align:right;">EUR 190.00</td></tr>
<tr style="font-weight:bold;">
<td colspan="2" style="text-align:right;">Total</td>
<td style="text-align:right;">EUR 1,190.00</td>
</tr>
</tfoot>
</table>
HTML;
$pdf->addPage()
->setFont('helvetica', size: 10)
->writeHtml($invoiceHtml);
// ── 3. Build the Factur-X XML (EN 16931 profile) ───────────────────────
$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:factur-x.eu:1p0:en16931</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>INV-2026-0042</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20260215</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>Acme GmbH</ram:Name>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">DE123456789</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>Customer Corp</ram:Name>
</ram:BuyerTradeParty>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>1000.00</ram:LineTotalAmount>
<ram:TaxBasisTotalAmount>1000.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="EUR">190.00</ram:TaxTotalAmount>
<ram:GrandTotalAmount>1190.00</ram:GrandTotalAmount>
<ram:DuePayableAmount>1190.00</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>
XML;
// ── 4. Embed the XML as an associated file ─────────────────────────────
$pdf->addEmbeddedFile(
EmbeddedFile::create()
->setFilename('factur-x.xml')
->setMimeType('text/xml')
->setContent($xml)
->setRelationship(AFRelationship::Alternative)
->setDescription('Factur-X EN 16931 invoice data')
);
// ── 5. Set ZUGFeRD XMP metadata ────────────────────────────────────────
$pdf->getMetadata()
->setXmpProperty('fx:DocumentType', 'INVOICE')
->setXmpProperty('fx:DocumentFileName', 'factur-x.xml')
->setXmpProperty('fx:Version', '1.0')
->setXmpProperty('fx:ConformanceLevel', 'EN 16931');
// ── 6. Save ────────────────────────────────────────────────────────────
$pdf->save(__DIR__ . '/INV-2026-0042.pdf');
echo 'ZUGFeRD invoice created.' . PHP_EOL;Validation
bash
# PDF/A conformance
verapdf --flavour 4f INV-2026-0042.pdf
# Factur-X XML schema
java -jar Mustang-CLI.jar --action validate --source INV-2026-0042.pdfTIP
Mustangproject is a free open-source validator for ZUGFeRD/Factur-X invoices.
Workflow Diagram
ERP / billing data
|
v
TCPDF-Next generate
|
v
PDF/A-4f + embedded factur-x.xml
|
v
(optional) PAdES digital signature
|
+-------+-------+-------+
| | | |
Email Portal PEPPOL ArchiveFurther Reading
- PDF/A-4 Archival -- ISO 19005-4 compliance details
- PAdES B-LTA Workflow -- digitally sign invoices