Blog

PDF/A en Factur-X / Peppol uitgelegd voor engineers (zonder juridisch jargon)

Wat PDF/A-profielen werkelijk beperken, waarom Peppol BIS 3 verplicht wordt in Nederland in 2026, en de kleinste praktische pipeline om compliant te worden vanaf een JSON-renderer.

Als je engineer bent en net is verteld «de facturen moeten PDF/A-3 met Factur-X zijn voor het volgende kwartaal», en je enige context is dat iemand bij legal die woorden uitsprak, is deze post voor jou.

We snijden door de standaardisatie-documententoon en leggen uit wat deze profielen werkelijk beperken, waarom regeringen ze begonnen verplicht te stellen, en de kleinste praktische pipeline om een compliant PDF te emitteren vanaf een gestructureerde-data-renderer.

PDF/A in twee paragrafen

PDF is een flexibel formaat. Te flexibel — dezelfde PDF-spec laat je JavaScript embedden, linken naar externe bronnen die over 50 jaar misschien niet bestaan, content versleutelen met omkeerbare cryptografie, externe fonts referencen, en honderd andere dingen die een document niet-zelfstandig maken.

PDF/A («A» voor Archival) is een profiel van PDF dat de delen verbiedt die zouden voorkomen dat het document identiek rendert over 50 jaar. De high-level regels:

  • Alle fonts moeten embedded zijn.
  • Geen JavaScript, geen externe links, geen audio/video.
  • Geen versleuteling.
  • Alle transparency moet flattened zijn of ondersteund door de profielversie.
  • Kleuren moeten apparaat-onafhankelijk zijn (ICC-profiel vereist).
  • Alle content moet in het bestand zijn — geen referenties die afhangen van het netwerk.

Er zijn verschillende versies, elk met meer tolerantie voor nieuwere features:

ProfielJaarWat het toevoegt
PDF/A-1b2005Originele baseline — strengst
PDF/A-2b2011Sta JPEG2000, transparency, layers toe
PDF/A-3b2012Sta willekeurige bestandsbijlagen toe (de basis van Factur-X)
PDF/A-42020ISO 32000-2 (PDF 2.0) basis, vereenvoudigde conformiteitsniveaus

Het «b»-suffix betekent «basic» conformiteit (visuele trouw). Er zijn ook «u» (unicode-mapped) en «a» (accessibility-tagged) varianten — voor de meeste invoice/bon-workflows, «b» is wat je wilt, omdat fiscale archivering om visuele reproduceerbaarheid geeft, niet om screen-reader semantiek.

Praktische conclusie: als je renderer zegt PDF/A-3b te ondersteunen, zou het een enkele config-flag moeten zijn ({ profile: "PDF/A-3b" } of equivalent). Als je een tweede tool (Ghostscript, qpdf, Acrobat) moet draaien om daarna te converteren, dat is een workflow-gat om in je ops te factoreren.

Waarom PDF/A-3 specifiek belangrijk is: het is de drager van e-facturen

PDF/A-3 voegde één capaciteit toe die wereld-veranderend bleek: willekeurige bestandsbijlagen binnen het PDF.

Klinkt saai. Is het niet. Het is de hele technische basis voor de e-factuur-mandaten die nu door Europa rollen.

De architectuur: een enkel PDF-bestand dat zowel

  1. Een mens-leesbare factuur (visuele layout, totalen, branding) — het deel dat de mens leest.
  2. Een machine-leesbare XML-factuur — het deel dat de software van de belastingdienst parset.

Beide binnen één bestand, beide dezelfde factuur representend, en de PDF/A-3 wrapper garandeert dat het bestand decennia lang parseable blijft.

De twee belangrijkste XML-formaten:

  • Factur-X (Frankrijk) — XML-profiel gebaseerd op UN/CEFACT Cross Industry Invoice
  • ZUGFeRD (Duitsland) — substantieel identiek aan Factur-X (de twee standaarden fuseerden technisch in 2018)
  • EN 16931 — de Europese norm waaraan beide implementaties voldoen
  • Peppol BIS 3 (Nederland + EU) — Peppol-bijbehorende UBL XML-profiel; ook gebaseerd op EN 16931

Voor de meeste workflows zijn «Factur-X» en «ZUGFeRD» uitwisselbare termen — ze delen een schema, delen het embedding-mechanisme, en een enkel PDF dat compliant is met één is meestal compliant met de ander.

Wat verplicht is, waar, wanneer

Een niet-uitputtende snapshot voor engineers die Q2/Q3 2026 rollouts plannen:

LandStatusVereist formaat
NederlandB2G verplicht via Peppol; B2B-uitbreiding 2026Peppol BIS 3 (UBL)
DuitslandB2B verplicht voor ontvangst vanaf 2025-01-01; uitgifte vanaf 2027EN 16931 (ZUGFeRD / Factur-X / XRechnung)
FrankrijkUitgifte verplicht voor grote bedrijven 2026-09; KMO 2027-09Factur-X via Chorus Pro
ItaliëB2B verplicht sinds 2019FatturaPA via SDI
PolenVerplicht sinds 2024-07KSeF
SpanjeVerplicht vanaf 2026 (B2B)Facturae via FACe

Het patroon: elke EU-lidstaat implementeert een variant van EN 16931-compliant e-facturatie op een 2024–2027 tijdlijn. Als je klanten in een van deze markten opereren, zal je PDF-generator moeten emitten met aangehechte XML naast de visuele factuur.

