zugferd-pdf
A fast, lightweight, and commercially friendly (Non-AGPL) Python library and CLI tool to generate, package, and validate ZUGFeRD 2.x / Factur-X hybrid electronic invoices conforming to PDF/A-3U (ISO 19005-3) and EN 16931.
Key Features
- Strict PDF/A-3U Conformance (ISO 19005-3): Full Unicode mapping (
ToUnicode) for embedded fonts. - Standard OutputIntent: Valid embedded sRGB ICC profile (
Identifier: sRGB,Info: sRGB IEC61966-2.1). - UN/CEFACT CII (EN 16931): Generates valid
factur-x.xmladhering to European e-invoicing standards. - Multi-Attachment Safety: Preserves existing attachments and allows adding extra
.xml/.pdfdocuments with proper/AFRelationshipwithout corruption. - Built-in Validator: Thorough local compliance checks for PDF/A-3 container and EN 16931 Schematron business rules (e.g. BR-DEC-23).
- 100% Non-AGPL / Commercially Friendly: Replaces Ghostscript and PyMuPDF using permissive open-source libraries (BSD-3-Clause / MPL-2.0 / MIT).
- CLI & Python API: Use as a command-line tool or as an importable library.
Installation
pip install zugferd-pdf
Quick Usage
1. Package an Existing PDF + XML into PDF/A-3U
from zugferd import package_zugferd_pdfa3u
# Convert an existing PDF + invoice XML into a compliant PDF/A-3U document
pdfa_bytes = package_zugferd_pdfa3u(
pdf_input="invoice.pdf", # File path, bytes, or BytesIO
xml_data="factur-x.xml", # File path, string, or bytes
output_target="zugferd_invoice.pdf", # Optional: output file path
conformance_level="EN 16931", # 'EN 16931', 'BASIC', 'EXTENDED', etc.
# Optional: Safely embed any extra attachments alongside factur-x.xml
additional_attachments=[
{
"filename": "timesheet.xml",
"data": b"<timesheet>...</timesheet>",
"relationship": "/Supplement",
}
],
)
2. End-to-End Invoice Generation in Python
from datetime import date, timedelta
from decimal import Decimal
from zugferd import (
Invoice,
LineItem,
PaymentMeans,
PostalAddress,
TradeParty,
create_zugferd_invoice,
)
# 1. Define Parties
seller = TradeParty(
name="TechSolutions GmbH",
address=PostalAddress(
postcode="10115",
line_one="Friedrichstraße 123",
city_name="Berlin",
country_id="DE",
),
vat_id="DE123456789",
contact_name="Max Mustermann",
contact_email="billing@techsolutions.example.com",
)
buyer = TradeParty(
name="Global Cloud Logistics AG",
address=PostalAddress(
postcode="80331",
line_one="Marienplatz 1",
city_name="München",
country_id="DE",
),
vat_id="DE987654321",
)
# 2. Define Line Items
items = [
LineItem(
line_id="1",
name="Cloud Architecture Consulting",
quantity=Decimal("10.00"),
unit_code="HUR", # Hours
net_unit_price=Decimal("120.00"),
vat_rate=Decimal("19.00"),
vat_category="S",
description="Design of secure cloud migration",
),
]
# 3. Define Payment Info
payment = PaymentMeans(
type_code="58", # SEPA Credit Transfer
iban="DE89370400440532013000",
bic="DBBADEFFXXX",
)
# 4. Create Invoice Object
invoice = Invoice(
invoice_number="INV-2026-0001",
issue_date=date.today(),
due_date=date.today() + timedelta(days=30),
currency="EUR",
seller=seller,
buyer=buyer,
items=items,
payment_means=payment,
buyer_reference="PO-2026-0001",
conformance_level="EN 16931",
)
# 5. Generate Compliant PDF/A-3U Document
pdf_bytes = create_zugferd_invoice(invoice, output_path="zugferd_invoice.pdf")
print("Invoice generated successfully.")
3. Validate Any PDF in Python
from zugferd import validate_zugferd_pdf
# Returns a ValidationReport object
report = validate_zugferd_pdf("invoice.pdf", print_report=True)
if report.is_valid:
print("PDF is compliant.")
else:
print(f"Validation failed with {len(report.errors)} error(s):")
for err in report.errors:
print(f" - {err}")
4. Extract XML from an Existing ZUGFeRD PDF
from zugferd import extract_zugferd_xml
xml_bytes = extract_zugferd_xml("zugferd_invoice.pdf", target_file="extracted.xml")
print(xml_bytes.decode("utf-8"))
Command-Line Interface (CLI)
The package installs a global CLI tool named zugferd:
# Validate any PDF for ZUGFeRD & PDF/A-3 compliance with detailed diagnostic summary
zugferd validate invoice.pdf
# Generate a sample invoice PDF/A-3U
zugferd create invoice.pdf
# Package existing PDF + XML into PDF/A-3U
zugferd package my_invoice.pdf factur-x.xml output_zugferd.pdf
# Package with extra attachments (.xml, .pdf, etc.)
zugferd package my_invoice.pdf factur-x.xml output_zugferd.pdf --attach extra_data.xml --attach timesheet.pdf
# Extract embedded factur-x.xml from any PDF
zugferd extract invoice.pdf -o extracted.xml
Validation
Upload your generated PDF to the official / popular German e-invoicing validator: https://e-rechnung-vorlage.de/xrechnung-zugferd-validator/
Verified against:
- VeraPDF: Strict PDF/A-3U (ISO 19005-3:2012) conformance.
- Factur-X / ZUGFeRD 2.x Schematron (
FACTUR-X_EN16931.xslt): Valid specification identifierurn:cen.eu:en16931:2017. - ColorSync / OutputIntent: Valid embedded sRGB profile.
- Associated Files (
/AF): Correct/Alternativeand/Supplementrelationships.
Publishing to PyPI
Option 1: Manual Upload with Twine
- Build distributions:
pip install build twine python -m build
- Verify package integrity:
twine check dist/*
- Upload to PyPI:
twine upload dist/*
Option 2: Automated via GitHub Actions (Trusted Publishing)
This repository includes .github/workflows/publish.yml. Connect your GitHub repository to PyPI via Trusted Publishing (OIDC) and create a new GitHub Release to automatically build and publish to PyPI.
License & AGPL-Free Guarantee
Licensed under the MIT License.
All internal dependencies (reportlab [BSD], pikepdf [MPL-2.0], pypdf [BSD-3-Clause], lxml [BSD]) are commercially friendly and non-copyleft. No AGPL software (Ghostscript, MuPDF) is used.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file zugferd_pdf-0.1.2.tar.gz.
File metadata
- Download URL: zugferd_pdf-0.1.2.tar.gz
- Upload date:
- Size: 24.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06203285d8de03fc2be912505bce6001f373581a7f859b63c0cd2abde8716ecc
|
|
| MD5 |
749e97cc19d593fc950f8bfdee17bee7
|
|
| BLAKE2b-256 |
a38a60e0fad9e12f8205f53fe128537b1fe96c0ae4c0e14b942b7850c789f976
|
Provenance
The following attestation bundles were made for zugferd_pdf-0.1.2.tar.gz:
Publisher:
publish.yml on raj-sh-git/zugferd-pdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zugferd_pdf-0.1.2.tar.gz -
Subject digest:
06203285d8de03fc2be912505bce6001f373581a7f859b63c0cd2abde8716ecc - Sigstore transparency entry: 2582862383
- Sigstore integration time:
-
Permalink:
raj-sh-git/zugferd-pdf@619e6cd6e427e1309026c9c066b6fb8dfc0848a7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/raj-sh-git
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@619e6cd6e427e1309026c9c066b6fb8dfc0848a7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file zugferd_pdf-0.1.2-py3-none-any.whl.
File metadata
- Download URL: zugferd_pdf-0.1.2-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c9362f72e95314cc4a44f523ffc4cf8410e3cd1e9365e63f77a4a0d4294909d
|
|
| MD5 |
fbeb0cb13fc9324c79ef42ec75eaf154
|
|
| BLAKE2b-256 |
f63baf0b41d293aebc288a58f37879f649d632edde40bfbad387a54890375563
|
Provenance
The following attestation bundles were made for zugferd_pdf-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on raj-sh-git/zugferd-pdf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zugferd_pdf-0.1.2-py3-none-any.whl -
Subject digest:
6c9362f72e95314cc4a44f523ffc4cf8410e3cd1e9365e63f77a4a0d4294909d - Sigstore transparency entry: 2582862389
- Sigstore integration time:
-
Permalink:
raj-sh-git/zugferd-pdf@619e6cd6e427e1309026c9c066b6fb8dfc0848a7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/raj-sh-git
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@619e6cd6e427e1309026c9c066b6fb8dfc0848a7 -
Trigger Event:
release
-
Statement type: