Technique

PDF/A-3 pour Factur-X : checklist conformité ISO 19005-3, pièges Ghostscript et mPDF

8 min de lecture Par FacturX API

Comment produire un PDF/A-3 conforme pour Factur-X : ICC profiles, AFRelationship, XMP metadata, erreurs Ghostscript, mPDF et VeraPDF. Guide technique complet.

Le conteneur PDF/A-3 est la partie la moins documentée de Factur-X. La norme EN16931 et les guides FNFE-MPE se concentrent sur le contenu XML — les profils, les règles Schematron, le mapping des champs. Mais une facture Factur-X n’est valide que si son enveloppe PDF respecte aussi la norme ISO 19005-3. Et c’est là que de nombreuses bibliothèques PDF du marché échouent silencieusement.

Un PDF généré avec une bibliothèque standard passe visuellement. Il s’ouvre, il s’imprime, il ressemble à une facture. Mais il sera rejeté par VeraPDF et potentiellement par la PDP — avec une erreur cryptique sur les profils ICC ou les métadonnées XMP.

Ce guide couvre les causes exactes de ces échecs et comment les corriger.

Pour le contexte général de la réforme et du pipeline de validation, voir Facturation électronique 2026 : le guide technique complet pour développeurs.

Ce qu’impose réellement ISO 19005-3 (PDF/A-3)

PDF/A-3 est un profil d’archivage à long terme du format PDF. Il impose que le document soit autonome : tout ce qui est nécessaire à son rendu doit être embarqué dans le fichier, sans dépendance externe.

Les contraintes principales pour un conteneur Factur-X :

1. Polices utilisées pour le rendu embarquées

Les polices utilisées dans le PDF doivent être embarquées (les sous-ensembles — subsets — sont acceptés). Une référence à une police système (Helvetica, Arial, Times New Roman sans embedding) rend le document non conforme. Ce n’est pas une recommandation — c’est une interdiction stricte.

2. Profil colorimétrique ICC (conditionnel)

Lorsque des espaces colorimétriques non calibrés sont utilisés dans le PDF (DeviceRGB, DeviceGray, DeviceCMYK), le dictionnaire OutputIntents doit contenir un entrée PDF/A avec un profil ICC valide. Ce profil doit correspondre à un espace colorimétrique autorisé : GRAY, RGB ou CMYK. La clause §6.2.3 d’ISO 19005-3 précise également que : la valeur S du dictionnaire doit être GTS_PDFA1 ; le flux DestOutputProfile doit être un profil ICC de classe prtr ou mntr ; la clé DestOutputProfileRef ne doit pas être présente ; si plusieurs OutputIntent ont un DestOutputProfile, ils doivent référencer le même objet indirect.

3. Aucune dépendance externe

Aucun lien vers des ressources extérieures : pas de JavaScript, pas d’actions Launch, pas d’encryption, pas de références à des flux distants. Ces fonctionnalités sont explicitement interdites par ISO 19005-3.

4. Métadonnées XMP

Le fichier doit contenir des métadonnées XMP dans le flux de métadonnées du document. Ces métadonnées doivent indiquer le niveau de conformité PDF/A (pdfaid:part=3, pdfaid:conformance=B). Pour Factur-X, les métadonnées XMP doivent aussi contenir les propriétés Factur-X dans le namespace fx: — mais ces propriétés personnalisées doivent être accompagnées de la description d’extension de schéma XMP PDF/A (PDF/A extension schema description), sans quoi le PDF/A-3 n’est pas formellement valide.

5. Pièce jointe XML avec AFRelationship adapté à la version ciblée

Le fichier XML CII embarqué doit être attaché avec un attribut /AFRelationship conforme à la version Factur-X ou ZUGFeRD ciblée :

  • ZUGFeRD 1.0 : /Alternative
  • ZUGFeRD 2.0 : /Alternative
  • ZUGFeRD 2.1 / Factur-X 1.x : /Alternative est la valeur recommandée par la spécification Factur-X

Ne pas supposer qu’une valeur unique est universellement correcte sans vérifier la version de la spec cible.

Les cinq erreurs les plus fréquentes

1. Ghostscript : violation de la clause §6.2.3

Ghostscript est largement utilisé pour convertir des PostScript ou des PDF génériques en PDF/A. Il dispose d’un profil de sortie -dPDFA=3 mais sa gestion des profils colorimétriques exige une configuration explicite.

L’erreur la plus courante : ne pas fournir de fichier pdfa_def.ps avec le chemin du profil ICC et l’OutputConditionIdentifier. Sans ce fichier de configuration, Ghostscript ne peut pas correctement renseigner le dictionnaire OutputIntents conforme à §6.2.3.

La commande insuffisante :

# Insuffisant : pas de pdfa_def.ps ni de profil ICC explicite
gs -dBATCH -dNOPAUSE -dNOSAFER \
   -sDEVICE=pdfwrite \
   -dPDFA=3 \
   -dPDFACompatibilityPolicy=1 \
   input.pdf -sOutputFile=output.pdf

