ZUGFeRD and Factur-X are hybrid e-invoice formats. The PDF is human-readable and machine-readable at the same time. The XML payload is embedded as an attachment.
use pdfluent::{PdfDocument, EInvoiceProfile, ZugferdAttachment};
fn main() -> pdfluent::Result<()> {
let mut doc = PdfDocument::open("invoice-template.pdf")?;
let xml = std::fs::read_to_string("invoice.xml")?;
doc.attach_einvoice(ZugferdAttachment {
xml,
profile: EInvoiceProfile::EN16931,
filename: "factur-x.xml".to_string(),
})?;
doc.convert_to_pdfa3()?;
doc.save("invoice-einvoice.pdf")?;
Ok(())
}E-invoice support requires the pdfa feature for PDF/A-3 conformance and the einvoice feature for ZUGFeRD/Factur-X metadata.
# Cargo.toml
[dependencies]
pdfluent = { version = "0.9", features = ["pdfa", "einvoice"] }Start from a rendered invoice PDF. This is the human-readable part. PDFluent will attach the XML and set the required PDF/A-3 metadata.
use pdfluent::PdfDocument;
// Load an existing rendered invoice
let mut doc = PdfDocument::open("invoice-template.pdf")?;
// Or build one from scratch
// let mut doc = PdfDocument::new();
// ... add content, text, tables ...The XML must conform to the UN/CEFACT CII schema. EN16931 is the profile required for public sector e-invoicing in the EU. MINIMUM and BASIC_WL are simpler profiles.
let xml = r#"<?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:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:en16931</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<!-- ... invoice content ... -->
</rsm:CrossIndustryInvoice>"#.to_string();attach_einvoice() embeds the XML as a PDF file attachment with the correct MIME type, AFRelationship, and XMP metadata required by the Factur-X spec.
use pdfluent::{EInvoiceProfile, ZugferdAttachment};
doc.attach_einvoice(ZugferdAttachment {
xml,
profile: EInvoiceProfile::EN16931,
filename: "factur-x.xml".to_string(),
})?;ZUGFeRD and Factur-X require PDF/A-3b conformance. convert_to_pdfa3() adds the required XMP metadata and output intent.
doc.convert_to_pdfa3()?;
doc.save("invoice-einvoice.pdf")?;
println!("E-invoice PDF saved.");No JVM, no runtime, no DLL dependencies. Ships as a single native binary or WASM module.
Rust's ownership model prevents buffer overflows and use-after-free. No segfaults in PDF parsing.
Same code runs server-side, in Docker, on AWS Lambda, on Cloudflare Workers, or in the browser via WASM.