Ce qu’est vraiment un PDF
ISO 32000-2 §7 Evidence: Standard-backed
Un PDF n’est pas simplement une description de page glissée dans un fichier. C’est une petite base de données orientée graphe, avec une imprimante greffée dessus. Cette page décrit les quatre parties que possède chaque PDF — en-tête, corps, table de références croisées, trailer — et la manière dont NextPDF les écrit pour qu’un lecteur puisse retrouver chaque objet sans rien deviner.
Pourquoi c’est important
Section intitulée « Pourquoi c’est important »La plupart des bugs PDF ne sont pas des bugs de rendu. Ce sont des bugs de structure : un décalage d’octets qui pointe un caractère après l’objet visé, un trailer qui désigne la mauvaise racine, une entrée de références croisées en désaccord avec l’emplacement réel de l’objet. Aucun de ces problèmes ne change l’aspect d’une page jusqu’à ce qu’un lecteur emprunte un autre chemin dans le fichier et se retrouve au-delà de sa fin.
Si tu traites un PDF comme une boîte noire, ces défaillances semblent aléatoires. Si tu connais le modèle objet, tu les vois pour ce qu’elles sont : un nombre qui ne correspond pas à une position. Savoir lire le format, c’est la différence entre « le PDF est corrompu » et « le décalage de l’objet 14 est périmé parce que le générateur l’a mesuré avant de finaliser la longueur du flux. »
En résumé
Section intitulée « En résumé »Un PDF comporte quatre parties, dans l’ordre du fichier :
- Un en-tête — une ligne qui indique la version (
%PDF-2.0). - Un corps — une suite d’objets indirects numérotés : dictionnaires, flux, tableaux, nombres, chaînes, noms.
- Une table de références croisées (ou, en PDF 2.0, un flux de références croisées) — un index associant chaque numéro d’objet à un décalage d’octets, afin que tout objet soit atteignable sans parcourir le fichier.
- Un trailer — un petit dictionnaire qui nomme l’objet racine du document et indique où commence la section de références croisées.
Un lecteur ne lit pas un PDF du début à la fin. Il lit d’abord la dernière ligne, trouve startxref, saute jusqu’à la section de références croisées et l’utilise comme index dans le corps. Le format est conçu pour être lu à rebours. Ce seul fait explique l’essentiel de sa conception.
Comment NextPDF l’aborde
Section intitulée « Comment NextPDF l’aborde »NextPDF construit un PDF dans le même ordre logique que sa lecture : l’objet d’abord, le décalage enregistré ensuite, la table écrite en dernier.
Chaque objet indirect reçoit un numéro depuis un registre unique (src/Core/ObjectRegistry.php). Le registre distribue des numéros séquentiels via allocate() et, après que les octets d’un objet ont été écrits dans le tampon de sortie, enregistre le décalage d’octets via register(). Les décalages ne sont jamais devinés à l’avance. Ils sont relevés depuis BinaryBuffer::getOffset() au moment où l’en-tête de l’objet est émis. C’est pourquoi une entrée de références croisées NextPDF ne peut pas se décaler par rapport à l’objet qu’elle décrit : le décalage correspond exactement à la position réelle de l’objet dans le tampon.
Une fois le corps complet, une stratégie de sérialisation propre à la version (src/Writer/PdfSerializationStrategy.php) écrit la section de références croisées et le trailer :
Pdf20StreamStrategyémet un flux de références croisées compressé (/Type /XRef) — la valeur par défaut en PDF 2.0.Pdf17TableStrategyetPdf14TableStrategyémettent une table de références croisées traditionnelle de 20 octets, plus un dictionnaire de trailer distinct — exigés par les profils PDF/A qui imposent une structure de fichier plus ancienne.
La stratégie est choisie par le profil de sortie, et non déduite. Quelle qu’elle soit, les octets finaux ont la même forme : la section de références croisées, puis startxref, puis le décalage d’octets, puis %%EOF. C’est cette fin de fichier qu’un lecteur trouve en premier.
- Step 1 of 4: ISO 32000-2 §7.5.5 — %%EOF and startxref at the file end
- Step 2 of 4: ISO 32000-2 §7.5.4 / §7.5.8 — the cross-reference section maps object number to offset
- Step 3 of 4: ISO 32000-2 §7.5.5 — the trailer names /Root, the document catalog
- Step 4 of 4: ISO 32000-2 §7.3.10 — each indirect object is reached at its recorded offset
Ce que disent les preuves
Section intitulée « Ce que disent les preuves »La structure en quatre parties n’est pas une convention propre à NextPDF ; elle relève de la clause de structure de fichier de Spec: ISO 32000-2, §7.5 ISO 32000-2 §7.5 . La norme définit un PDF comme un en-tête, un corps d’objets, une table de références croisées et un trailer, et précise qu’un lecteur devrait analyser le fichier depuis sa fin. La dernière ligne est %%EOF, et les deux lignes qui la précèdent sont le mot-clé startxref et le décalage d’octets vers la section de références croisées.
Un objet indirect est défini par un numéro d’objet et un numéro de génération, séparés par une espace, suivis de la valeur de l’objet encadrée entre les mots-clés obj et endobj. La combinaison du numéro d’objet et du numéro de génération identifie l’objet de façon unique ; une référence indirecte vers lui s’écrit avec le numéro d’objet, le numéro de génération et le mot-clé R. Le ObjectRegistry de NextPDF reproduit cela à l’identique : un numéro séquentiel, la génération 0 pour les objets nouvellement écrits, et un décalage enregistré.
À partir de PDF 1.5, les objets peuvent aussi résider dans un flux d’objets, où ils sont stockés sans les mots-clés obj/endobj et doivent avoir la génération zéro. Le flux de références croisées (/Type /XRef,
Spec: ISO 32000-2, §7.5.8 ISO 32000-2 §7.5.8 ) est le mécanisme PDF 2.0
qui indexe à la fois les objets ordinaires et ces objets compressés.
Le CrossReferenceStream de NextPDF le construit avec un tableau de largeurs de champ /W et une compression
FlateDecode.
Exemple concret
Section intitulée « Exemple concret »Voici la forme d’un corps PDF minimal et de son trailer. Les nombres dans la section de références croisées sont des décalages d’octets. Ils doivent être exacts au caractère près ; c’est pourquoi NextPDF les relève depuis le tampon plutôt que de les calculer.
%PDF-2.01 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>endobjxref0 40000000000 65535 f0000000009 00000 n0000000058 00000 n0000000122 00000 ntrailer<< /Size 4 /Root 1 0 R >>startxref196%%EOFUn lecteur ouvre ce fichier par la fin : %%EOF, puis startxref 196, puis il se positionne à l’octet 196 où commence xref, lit que l’objet 1 se trouve à l’octet 9, suit /Root 1 0 R jusqu’au catalogue, et parcourt l’arbre des pages à partir de là. L’objet 0 est toujours la tête de la liste des objets libres, avec la génération 65535 — une bizarrerie héritée de la conception initiale du format, fidèlement reproduite parce que les lecteurs s’y attendent.
Idée fausse répandue
Section intitulée « Idée fausse répandue »Le piège, c’est de croire qu’un PDF se lit de haut en bas comme du code source. Ce n’est pas le cas. Le corps peut présenter les objets dans n’importe quel ordre. Les numéros d’objets n’ont pas à être séquentiels dans le fichier, et un lecteur ne suppose jamais qu’ils le soient. Le seul index qui fasse autorité est la section de références croisées, et la seule façon de la trouver est le trailer à la fin. Un PDF doté d’un corps parfaitement valide mais d’un seul nombre erroné dans startxref est illisible. Un PDF dont les objets sont écrits dans un ordre non trié, mais avec une table de références croisées correcte, fonctionne parfaitement. La position brute ne signifie rien ; seule la position enregistrée compte.
Limites et périmètre
Section intitulée « Limites et périmètre »Cette page décrit la structure du fichier, pas le contenu des pages. La manière dont les marques apparaissent sur une page — flux de contenu, opérateurs graphiques, affichage du texte — relève d’un autre sujet. Elle ne couvre pas non plus ce qui se passe lorsqu’un fichier est modifié après son écriture. C’est le rôle des mises à jour incrémentales, où une seconde section de références croisées est ajoutée et où le trailer s’enchaîne vers l’arrière.
NextPDF est un générateur. Le comportement décrit ici correspond à la manière dont il sérialise un document qu’il a lui-même construit. Ce n’est pas un analyseur PDF généraliste ni un outil de réparation. Il ne promet pas de lire, reconstruire ou récupérer un fichier tiers arbitraire dont la table de références croisées est endommagée. La portée de la garantie est étroite, par choix. Les fichiers que NextPDF écrit ont des décalages qui correspondent, parce qu’ils sont mesurés, pas prédits.
Mini-FAQ
Section intitulée « Mini-FAQ »Pourquoi des numéros de génération si les nouveaux fichiers utilisent toujours 0 ? Les numéros de génération servent à réutiliser les objets au fil des mises à jour. Dans un fichier fraîchement écrit, chaque objet est à la génération 0. Des générations non nulles n’apparaissent que lorsqu’un fichier a été mis à jour de façon incrémentale et qu’un numéro d’objet est recyclé.
Deux objets peuvent-ils avoir le même numéro ? Dans une seule section de références croisées, non. Au fil de mises à jour incrémentales, un fichier peut physiquement contenir plusieurs copies du même numéro d’objet. L’entrée de références croisées la plus récente l’emporte. C’est le sujet de la page suivante.
L’ordre des objets dans le fichier a-t-il une importance pour la sortie ? Non. NextPDF écrit les objets dans un ordre déterministe pour des compilations reproductibles, mais un lecteur résout tout par la section de références croisées, si bien que l’ordre physique n’a aucune signification sémantique.
Documentation associée
Section intitulée « Documentation associée »- Les mises à jour incrémentales et pourquoi elles comptent — ce qui se passe quand un PDF déjà écrit est modifié : sections ajoutées et trailer chaîné.
- Flux et filtres — comment les objets flux du corps sont compressés et encodés.
- PDF 2.0 : ce qui a changé — en quoi la structure du fichier diffère entre PDF 1.7 et la base 2.0 ciblée par NextPDF.
Glossaire
Section intitulée « Glossaire »- Objet indirect — un objet numéroté dans le corps, écrit sous la forme
N G obj … endobj, oùNest le numéro d’objet etGle numéro de génération. - Référence indirecte — un pointeur vers un objet indirect, écrit
N G R. - Table de références croisées (xref) — l’index associant chaque numéro d’objet à un décalage d’octets. En PDF 2.0, il s’agit généralement d’un flux de références croisées (
/Type /XRef) au lieu de la table textuelle classique de 20 octets par entrée. - Trailer — le dictionnaire situé à la fin d’une section de références croisées qui nomme
/Root(le catalogue du document) et/Size, et que l’on trouve via le décalagestartxref. - Flux d’objets — un objet flux qui contient lui-même d’autres objets indirects (compressés ensemble) ; les membres n’ont pas de
obj/endobjet sont à la génération zéro. - Catalogue du document — l’objet nommé par
/Root; le point d’entrée vers l’arbre des pages et tout le reste du document.