Une approche plus conforme à la documentation Ghostscript, avec accès explicite au profil ICC :

# Plus conforme : profil ICC déclaré, accès fichier autorisé explicitement
gs --permit-file-read=/chemin/vers/profil.icc \
   -sDEVICE=pdfwrite \
   -dPDFA=3 \
   -dPDFACompatibilityPolicy=1 \
   -sColorConversionStrategy=RGB \
   -o output.pdf \
   /chemin/vers/pdfa_def.ps \
   input.pdf

Le fichier pdfa_def.ps doit définir OutputConditionIdentifier et le chemin vers le profil ICC. La doc Ghostscript sur ZUGFeRD/Factur-X détaille cette configuration. -dNOSAFER est à éviter en production ; préférer --permit-file-read= pour autoriser explicitement la lecture du profil ICC.

2. mPDF (PHP) : support PDF/A-3 incertain

mPDF est la bibliothèque PHP la plus utilisée pour générer des PDF dans les ERP open source (Dolibarr, certaines versions d’InvoiceNinja). Elle mentionne dans sa documentation le support des “associated files” et des “additional XMP RDF” pour PDF/A-3. En pratique, des discussions et issues sur le dépôt GitHub de mPDF indiquent que les fichiers produits sont souvent identifiés comme PDF/A-1b plutôt que PDF/A-3b par les validateurs.

Avant de déployer mPDF pour générer des conteneurs Factur-X, valider systématiquement les fichiers produits avec VeraPDF. Si la pièce jointe XML est présente mais que le fichier est identifié comme PDF/A-1b, mPDF n’a probablement pas produit un vrai PDF/A-3.

Diagnostic via VeraPDF ou via Python :

from pypdf import PdfReader

reader = PdfReader("facture.pdf")
for name, data_list in reader.attachments.items():
    for data in data_list:
        print(f"{name}: {len(data)} bytes")
# Si output: factur-x.xml: 0 bytes → fichier attaché mais flux vide

3. Bibliothèques générant PDF/A-1 ou PDF/A-2 au lieu de PDF/A-3

Factur-X nécessite PDF/A-3 (ISO 19005-3) spécifiquement, car c’est le seul niveau qui autorise les pièces jointes génériques (fichiers non-PDF/A). PDF/A-2 autorise les pièces jointes PDF/A uniquement — le scénario hybride Factur-X (XML CII embarqué dans le PDF) n’est donc pas supporté en PDF/A-2.

Certaines bibliothèques configurées en mode “PDF/A” sans précision de version produisent du PDF/A-1 ou PDF/A-2. VeraPDF rapportera alors :

ERROR: The embedded file is not allowed in PDF/A-1 documents (ISO 32000-1:7.11)

Vérifier le niveau de conformité dans les métadonnées XMP :

<pdfaid:part>3</pdfaid:part>
<pdfaid:conformance>B</pdfaid:conformance>

Toute valeur de pdfaid:part autre que 3 invalide le document pour Factur-X.

4. AFRelationship incorrect pour la version ciblée

L’attribut /AFRelationship de la pièce jointe XML dépend de la version Factur-X ou ZUGFeRD que vous ciblez. Il n’existe pas de valeur universellement correcte :

  • ZUGFeRD 1.0 et 2.0 utilisaient /Alternative
  • Pour Factur-X 1.x / ZUGFeRD 2.1+, /Alternative est la valeur attendue selon la spécification Factur-X

Vérifier la spécification de la version ciblée plutôt que d’appliquer une valeur par défaut. Un /AFRelationship incohérent avec la version déclarée dans les métadonnées XMP peut entraîner des rejets chez certaines PDP.

Extrait du dictionnaire pour Factur-X 1.x :

/Type /Filespec
/F (factur-x.xml)
/UF (factur-x.xml)
/AFRelationship /Alternative
/EF << /F stream_ref /UF stream_ref >>

5. Métadonnées XMP Factur-X absentes ou incomplètes

En plus des métadonnées PDF/A standard (pdfaid:part, pdfaid:conformance), Factur-X exige un namespace XMP spécifique fx: contenant les propriétés du document :

<rdf:Description rdf:about=""
  xmlns:fx="urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#">
  <fx:DocumentFileName>factur-x.xml</fx:DocumentFileName>
  <fx:DocumentType>INVOICE</fx:DocumentType>
  <fx:Version>1.0</fx:Version>
  <fx:ConformanceLevel>EN 16931</fx:ConformanceLevel>
</rdf:Description>

