--- status: Accepted date: 2026-07-05 applyTo: - "Fuchs/code/FuchsPdf.cs" - "Fuchs/Services/FuchsPdfService.cs" - "Fuchs/Services/InvoiceService.cs" - "Fuchs/Services/ReminderService.cs" - "eRechnungLib/**" supersededBy: "" --- # 0005 — PDF generation, rendering, and eRechnung output ## Context Fuchs produces letters, invoices, and reminders as PDFs. The layout is a faithful port of the legacy VB module `fuchs_fds_pdf.vb` (letterhead, DIN address window, admin block, four-block footer with page numbers, invoice item table, GiroCode). The port had silently drifted — wrong letterhead image filenames (`image1.png` instead of the shipped `image1.jpeg`, which `AddHeaderImage` skips via `File.Exists`), a too-small bottom margin, and a reworked footer/admin block — so generated PDFs (e.g. the `sprep` invoice preview) rendered broken. Separately, German B2B/B2G invoicing now requires **eRechnung** (structured electronic invoices). The company direction is that **all invoices are emitted as eRechnung**, not just human-readable PDFs. Rendering also depends on **Spire.PDF** (commercial, licensed) for PDF/A conversion and rasterising PDFs to preview images. ## Decision - **PDF layout stays a 1:1 port of the legacy `fuchs_fds_pdf.vb`.** `FuchsPdf` (MigraDoc/PdfSharp) is the single source of the visual layout. When changing the letter/invoice/reminder layout, compare against the legacy module and keep the letterhead assets (`Fuchs/Data/image1-3.jpeg`, `image4.png`, `overlay.png`), margins, sender line, label-over-value admin block, absolutely-positioned four-block footer, and `Seite X von Y` page numbers aligned with it. Reference the shipped asset filenames exactly — `AddHeaderImage` no-ops on a missing file, so a wrong extension silently drops a logo. - **Rendering pipeline:** `FuchsPdf.DocToPdfBytes` renders MigraDoc → PDF and post-processes to PDF/A; `DocToImageCollection` / `BytesToImageCollection` rasterise via Spire for the on-screen invoice preview (`sprep`/`sedit`). The OCORE `OCOREFontResolver` must be installed before any PdfSharp rendering. - **Spire license comes from a managed secret.** `FuchsPdfService` reads the license from configuration key `SpirePdf_License` (Key Vault secret `fuchs--SpirePdf-License`, registered in `ManagedSecretKeys`) and passes it to `FuchsPdf.SetLicense(key)`. An embedded fallback key keeps local/dev rendering working without Key Vault. - **eRechnung via `eRechnungLib`.** The `eRechnungLib` submodule is the single library for structured invoices. Invoices are to be produced as eRechnung: build an `eRechnungLib.Model.Invoice` from the Fuchs invoice data, then `EInvoice.CreateInvoice(model).ToZugferd(ZugferdProfile.EN16931, visualPdfBytes)` to embed the CII XML into the FuchsPdf-rendered visual PDF (ZUGFeRD/Factur-X hybrid PDF/A-3), or `ToXRechnung(...)` for pure UBL/CII XML. The visual PDF is the FuchsPdf output — the two layers stay consistent (same amounts/parties). Default `ConversionOptions` runs model + XSD validation; use `StrictValidation` when a malformed invoice must withhold output rather than ship with findings. ## Consequences - Layout edits must be validated against the legacy reference and the shipped `Data/` assets; do not invent new positions/sizes. The pipeline test `Fuchs.Tests/PdfPipelineTests.cs` exercises the full chain (PdfSharp visual PDF → Spire preview images → eRechnung hybrid/XML) and must stay green. - Do **not** upgrade Spire.PDF beyond 8.10.5 (see project libraries rule). The license must never be hard-coded in new code paths — read it from `SpirePdf_License`. - Wiring the app's invoice flow to emit eRechnung is the follow-up: map `FdsInvoiceData`/`InvoiceRegistration` → `eRechnungLib.Model.Invoice` (parties, lines, VAT breakdown, payment/IBAN, buyer reference, seller electronic address) and persist/deliver the ZUGFeRD PDF and/or XRechnung XML. - eRechnungLib depends only on open-source libraries (PDFsharp/MigraDoc; optional SaxonCS-HE for Schematron) — no new commercial dependency for the structured output itself. ## Alternatives considered - **Hand-rolling ZUGFeRD/XRechnung XML** in Fuchs: rejected — EN 16931 + CIUS validation, multiple profiles/syntaxes, and PDF/A-3 embedding are error-prone; a dedicated, validated library is safer. - **Rewriting the PDF layout from scratch** rather than porting the legacy module: rejected — the letterhead is a fixed corporate design; the legacy VB is the authoritative spec, so faithful porting avoids visual regressions. - **Bundling a Spire license file / hard-coding the key**: rejected in favor of the managed-secret path so the production key is centrally rotated and never committed, with the embedded key only as a dev fallback.