Écrire un PDF à partir de zéro (01) : Hello, World - Construire un PDF minimal viable
Écrire un PDF à partir de zéro (01) : Hello, World - Construire un PDF minimal viable
Objectif de la série : Comprendre le PDF comme un format de fichier lisible - commencer par un exemple minimal qui "fonctionne", puis étendre progressivement aux graphiques, aux pages multiples, à la compression et à la réutilisation des ressources.
Table des matières de la série
- Article 01 (celui-ci) : Écrire manuellement un PDF minimal (1 page + 1 ligne de texte) et utiliser un outil pour le compléter en un PDF standard pouvant être ouvert
- Article 02 : Dessiner des lignes/rectangles dans le flux de contenu (comprendre les chemins, les contours, le remplissage)
- Article 03 : PDF multi-pages (comment l'arbre des Pages se développe)
- Article 04 : Plus proche du monde réel (flux compressés, réutilisation des ressources, structures optionnelles, etc.)
Pourquoi comprendre la structure sous-jacente du PDF ?
Le PDF (Portable Document Format, format de document portable) est l'un des langages de description de page les plus populaires aujourd'hui. Contrairement à HTML/CSS qui sépare "contenu et présentation, et qui est reflowable", le PDF met davantage l'accent sur la mise en page fixe, ce que vous voyez est ce que vous obtenez - peu importe sur quel appareil il est ouvert, la mise en page reste cohérente.
Comprendre la structure sous-jacente du PDF présente plusieurs avantages pratiques :
- Débogage des problèmes de génération de PDF : Lorsque vous générez un PDF avec une bibliothèque de code et que cela échoue, comprendre la structure sous-jacente vous permet de localiser rapidement le problème.
- Traitement automatisé : Pour des opérations telles que l'extraction de texte en masse, la fusion de documents, l'ajout de filigranes, comprendre la structure permet d'opérer avec précision.
- Audit de sécurité : Comprendre quels contenus peuvent être intégrés dans un PDF (JavaScript, pièces jointes, formulaires, etc.) aide à l'analyse de sécurité.
- Apprentissage de la conception de formats de fichiers : La conception "objet graphique + accès aléatoire" du PDF est un exemple classique qui mérite d'être étudié.
Préparation
Cet article va d'abord écrire un hello-broken.pdf "structure incomplète mais logique", puis utiliser pdftk pour compléter automatiquement la structure clé et produire hello.pdf.
- Outils nécessaires : pdftk (outil en ligne de commande gratuit, compatible avec Windows/macOS/Linux)
- Fichiers de sortie :
hello-broken.pdf(manuscrit),hello.pdf(réparé et pouvant être ouvert)
Concepts clés : La structure à trois niveaux du PDF
Comprendre le PDF nécessite d'établir un modèle mental à trois niveaux :

1. Niveau des objets (Document Content)
Un document PDF est composé de nombreux objets, reliés entre eux par des références indirectes (comme 2 0 R) pour former une image. Types d'objets courants :
| Type | Exemple | Description |
|---|---|---|
| Nom | /Page | Nom commençant par / |
| Entier/Réel | 50, 36.0 | Valeur numérique |
| Chaîne | (Hello, World!) | Enveloppée dans des parenthèses |
| Tableau | [0 0 612 792] | Ensemble ordonné |
| Dictionnaire | << /Type /Page >> | Ensemble de paires clé-valeur |
| Référence indirecte | 2 0 R | Référence à l'objet 2 (numéro de génération 0) |
| Flux | stream...endstream | Données binaires (comme des instructions de dessin, des images) |
2. Niveau de contenu (Page Content)
La séquence d'instructions qui "dessine le texte/les graphiques sur la page", généralement écrite dans stream ... endstream. Le format est : opérande en premier, opérateur en second.
/F0 36 Tf ← Opérande : /F0, 36 Opérateur : Tf (définir la police)
(Hello, World!) Tj ← Opérande : chaîne Opérateur : Tj (dessiner le texte)
3. Niveau de structure de fichier (File Structure)
Permet au lecteur d'accéder rapidement et aléatoirement à n'importe quel objet, sans avoir à lire de bout en bout :
| Élément | Fonction |
|---|---|
%PDF-1.x | En-tête de fichier, identifie la version PDF |
xref | Table de référence croisée : numéro d'objet → décalage en octets |
trailer | Dictionnaire de fin : pointe vers l'objet racine /Root |
startxref | Indique la position de départ de la table xref |
%%EOF | Marque de fin de fichier |
Quels objets sont nécessaires pour un PDF minimal ?
Un PDF "minimal mais capable d'afficher du texte" a les relations de référence entre les objets comme suit :

Liste des objets minimaux :
| Objet | Fonction | Champs clés |
|---|---|---|
| Catalog | Objet racine, point d'entrée du document | /Type /Catalog, /Pages |
| Pages | Arbre des pages | /Type /Pages, /Kids, /Count |
| Page | Page unique | /Type /Page, /MediaBox, /Resources, /Contents, /Parent |
| Resources | Conteneur de ressources | /Font (dictionnaire de polices) |
| Font | Définition de la police | /Type /Font, /BaseFont, /Subtype |
| Contents | Flux de contenu | Flux d'instructions de dessin |
Pratique : Écrire hello-broken.pdf
Créez un fichier hello-broken.pdf et collez-y le contenu suivant :
%PDF-1.0
1 0 obj
<< /Type /Pages
/Count 1
/Kids [2 0 R]
>>
endobj
2 0 obj
<< /Type /Page
/MediaBox [0 0 612 792]
/Resources 3 0 R
/Parent 1 0 R
/Contents [4 0 R]
>>
endobj
3 0 obj
<< /Font
<< /F0
<< /Type /Font
/BaseFont /Times-Italic
/Subtype /Type1 >>
>>
>>
endobj
4 0 obj
<< >>
stream
1. 0. 0. 1. 50. 700. cm
BT
/F0 36. Tf
(Hello, World!) Tj
ET
endstream
endobj
5 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 6
trailer
<< /Size 6
/Root 5 0 R
>>
startxref
0
%%EOF
Pourquoi ce fichier est-il "cassé" ?
Nous avons délibérément omis ou mal rempli les éléments suivants :
| Élément manquant/erroné | Description |
|---|---|
Décalage xref | Aucun décalage en octets réel pour chaque objet n'a été rempli |
startxref | Rempli avec 0, pas la position réelle de xref |
/Length | Le flux de contenu n'a pas déclaré de longueur |
| Marque binaire | Ligne d'identification binaire manquante dans l'en-tête |
Ce sont toutes des informations clés nécessaires au lecteur, et leur absence peut entraîner l'impossibilité d'ouvrir le fichier ou une ouverture avec des erreurs.
Détails des instructions clés du flux de contenu
Le flux de contenu se trouve entre stream ... endstream de l'objet 4 0 obj, expliqué ligne par ligne :
1. 0. 0. 1. 50. 700. cm ← Définit la matrice de transformation (notez que 1. représente le nombre à virgule flottante 1.0)
BT ← Début de l'objet texte
/F0 36. Tf ← Sélectionne la police F0, taille 36pt
(Hello, World!) Tj ← Dessine la chaîne
ET ← Fin de l'objet texte
Opérateur de matrice de transformation cm
1 0 0 1 50 700 cm est une matrice de transformation à 6 éléments [a b c d e f], correspondant à :
| a b 0 | | 1 0 0 |
| c d 0 | = | 0 1 0 |
| e f 1 | | 50 700 1 |
Lorsque a=1, b=0, c=0, d=1, c'est une matrice de translation pure, déplaçant l'origine du système de coordonnées (c'est-à-dire le point (0,0) pour les opérations de dessin suivantes) à (50, 700). Si vous ne déplacez pas, l'origine par défaut est dans le coin inférieur gauche de la page.
Opérateurs de texte
| Opérateur | Signification | Exemple |
|---|---|---|
BT | Begin Text, début de l'objet texte | BT |
ET | End Text, fin de l'objet texte | ET |
Tf | Définit la police et la taille | /F0 36 Tf |
Tj | Dessine la chaîne | (Hello!) Tj |
Utiliser pdftk pour réparer en un PDF pouvant être ouvert
Exécutez dans le répertoire où se trouve hello-broken.pdf :
pdftk hello-broken.pdf output hello.pdf
Ouvrez hello.pdf avec n'importe quel lecteur PDF, vous devriez voir apparaître sur la page "Hello, World!" (police Times-Italic, 36pt, située en haut à gauche de la page).
Qu'est-ce que pdftk a complété pour vous ?
| Éléments complétés | Description |
|---|---|
| Ligne de marque binaire | Ajoute une ligne de caractères non imprimables après %PDF-1.0, garantissant qu'il soit reconnu comme un fichier binaire |
/Length | Calcule et ajoute la longueur en octets pour le flux de contenu |
Table xref | Calcule le décalage en octets de chaque objet et le remplit |
startxref | Remplit la position de départ réelle de la table xref |
Pourquoi avoir besoin de xref / trailer / startxref ?
Objectif principal : accès aléatoire
Imaginez un PDF de 500 pages, sans xref, le lecteur doit analyser depuis le début jusqu'à la page 449 pour afficher la page 450 - c'est trop lent.
Avec xref, le lecteur peut :
- Lire d'abord
startxref→ trouver la position de xref - Lire
trailer→ trouver l'objet racine/Root - Suivre les indices depuis l'objet racine → sauter directement à l'objet de la page 450
- Vérifier le décalage en octets de cet objet via xref → se déplacer directement pour lire
La complexité temporelle passe de O(n) à O(1).
Exercice de cet article
Il est conseillé de modifier réellement hello-broken.pdf, puis de le réparer à nouveau avec pdftk et d'observer les effets :
| Exercice | Contenu modifié | Points d'observation |
|---|---|---|
| A | Remplacer (Hello, World!) par une autre phrase en anglais | Changement de texte |
| B | Remplacer 36 par 12 ou 72 | Changement de taille de police |
| C | Remplacer 50 700 par 50 100 | Déplacement vers le bas (l'origine du système de coordonnées PDF est en bas à gauche) |
| D | Remplacer /Times-Italic par /Helvetica ou /Courier | Changement de police |
| E | Remplacer /MediaBox [0 0 612 792] par [0 0 595 842] | Changement de papier de US Letter à A4 |
Astuce : L'origine du système de coordonnées PDF est en bas à gauche de la page, l'axe Y va vers le haut.
(50, 700)signifie à 50pt du bord gauche et à 700pt du bas.
Questions fréquentes
Q : Pourquoi utiliser des polices intégrées de type 1 plutôt que TrueType ?
R : Les 14 polices standard de type 1 (Times, Helvetica, Courier, etc.) doivent être intégrées dans le lecteur PDF, sans nécessiter d'inclure des fichiers de police, ce qui est le plus simple. Dans des scénarios réels, il est souvent nécessaire d'incorporer des polices pour garantir la cohérence entre les plateformes.
Q : Que signifient ces chiffres dans /MediaBox [0 0 612 792] ?
R : L'unité est le point (1 point = 1/72 de pouce). 612 × 792 points = 8.5 × 11 pouces = papier US Letter. A4 est 595 × 842 points.
Q : Que signifie le numéro de génération (comme le 0 dans 2 0 R) ?
R : Utilisé pour les mises à jour incrémentielles. Lorsque l'objet est modifié, le numéro de génération augmente de 1. Dans un PDF nouvellement créé, tous les numéros de génération des objets sont généralement 0.
Aperçu du prochain article
Dans l'article 02, nous continuerons avec la méthode "écriture manuelle du flux de contenu", en ajoutant les opérations de chemin graphique les plus fondamentales :
m(moveto),l(lineto) : définir un cheminS(stroke) : tracerre(rectangle),f(fill) : dessiner un rectangle et le remplir
Vous apprendrez à dessiner simultanément sur la même page : texte de titre + une ligne de séparation horizontale + un cadre rectangulaire, passant de "savoir écrire" à "savoir dessiner des graphiques".