Point critique souvent omis : en PDF/A-3, tout namespace XMP personnalisé (comme fx:) doit être accompagné de sa description d’extension de schéma PDF/A (pdfaExtension:schemas). Sans cette description, les propriétés fx: sont présentes dans le XMP mais le fichier n’est pas formellement conforme à PDF/A-3. La description de schéma déclare chaque propriété avec son type, son nom complet et sa description — elle figure dans les samples de référence du dépôt factur-x sur GitHub.

Les valeurs de fx:ConformanceLevel correspondent aux profils Factur-X :

ProfilConformanceLevel
MINIMUMMINIMUM
BASIC WLBASIC WL
BASICBASIC
EN16931EN 16931
EXTENDEDEXTENDED

Le nom du fichier dans fx:DocumentFileName doit correspondre exactement au nom de la pièce jointe dans le dictionnaire PDF.

Checklist de validation PDF/A-3

Avant de soumettre un PDF Factur-X à une PDP, vérifier chaque point :

CONFORMITÉ PDF/A-3 (ISO 19005-3)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Structure PDF
☐ Polices utilisées pour le rendu embarquées (subsets acceptés)
☐ Si espaces DeviceRGB/CMYK/Gray : OutputIntents avec profil ICC valide
☐   └ S = GTS_PDFA1, classe ICC prtr ou mntr, pas de DestOutputProfileRef
☐ Pas de JavaScript, pas d'encryption, pas de launch actions
☐ pdfaid:part = 3 dans les métadonnées XMP
☐ pdfaid:conformance = B dans les métadonnées XMP

Pièce jointe XML
☐ Nom conforme à la version :
☐   └ Factur-X 1.x / ZUGFeRD 2.1+ : "factur-x.xml"
☐   └ ZUGFeRD 2.0 : "zugferd-invoice.xml"
☐   └ ZUGFeRD 1.0 : "ZUGFeRD-invoice.xml"
☐ AFRelationship conforme à la version ciblée (/Alternative pour FX 1.x)
☐ MimeType = text/xml
☐ Taille du stream > 0
☐ Encoding UTF-8 sans BOM

Métadonnées XMP Factur-X
☐ Namespace fx: présent (urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#)
☐ PDF/A extension schema description présente pour le namespace fx:
☐ fx:DocumentFileName correspond au nom du fichier attaché
☐ fx:DocumentType = INVOICE
☐ fx:ConformanceLevel correspond au profil du XML (ex: "EN 16931")
☐ fx:Version = 1.0

Contenu XML
☐ XML parsable (pas de BOM, encodage UTF-8)
☐ GuidelineID dans le XML correspond au ConformanceLevel XMP

Valider avec VeraPDF

VeraPDF est le validateur de référence pour PDF/A. Il implémente la spécification ISO 19005-3 et permet de vérifier la conformité PDF/A-3 de votre conteneur.

# Image CLI officielle
docker run --rm -v "$PWD":/work verapdf/cli \
  --flavour 3b \
  /work/facture.pdf

# Avec sortie JSON
docker run --rm -v "$PWD":/work verapdf/cli \
  --flavour 3b \
  --format json \
  /work/facture.pdf | jq '.jobs[0].validationResult'

Un résultat conforme retourne "isCompliant": true. En cas d’échec, le rapport JSON liste chaque violation avec sa référence de clause ISO :

{
  "isCompliant": false,
  "statement": "1 fail(s)",
  "details": {
    "ruleSummaries": [
      {
        "clause": "6.2.3",
        "testNumber": 1,
        "description": "The document catalog shall include a DestOutputProfile key...",
        "occurrences": 1
      }
    ]
  }
}

La clause 6.2.3 indique systématiquement un problème avec le dictionnaire OutputIntents (profil ICC absent, invalide, ou mal configuré).

VeraPDF valide la conformité PDF/A-3 de votre fichier. La validation des règles métier Factur-X (XSD + Schematron EN16931) est distincte et nécessite un outil dédié — voir Valider EN16931/Factur-X : XSD vs Schematron, erreurs BR-*.

L’alternative : déléguer la génération PDF/A-3

La conformité PDF/A-3 exige une connaissance fine de la spec ISO 19005-3, une gestion correcte des ICC profiles, de l’extension schema XMP, et une maintenance lors des mises à jour des bibliothèques. Pour la plupart des équipes, le coût de maintenance d’une génération PDF/A-3 maison est disproportionné par rapport à la valeur métier.

L’API FacturX accepte aussi bien un XML CII seul qu’un PDF/A-3 complet. Pour les équipes qui génèrent leur XML mais ne veulent pas gérer la couche PDF/A-3 :

# Envoyer uniquement le XML — l'API valide sans nécessiter le PDF
curl -X POST https://facturxapi.com/api/v1/validate \
  -H "X-API-Key: votre-cle-api" \
  -F "[email protected]"

Pour les PDF complets, la validation couvre les trois niveaux en une passe : PDF/A-3, XSD, et Schematron.

Ressources

#pdf/a-3 #factur-x #verapdf #ghostscript #iso-19005-3 #conformité