Voor Nederlandse teams specifiek: Peppol BIS 3 wordt beheerd via het Peppol-netwerk (gefaciliteerd door OpenPeppol), met Logius als de Nederlandse autoriteit voor B2G. Voor B2B-uitbreiding vanaf 2026 zal elk Nederlands bedrijf in staat moeten zijn om gestructureerde e-facturen te ontvangen en te verwerken — niet alleen PDFs als afbeeldingen, maar PDF/A-3-bestanden met embedded UBL XML, geleverd via een Peppol Access Point.

De kleinste praktische pipeline

Vergeet wat de standaardisatie-documenten voorschrijven. Hier is de engineer-view:

   ┌─────────────────────┐
   │  Je factuur-data    │  (al een JSON-object ergens)
   └─────────┬───────────┘


   ┌─────────────────────┐
   │ Bouw EN 16931 XML   │  (deterministische mapping; geteste libs bestaan)
   │   (of UBL Peppol)   │
   └─────────┬───────────┘


   ┌─────────────────────┐
   │ Render PDF/A-3b +   │
   │ hecht XML aan       │  (enkele API-call naar gPdf — of twee stappen elders)
   └─────────┬───────────┘


   ┌─────────────────────┐
   │  Lever aan          │
   │  Peppol AP / Logius │
   │  / Chorus Pro / etc │
   └─────────────────────┘

De twee niet-triviale stappen:

Stap 1: bouw de XML

Dit is irritant maar mechanisch. Je mapt je factuur-data (regels, taxes, totalen, partijen) naar EN 16931 XML-veldnamen (of UBL voor Peppol). Verschillende Java/Node/Python libraries doen dit voor je — zoek naar «factur-x library» of «peppol library» in jouw taal. Schrijf het niet vanaf nul tenzij je echt geniet van XML-schema-specs.

Stap 2: render PDF/A-3 en hecht de XML aan

Hier maakt de keuze van renderer uit.

Zonder ingebouwde ondersteuning: je rendert een gewone PDF, dan post-process je met een tool die converteert naar PDF/A-3 en de XML als embedded bestand aanhecht. Veelgebruikte stacks: Ghostscript + qpdf, of een betaalde tool zoals Aspose. Twee extra stappen, twee extra failure points, en je moet ervoor zorgen dat de post-processing de visuele layout niet verschuift.

Met ingebouwde ondersteuning (gPdf’s aanpak): één call.

curl -X POST https://gpdf.com/api/v1/e-invoice/render \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "document": {
      "pages": [{ "size": "a4", "elements": [...] }]
    },
    "einvoice": {
      "format": "factur-x",
      "profile": "BASIC",
      "xml": "<rsm:CrossIndustryInvoice>...</rsm:CrossIndustryInvoice>"
    }
  }' \
  --output factuur-met-factur-x.pdf

Dat is de hele pipeline. De renderer emit PDF/A-3b, hecht je XML als factur-x.xml aan (of zugferd-invoice.xml, beide herkend door elke consument) en geeft de bytes terug.

Veelvoorkomende valkuilen

Een paar dingen die mensen op de harde manier leren:

«PDF/A» en «met PDF/A-conforme fonts» zijn niet hetzelfde

Een PDF/A-3-bestand vereist dat alle fonts embedded zijn met volledige glyphdekking van gebruikte tekens. Als je factuur een buitenlandse klantnaam heeft en de renderer terugvalt op een font dat niet volledig embeddable is, zullen validatie-tools het afwijzen. Controleer of je renderer CJK-fonts embed in PDF/A-modus — veel doen dit niet standaard.

Visueel + XML moeten overeenkomen

De XML-factuur en de visuele factuur worden geacht dezelfde factuur te representen. Belasting-auditors zullen ze diff-en. Als je code XML emit met total: 119,00 en de visuele PDF toont Totaal: 120,00 (vanwege een afrondingsbug of een verouderd template), heb je een fiscale discrepantie in het dossier. Genereer beide vanaf dezelfde source-of-truth, idealiter in hetzelfde codepad.

«Profile»-niveaus in EN 16931

Factur-X heeft profielen: MINIMUM, BASIC, EN 16931, EXTENDED. Ze verschillen in hoeveel data in de XML zit. Gebruik BASIC tenzij je klant specifiek meer vereist — het dekt belastingcodes, lijnitems, partijen, totalen, wat genoeg is voor ~95 % van B2B-facturatie.

Validatie vóór indiening

Valideer altijd het gegenereerde PDF tegen een PDF/A-validator (veraPDF is de open-source standaard) en valideer de XML tegen het EN 16931 schema vóór je naar de belastingdienst stuurt. Mislukte indieningen bij Peppol / Chorus Pro / SDI tellen tegen je betrouwbaarheidsmetrics bij de regulator.

TL;DR

PDF/A is een zelfstandig-document profiel. PDF/A-3 laat je bestanden aanhechten. Factur-X / ZUGFeRD is «een EN 16931 XML aangehecht binnen een PDF/A-3». E-factuur-mandaten in de EU maken deze combinatie het de facto B2B-factuurformaat van 2025–2027.

Als je renderer PDF/A-3 + Factur-X als een enkele config-flag behandelt, is de migratie mechanisch. Zo niet, dan bouw je een multi-stap ops-pipeline. gPdf’s /api/v1/e-invoice/render is de single-flag-versie — de API-referentie heeft het volledige schema, of probeer een sample-render in de Playground.