feat: add @node-zugferd/pdf plugin for programmatic PDF generation
Closes #119
What this PR does
Adds @node-zugferd/pdf — a new plugin package that turns invoice data into a finished e-invoice in a single call, with no server and no headless browser:
const invoicer = zugferd({
profile: EN16931,
plugins: [pdf({ template: defaultTemplate() })],
});
// renders the PDF, generates the XML, embeds it → PDF/A-3b Uint8Array
const pdfA = await invoicer.createPdf(data, { metadata: { title: "Rechnung 471102" } });
Pipeline
data → template (pdfmake docDefinition) → pdfmake printer (fonts embedded)
→ document.create(data).embedInPdf(buffer) → PDF/A-3b with Factur-X XML
Only public core APIs are used (ZugferdPlugin, document.create, embedInPdf) — no core changes.
Built-in German template
defaultTemplate() renders a classic German business invoice (DIN 5008 inspired) with all content required by § 14 Abs. 4 UStG: sender line, address field, document meta block (invoice number, dates, customer number, invoicing period, VAT ID / tax number), line item table with automatic page breaks, totals with per-rate VAT breakdown (incl. exemption reasons), notes / payment terms, and per-page footer columns.
It renders exclusively from the same data the XML is generated from, so the visible PDF cannot contradict the embedded XML. Customization tiers: options (logo, accent color, sender line, footer columns, label overrides — e.g. for English invoices) → fully custom template (any function returning a pdfmake document definition, with locale-aware formatters provided).
Since PDF/A forbids non-embedded fonts, the standard-14-fonts path is never used; the Roboto family shipped inside pdfmake is embedded by default, custom TTFs are supported via the fonts option.
Testing
- 24 Vitest tests (files next to sources): formatter units, default-template content assertions against the shared profile fixtures, and full-pipeline integration tests (embedded
factur-x.xmlbyte-equal totoXML(), EN 16931 profile, multi-page tables, custom templates). - veraPDF conformance: output passes official PDF/A-3b validation (veraPDF 1.30.2). The conformance test auto-skips when no veraPDF binary is found (
VERAPDFenv or PATH), mirroring how the core treats its optional XSD validator — CI without veraPDF is unaffected. - XSD validation was active (strict mode) in manual end-to-end runs with real invoice data.
typecheck,biome checkandbuild(incl. DTS) are clean; the docs site builds successfully.
Docs
- New page
docs/content/docs/plugins/pdf.mdx(installation, default template, custom fonts, custom templates, options reference), registered in the plugins sidebar. - Package README.
Open points
Happy to adjust naming (package name, createPdf handler), the bundled font, or anything else — see the open questions in #119.
🤖 Generated with Claude Code