---
title: Générer un PDF/A-3 en PHP (le conteneur de Factur-X)
source: https://synapx.fr/blog/generer-pdf-a-3-php/
date: 2026-06-27
category: Facturation électronique
site: SynapxLab
---

# Générer un PDF/A-3 en PHP

PDF/A-3 est la norme ISO 19005-3 et c'est le conteneur exigé par la spécification Factur-X pour embarquer un fichier XML de facture dans un PDF. Avant d'aller plus loin : produire un PDF/A-3 *réellement conforme* en PHP est plus difficile qu'il n'y paraît. La majorité des bibliothèques PHP ne le font pas nativement. Cet article l'explique sans détour.

> 🔎 Si vous cherchez à intégrer Factur-X de bout en bout (génération de l'XML + embarquement), consultez le testeur [/sdk/FactureX/](/sdk/FactureX/) qui valide ces étapes.

---

## Pourquoi PDF/A-3 et pas un PDF ordinaire ?

PDF/A est une famille de profils ISO conçue pour l'archivage long terme. Chaque version de la norme ajoute des capacités :

| Norme | Fichiers joints autorisés |
|---|---|
| PDF/A-1 (ISO 19005-1) | Non |
| PDF/A-2 (ISO 19005-2) | Oui (mais usage limité) |
| **PDF/A-3 (ISO 19005-3)** | **Oui, tout type de fichier** |

C'est cette dernière caractéristique qui rend PDF/A-3 obligatoire pour Factur-X : la norme exige que le fichier `factur-x.xml` (ou `zugferd-invoice.xml`) soit une pièce jointe *embedded* avec une relation `AF` (Associated File). PDF/A-1 et la plupart des PDF classiques ne le permettent pas de manière conforme.

Autrement dit : un PDF Factur-X sans le profil PDF/A-3 n'est pas un document Factur-X valide.

---

## Les exigences d'un PDF/A-3

La norme impose plusieurs contraintes cumulatives.

### OutputIntent avec profil colorimétrique ICC

Le fichier PDF doit déclarer un `OutputIntent` pointant vers un profil ICC valide (typiquement `sRGB.icc`). Sans cela, les couleurs ne sont pas définies de manière indépendante du périphérique, ce qui viole la norme.

### Métadonnées XMP obligatoires

Le document doit embarquer un flux XMP incluant au minimum :

- l'identifiant PDF/A : `pdfaid:part = 3`
- le niveau de conformance : `pdfaid:conformance = B` (Basic) ou `U` (Unicode)
- les métadonnées Dublin Core standard (titre, auteur, date…)

Ces métadonnées doivent être un flux XMP valide dans le dictionnaire `Metadata` du catalogue PDF, pas seulement dans les `Info` du document.

### Polices intégrées

Toutes les polices utilisées doivent être intégralement embarquées. Les polices référencées par nom mais non embarquées rendent le document non conforme.

### Contenus interdits

PDF/A-3 interdit explicitement : JavaScript embarqué, chiffrement (pas de mot de passe), références externes non résolues, transparence non aplatie (selon le niveau), et certaines actions.

---

## Les voies disponibles en PHP

### Voie 1 — Post-traitement par Ghostscript (la plus fiable)

Ghostscript est un interpréteur PostScript/PDF open source qui dispose d'une option `-dPDFA` permettant de convertir un PDF existant vers PDF/A. C'est actuellement la voie la plus documentée pour obtenir un PDF/A-3 réellement conforme depuis PHP.

**Principe :** PHP génère d'abord un PDF « ordinaire » avec la bibliothèque de son choix, puis appelle Ghostscript en sous-processus pour le convertir.

#### Fichier de définition PDFA_def.ps

Ghostscript a besoin d'un fichier PostScript de définition pour connaître le profil ICC et les métadonnées de base. Exemple minimal (à adapter) :

```postscript
%!
% PDFA_def.ps — définition pour conversion PDF/A-3
% Adapter le chemin vers votre profil ICC sRGB

[ /Title (Ma Facture)
  /Author (Mon Entreprise)
  /Subject (Facture Factur-X)
  /DOCINFO pdfmark

[/_objdef {icc_PDFA} /type /stream /OBJ pdfmark
[{icc_PDFA}
<<
  /N 3
>>
/PUT pdfmark
[{icc_PDFA} (/usr/share/ghostscript/iccprofiles/default_rgb.icc) (r) file /PUT pdfmark

[/_objdef {OutputIntent_PDFA} /type /dict /OBJ pdfmark
[{OutputIntent_PDFA} <<
  /Type /OutputIntent
  /S /GTS_PDFA1
  /DestOutputProfile {icc_PDFA}
  /OutputConditionIdentifier (sRGB)
>> /PUT pdfmark

[ {Catalog} << /OutputIntents [ {OutputIntent_PDFA} ] >> /PUT pdfmark
```

> Le chemin vers le profil ICC (`default_rgb.icc`) varie selon votre installation Ghostscript et votre OS. Vérifiez avec `find /usr -name "*.icc" 2>/dev/null`.

#### Appel depuis PHP

```php
<?php

function convertToPdfA3(string $inputPdf, string $outputPdf, string $pdfaDefPs): bool
{
    if (!file_exists($inputPdf)) {
        throw new \InvalidArgumentException("Fichier source introuvable : $inputPdf");
    }
    if (!file_exists($pdfaDefPs)) {
        throw new \InvalidArgumentException("Fichier de définition PDFA introuvable : $pdfaDefPs");
    }

    $command = sprintf(
        'gs -dBATCH -dNOPAUSE -dNOOUTERSAVE'
        . ' -dPDFA=3'
        . ' -dPDFACompatibilityPolicy=1'
        . ' -sColorConversionStrategy=UseDeviceIndependentColor'
        . ' -sDEVICE=pdfwrite'
        . ' -sOutputFile=%s'
        . ' %s'
        . ' %s'
        . ' 2>&1',
        escapeshellarg($outputPdf),
        escapeshellarg($pdfaDefPs),
        escapeshellarg($inputPdf)
    );

    exec($command, $output, $exitCode);

    if ($exitCode !== 0) {
        throw new \RuntimeException(
            "Ghostscript a échoué (code $exitCode) :\n" . implode("\n", $output)
        );
    }

    return file_exists($outputPdf);
}

// Usage
convertToPdfA3('/tmp/facture_brute.pdf', '/tmp/facture_pdfa3.pdf', '/var/app/pdfa/PDFA_def.ps');
```

**Avertissements importants :**

- Les options exactes de Ghostscript et leur comportement varient selon la version (la syntaxe ci-dessus vise Ghostscript 9.x/10.x mais peut évoluer).
- `exec()` doit être disponible (souvent désactivé en hébergement mutualisé).
- La conversion ne garantit pas la conformité si le PDF source contient des éléments non convertibles. Ghostscript retourne parfois `0` malgré des avertissements.
- **Valider le résultat avec veraPDF est indispensable.**

---

### Voie 2 — mPDF avec son option PDFA

mPDF propose une option `PDFA` qui tente de produire un PDF compatible PDF/A. Il faut être honnête sur ses limites.

```php
<?php

require_once __DIR__ . '/vendor/autoload.php';

$mpdf = new \Mpdf\Mpdf([
    'PDFA'      => true,
    'PDFAauto'  => true,
    'fontDir'   => [__DIR__ . '/fonts/'],
    'tempDir'   => '/tmp/mpdf',
]);

$mpdf->SetTitle('Ma Facture');
$mpdf->WriteHTML('<h1>Facture N° 2026-001</h1>');
$mpdf->Output('/tmp/facture_mpdf.pdf', 'F');
```

**Limites à connaître :**

- mPDF vise plutôt PDF/A-1b, pas nécessairement PDF/A-3. Le niveau réellement atteint dépend de la version et de la configuration.
- L'embarquement d'un fichier joint avec la relation `AF` requise par Factur-X n'est pas une fonctionnalité standard de mPDF.
- **Validez toujours avec veraPDF.**

---

### Voie 3 — TCPDF, FPDF, Dompdf

Ces bibliothèques n'ont pas de support natif pour un PDF/A conforme : **FPDF** (aucune gestion PDF/A), **TCPDF** (mécanismes partiels, pas de PDF/A-3 conforme sans travail important), **Dompdf** (pas de support). Une voie HTML → PDF puis conversion Ghostscript reste possible avec les mêmes précautions.

---

### Voie 4 — Déléguer à une librairie Factur-X

Pour produire une Factur-X complète (PDF/A-3 + XML embarqué + XMP), la voie la plus pragmatique est une bibliothèque qui gère tout le pipeline. Voir :
- [Générer un fichier Factur-X en PHP](/blog/generer-factur-x-php/)
- [Construire Factur-X sans bibliothèque](/blog/construire-factur-x-sans-bibliotheque-php/)
- [Factur-X : comprendre et valider la norme](/blog/factur-x-comprendre-valider/)

---

## Valider le résultat

Produire le fichier ne suffit pas. **Un PDF/A-3 doit être validé** par un outil qui implémente ISO 19005-3.

### veraPDF — le validateur de référence

veraPDF est le validateur PDF/A open source de référence, soutenu par la PDF Association. Disponible en ligne de commande :

```bash
# Validation d'un fichier
verapdf --flavour 3b votre_facture.pdf

# Avec rapport détaillé en XML
verapdf --flavour 3b --format xml votre_facture.pdf > rapport_pdfa3.xml
```

L'option `--flavour 3b` correspond à PDF/A-3 niveau B (Basic) ; `3u` pour le niveau U (Unicode). Une sortie conforme affiche `PASS` ; tout `FAIL` indique une violation à corriger.

veraPDF valide la conformance PDF/A mais pas la sémantique Factur-X (XML, profil EN 16931). Pour valider l'ensemble, combinez veraPDF et un validateur Factur-X.

---

## FAQ

**Un PDF généré par FPDF/TCPDF peut-il passer veraPDF ?**
Dans la grande majorité des cas, non, sans post-traitement : il manque au minimum les métadonnées XMP correctement structurées et souvent l'OutputIntent ICC.

**Ghostscript est-il disponible sur les hébergements mutualisés ?**
Rarement. Sur un VPS ou serveur dédié, installez-le (`apt install ghostscript`). En mutualisé, une API tierce est souvent la seule option.

**PDF/A-3 niveau B ou U : quelle différence ?**
Le niveau B garantit la reproduction visuelle fiable. Le niveau U ajoute que tout le texte est mappé sur des caractères Unicode. Pour Factur-X, le niveau B suffit, mais U est préférable pour l'extraction de texte.

**Peut-on générer un PDF/A-3 entièrement en PHP sans Ghostscript ?**
Techniquement oui, en manipulant les structures PDF bas niveau (XMP, OutputIntent, fichiers avec AF) — c'est ce que font les bibliothèques Factur-X. Le réimplémenter from scratch est long et sujet à erreurs.

---

## Pour aller plus loin

- **veraPDF** — validateur PDF/A open source de référence (site officiel veraPDF).
- **Ghostscript** — documentation des options `-dPDFA` et des fichiers de définition (site officiel Ghostscript).
- **ISO 19005-3** — la norme PDF/A-3 (via l'ISO). La spécification Factur-X (FNFE-MPE) la référence explicitement.
- [Générer un fichier Factur-X en PHP](/blog/generer-factur-x-php/)
- [Construire Factur-X sans bibliothèque](/blog/construire-factur-x-sans-bibliotheque-php/)
- [Factur-X : comprendre et valider](/blog/factur-x-comprendre-valider/)
