How-to guides/E-Invoicing

Create a ZUGFeRD or Factur-X e-invoice PDF in Rust

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.

rust
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(())
}
Install:cargo add pdfluentDownload SDK →

Step by step

1

Add PDFluent with the einvoice and pdfa features

E-invoice support requires the pdfa feature for PDF/A-3 conformance and the einvoice feature for ZUGFeRD/Factur-X metadata.

rust
# Cargo.toml
[dependencies]
pdfluent = { version = "0.9", features = ["pdfa", "einvoice"] }
2

Create or load the visual PDF

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.

rust
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 ...
3

Prepare the Factur-X XML payload

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.

rust
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();
4

Attach the XML to the PDF

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.

rust
use pdfluent::{EInvoiceProfile, ZugferdAttachment};

doc.attach_einvoice(ZugferdAttachment {
    xml,
    profile: EInvoiceProfile::EN16931,
    filename: "factur-x.xml".to_string(),
})?;
5

Convert to PDF/A-3 and save

ZUGFeRD and Factur-X require PDF/A-3b conformance. convert_to_pdfa3() adds the required XMP metadata and output intent.

rust
doc.convert_to_pdfa3()?;
doc.save("invoice-einvoice.pdf")?;
println!("E-invoice PDF saved.");

Notes and tips

  • PDF/A-3 requires all fonts to be embedded. convert_to_pdfa3() checks and embeds missing fonts automatically.
  • The XML attachment filename must be exactly factur-x.xml for Factur-X or ZUGFeRD-invoice.xml for ZUGFeRD. Some validators reject other filenames.
  • EN16931 is the baseline profile for mandatory EU e-invoicing (Directive 2014/55/EU). Check your national requirements for the correct profile.
  • Validate the output with the Mustang library (Java) or an online Factur-X validator before sending to customers.

Why PDFluent for this

Pure Rust

No JVM, no runtime, no DLL dependencies. Ships as a single native binary or WASM module.

Memory safe

Rust's ownership model prevents buffer overflows and use-after-free. No segfaults in PDF parsing.

Runs anywhere

Same code runs server-side, in Docker, on AWS Lambda, on Cloudflare Workers, or in the browser via WASM.

Frequently asked questions