En dépouillant la description de la page, il reste une mince structure — personne ne l'imprime, mais chaque lecteur, indexeur et système d'archivage en dépend. L'objet page ne sait rien de la section à laquelle il appartient, de l'auteur qui l'a écrit ou des notes de bas de page pointant ailleurs. Ces connaissances se situent au niveau supérieur, dans trois structures rattachées au catalogue de documents : le flux de métadonnées, l'arbre des contours et le tableau d'annotations page par page. Elles partagent une caractéristique commune qui facilite les erreurs — aucun marquage visible ne s'affiche sur la page, de sorte qu'un fichier peut s'afficher parfaitement tout en ayant des signets manquants, des champs d'auteur contradictoires ou des liens pointant vers des objets de page inexistants
C'est la couche exposée par la bibliothèque PDF sous forme de propriétés de document, d'API de signets, de liens ou d'appels d'annotations, et c'est aussi celle qu'utilisent les robots de recherche pour évaluer le contenu de votre document. Le modèle d'objet sous-jacent est détaillé dans la Structure des documents PDF. Cet article se focalise strictement sur ce qui est rattaché au catalogue
Trois bibliothèques. Trois tâches distinctes. Choisir la mauvaise vous obligera à faire des détours pendant des semaines ; et utiliser les trois alors que vous n'en aviez besoin que d'une vous imposera des coûts de maintenance sans budget. Voici une explication claire de ce que fait réellement chaque bibliothèque PDF de losLab, où elle convient, et où elle passe le relais à sa bibliothèque sœur
1 0 obj
<< /Type /Catalog
/Pages 2 0 R
/Outlines 3 0 R
/Names << /EmbeddedFiles 4 0 R >>
/Metadata 5 0 R
>>
endobj
Quatre entrées, quatre sous-systèmes indépendants. /Pages est le document visible ; /Outlines est l'arborescence des signets ; /Metadata pointe vers le flux XMP ; /Names accède au dictionnaire de noms au niveau du document, qui comprend des éléments tels que les pièces jointes de fichiers intégrés. Chaque entrée est facultative, et même si aucune n'est présente, le lecteur peut toujours afficher la page. C'est cette nature facultative qui fait que la couche de navigation est la première à se corrompre après qu'un fichier a été modifié par un outil qui ne comprend que les pages
Compilation conditionnelle lorsque les deux cibles restent actives
Le PDF stocke les métadonnées du document à deux endroits simultanément, et les ennuis commencent lorsque les deux versions ne sont pas d'accord. Le mécanisme d'origine est le dictionnaire d'informations sur le document, référencé via /Info dans le dictionnaire de fin (trailer) : un ensemble plat de paires clé-valeur contenant /Title, /Author, /Subject, /Keywords, /Creator, /Producer et deux champs de date. C'est simple et lisible par tous les lecteurs. Le PDF 2.0 a déprécié la majeure partie de son contenu au profit du second mécanisme — le flux de métadonnées XMP
Sémantique de propriété dans la VCL
5 0 obj
<< /Type /Metadata /Subtype /XML /Length 1235 >>
stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about=""
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/"
xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
<dc:title><rdf:Alt><rdf:li xml:lang="x-default">Quarterly Report</rdf:li></rdf:Alt></dc:title>
<dc:creator><rdf:Seq><rdf:li>A. Author</rdf:li></rdf:Seq></dc:creator>
<xmp:CreateDate>2026-06-16T10:46:27+08:00</xmp:CreateDate>
<xmp:CreatorTool>Reporting Service 4.2</xmp:CreatorTool>
<pdf:Producer>losLab PDF Library</pdf:Producer>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream
endobj
Trois détails dans ce bloc déterminent si les métadonnées survivront dans la chaîne d'outils réelle. Les instructions de traitement xpacket ne sont pas de la décoration : elles encadrent le paquet afin que les outils d'extraction le repèrent dans des flux d'octets plus importants — les outils d'écriture omettant la fin <?xpacket end="w"?> génèrent des fichiers qui s'ouvrent sans problème, mais déclenchent des erreurs de validateurs stricts. Les types de données des propriétés sont tout aussi importants. dc:title est une valeur alternative de langue enveloppée dans rdf:Alt, tandis que dc:creator est une liste ordonnée utilisant rdf:Seq ; exporter l'un de ces éléments sous forme de nœud de texte brut est l'erreur XMP la plus courante, tolérée par la plupart des lecteurs jusqu'à ce qu'ils rencontrent un lecteur strict. Les préfixes d'espace de noms sont des conventions, mais les URI auxquels ils sont liés sont normatifs : l'analyseur effectue sa recherche par rapport à l'URI, pas au préfixe
Deux faits de coordonnées pilotent l'ensemble de la boucle. Le PDF mesure y vers le haut depuis le coin inférieur gauche, de sorte que les lignes descendent le long de la page en soustrayant RowStep de Y à chaque fois, et le test de page pleine est déclenché lorsque Y descend en dessous de la marge inférieure, plutôt que de dépasser une valeur supérieure. Inversez la direction, et la première ligne s'imprimera au-delà du bord inférieur de la page, tandis que la boucle pensera qu'il reste encore toute la page de libre
Les signets et les liens pointent vers des destinations, et une destination n'est pas seulement un numéro de page. C'est un tableau désignant un objet de page, puis spécifiant comment le visualiseur doit le présenter via le verbe dans le second emplacement. Le plus courant et le plus sujet aux abus est /XYZ, sous la forme [page /XYZ left top zoom]. Ses trois opérandes sont indépendants, et chacun d'eux peut être nul, signifiant "conserver les paramètres d'origine du lecteur". Ainsi, [page /XYZ null null null] saute à cette page sans toucher à la position de défilement ni au niveau de zoom, ce qui est généralement le comportement attendu d'un lien "aller à la page". Ces coordonnées sont dans l'espace utilisateur par défaut, mesurées depuis le coin inférieur gauche, l'axe y augmentant vers le haut, le même système de coordonnées utilisé par le contenu de la page. Les auteurs habitués aux mises en page sur écran mesurent généralement depuis le haut, ce qui envoie le lecteur au mauvais bout de la page
Ce que le visualiseur affiche sous forme de panneau de signets est en réalité une arborescence doublement liée de dictionnaires au sein du fichier, appelée contour du document. Le catalogue pointe vers le dictionnaire de contour racine via /Outlines ; le nœud racine pointe vers ses première et dernière entrées de premier niveau ; chaque entrée est liée à ses voisines et à son nœud parent. Le tableau de signets n'existe nulle part en tant que tel, toute la structure étant reconstruite en suivant les références — c'est pourquoi un lien rompu peut faire disparaître une branche entière du panneau sans qu'aucun message d'erreur ne s'affiche
8 0 obj % the outline root
<< /Type /Outlines /Count 4 /First 9 0 R /Last 9 0 R >>
endobj
9 0 obj % top-level: a chapter
<< /Title (Chapter 1: Results)
/Parent 8 0 R /Count 2
/First 12 0 R /Last 15 0 R >>
endobj
12 0 obj % first child
<< /Title (Introduction)
/Parent 9 0 R /Next 15 0 R
/Dest [3 0 R /XYZ 72 720 0] >>
endobj
15 0 obj % second child, last sibling
<< /Title (Methodology)
/Parent 9 0 R /Prev 12 0 R
/Dest [3 0 R /Fit] >>
endobj
Une fois ces liens compris, les contraintes deviennent claires. Chaque entrée renvoie à son nœud parent via /Parent. Les nœuds frères forment une chaîne via et /Prev, le premier omettant /Prev et le dernier /Next. Le parent nomme ses premier et dernier enfants via /Next/First et , et les enfants intermédiaires ne sont accessibles qu'en parcourant la chaîne des frères. Si l'un d'eux est erroné, l'échec est silencieux : un /Next obsolète tronquera un chapitre, un parent dont le /Last ne termine pas correctement la chaîne isolera les entrées, et le visualiseur ne restituera que ce qu'il peut atteindre/Last
L'état transporté par le champ est souvent surprenant. Sur le nœud racine et tout élément développé, il enregistre le nombre de descendants actuellement visibles ; sur un élément réduit, il s'agit d'un nombre négatif dont la valeur absolue est le nombre de descendants qui apparaîtraient s'il était développé. Ainsi, /Count n'est pas un fait structurel fixe de l'arbre, mais plutôt l'état d'expansion ou de réduction enregistré du panneau. Un générateur qui code en dur le total positif redéveloppera chaque branche que l'auteur souhaitait garder réduite/Count
Chaque entrée tire son sens de ce vers quoi elle pointe. /Title est le contenu affiché dans le panneau ; /Dest est la destination de navigation lors d'un clic. La destination peut être intégrée directement dans l'entrée comme ci-dessus, ou être un nom résolu via le dictionnaire de noms du document — cette dernière option étant préférable lorsque de nombreux signets et liens pointent vers le même endroit, car déplacer la cible ne nécessite qu'une seule modification. Les bibliothèques masquent généralement cet arbre derrière un descripteur d'outline racine et des méthodes d'ajout d'entrées enfants ; dans HotPDF, le document expose le contour via OutlineRoot de type THPDFDocOutlineObject, et maintient automatiquement les liens /Prev, /Next, /Parent et /Count lorsque vous ajoutez des entrées. Il convient d'en tirer pleinement parti, car maintenir manuellement ces contraintes lors de l'édition est précisément la source des ruptures d'outline
Destination : syntaxe pour le saut au clic
C'est précisément ce que le visualiseur d'un utilisateur final ne verra pas, et c'est pourquoi il est utile de le savoir. Si le document est principalement de gauche à droite avec un seul bloc de droite à gauche, le premier appel à RtLTextOut modifiera la préférence de l'ensemble du fichier, et rien ne s'affichera sur votre exemplaire d'essai d'une seule page. Les symptômes apparaîtront quelques semaines plus tard — lorsque quelqu'un imprimera un livret recto-verso et que les pages en regard seront inversées. Si ce n'est pas ce que vous souhaitez, configurez explicitement la direction en sens inverse après le segment de texte de droite à gauche :
La série privilégie une plus grande robustesse au détriment d'un positionnement précis. /Fit[page /Fit] ajuste toute la page à la fenêtre, [page adapte la largeur de la page avec le bord supérieur donné, et /FitH top][page /FitR l b r t] agrandit un rectangle pour remplir la vue. Étant donné que ces destinations calculent le taux de zoom à partir de la géométrie de la page plutôt que de coordonnées fixes, la destination /Fit fait toujours ce qu'il faut après un changement de taille de la page, alors que les destinations avec un taux de zoom codé en dur peuvent laisser le lecteur regarder les marges. Pour une table des matières, un /FitH avec les coordonnées du haut de la section résiste mieux au temps qu'un /XYZ avec un taux de zoom deviné/XYZ
Annotations : tous les éléments interactifs ne faisant pas partie du contenu de la page
Les annotations sont des objets superposés sur la page mais ne faisant pas partie de son flux de contenu. Les liens, les notes autocollantes, les surbrillances, les contrôles de formulaires, les icônes de pièces jointes, les tampons — tout cela constitue des annotations, listées dans le tableau /Annots de leur page. Supprimer une annotation de ce tableau la retire de la page, même si le contenu sous-jacent reste inchangé. C'est le point clé de sa conception : l'annotation est une couche d'édition indépendante du contenu qu'elle recouvre
Chaque annotation possède une structure commune. /Subtype nomme le type, /Rect donne son rectangle de délimitation en coordonnées de page, et /Contents contient le texte qui sert également de description d'accessibilité. L'annotation de lien est le cas le plus intéressant car elle a deux formes : la destination brute et l'action
12 0 obj % link to a destination
<< /Type /Annot /Subtype /Link
/Rect [100 200 300 250]
/Border [0 0 0]
/Dest [5 0 R /XYZ null null null] >>
endobj
13 0 obj % link that runs an action
<< /Type /Annot /Subtype /Link
/Rect [50 50 200 100]
/Border [0 0 0]
/A << /Type /Action /S /URI /URI (https://www.example.com) >> >>
endobj
/Rect est la zone active ; cliquer dessus envoie le lecteur vers la destination, en réutilisant la même syntaxe que celle utilisée par les contours. /Border [0 0 0] fait un travail important en masquant le rectangle par défaut laid que le visualiseur dessine autour des liens. La seconde forme remplace le simple /Dest par une action /A, dont le sous-type /S sélectionne le comportement : pour sauter à l'intérieur de ce fichier, /GoTo/GoToR pour sauter vers un autre fichier, pour ouvrir un site web, et /URI pour exécuter un programme externe. Ce dernier doit être traité avec prudence. Le /Launch qui démarre un exécutable est précisément ce qui fait du PDF un vecteur de logiciels malveillants ; les visualiseurs conformes le bloqueront ou afficheront un avertissement très visible, et le lien cessera de fonctionner pour la plupart des lecteurs. Veuillez utiliser /URI et /GoTo, et éviter /Launch/Launch
Les annotations de balisage comme les surbrillances et les notes, ainsi que les annotations de formes telles que /Square, présentent une complexité supplémentaire : leur apparence à l'écran n'est pas implicitement dictée par leur type. À moins de fixer l'apparence à l'aide d'un flux d'apparence (entrée , référençant un XObject de formulaire contenant les opérateurs de tracé), le visualiseur effectuera le rendu à sa guise. Omettez cette entrée, et la même surbrillance pourra s'afficher différemment d'un lecteur à l'autre, ou après un aller-retour dans un éditeur. Pour tout contenu dont l'apparence exacte fait partie du document, fournissez l'entrée /AP. Les pièces jointes réutilisent le même mécanisme : le flux de fichier intégré et le dictionnaire de spécification de fichier, présentés via l'annotation /AP/FileAttachment ou l'arbre de noms /EmbeddedFiles sous le catalogue /Names
Où cette couche échoue-t-elle et comment le détecter
Le mode d'échec récurrent dans tous ces problèmes est la présence de références pendantes. Les signets cessent de s'afficher lorsque le catalogue n'a pas d'entrée /Outlines ou si la chaîne de fratrie est rompue au milieu de l'arbre ; les métadonnées sont ignorées si le flux XMP n'a pas les balises /Type /Metadata /Subtype /XML ou si l'enveloppe xpacket est mal formée. Dans chaque cas, le contenu de la page est correct, l'ouverture simple semble correcte, le défaut ne se manifestant que dans ce panneau que personne n'a vérifié
Les deux règles d'or du stockage exigent la cohérence. Si /Info indique que l'auteur est une personne et que dc:creator indique qu'il s'agit d'une autre, vous publiez un document qui donne deux réponses différentes à la même question, et la réponse finale dépend de l'outil utilisé pour lire le champ. Les bibliothèques écrivent généralement les deux pour vous, mais dès que vous modifiez manuellement l'un d'eux ou fusionnez des fichiers provenant de générateurs différents, des écarts apparaissent. Traitez le dictionnaire Info comme un héritage de compatibilité ascendante, en utilisant XMP comme source de vérité et en régénérant les deux à partir du même ensemble de valeurs, plutôt qu'en les corrigeant indépendamment. Pour PDF/A, cela devient une exigence de conformité : la norme ISO 19005 impose XMP et interdit tout attribut Info contredisant le champ XMP correspondant