Parcours des tutoriels · Précédent : graphiques · Suivant : liens
But et prérequis
Créer trois pages, un plan hiérarchique de signets et des destinations nommées. Les tutoriels premier document et pages et texte expliquent déjà les ressources, les flux et l'arbre des pages. Installer les dépendances avec composer install ; PHP 8.3 ou supérieur et les extensions indiquées par Composer sont nécessaires.
Le script complet est examples/navigation.php. Chaque étape y est commentée ; il fonctionne seul et n'utilise aucun fichier produit par les autres tutoriels.
Exécuter et regarder
Depuis la racine d'un checkout du core :
php examples/navigation.php
# Destination personnalisée, dans un répertoire existant :
php examples/navigation.php navigation-demo.pdf
Sans argument, le fichier est examples/navigation.pdf. Ouvrir le PDF dans un lecteur disposant d'un panneau de signets. Le résultat contient trois chapitres. Le signet « Comprendre la navigation » contient trois enfants ; cliquer sur le deuxième affiche le deuxième chapitre à un zoom de 100 %. Les autres ajustent la page entière. Le catalogue demande l'ouverture du panneau, mais cette préférence dépend du lecteur.

L'aperçu est une image de la page ; les signets se trouvent dans la structure interactive du PDF, en dehors de son dessin.
Lire la construction
- Les pages et le plan sont deux arbres.
PageTreeNode::addKid()détermine l'ordre de lecture.OutlineDictionary::addItem()etOutlineItem::addItem()construisent un plan indépendant : on peut choisir une hiérarchie éditoriale différente de celle des pages. - Une destination vise un objet Page.
new Fit($page)construit le tableau[référence-à-la-page /Fit]. Le numéro de page affiché dans un lecteur découle de sa place dans l'arbre ; il ne sert pas d'identifiant à cette destination. XYZprécise la vue. Le script passe trois objetsNumericTypede valeurs 0, 842 et 1 : le point visé est le bord supérieur de la page et le zoom vaut 1, soit 100 %. Un paramètre PHPnulldevient un objet PDFnull, qui demande au lecteur de conserver ce paramètre de la vue courante. Pour le zoom, la valeur 0 a aussi ce sens dans la norme.- Les liens du plan sont déduits. Les objets de plan produisent
/Parent,/Prev,/Next,/First,/Lastet/Countà partir des rattachements. Les titres sont desTextString: ils peuvent ici contenir des accents, indépendamment de l'encodage du texte dessiné avec Helvetica. - Les destinations nommées offrent des clés stables. Le
NameTreeassociechapitre-1,chapitre-2etchapitre-3aux destinations explicites. Ses clés sont des chaînes d'octets, représentées parLiteralString, et sont triées par l'objet arbre. LeNameDictionaryl'expose sous/Dests, puis l'entrée de catalogueNamesl'expose sous/Names. - Le catalogue relie tout.
new Outlines(new IndirectReference($outline))respecte l'obligation de référence indirecte de cette entrée.PageMode::UseOutlinesdéfinit la préférence d'interface ;OpenAction($firstPage)définit la vue initiale.
Le script publie les destinations nommées pour montrer leur construction. Ses signets emploient directement Fit et XYZ ; la présence d'un arbre de noms ne transforme pas automatiquement ces cibles en destinations nommées.
Correspondance avec ISO 32000-1:2008
| Objet ou entrée | Rôle | Référence |
|---|---|---|
PageTreeNode, PageObject |
Arbre des pages | §7.7.3, tableaux 29 et 30 |
/Names, NameDictionary |
Arbres de noms accessibles au catalogue | §7.7.4, tableau 31 |
NameTree, NameTreeEntry |
Association de chaînes à des valeurs | §7.9.6 |
Fit, XYZ |
Destinations explicites | §12.3.2.2, tableau 151 |
/Names /Dests |
Destinations nommées | §12.3.2.3 |
OutlineDictionary, OutlineItem |
Plan du document | §12.3.3, tableaux 152 et 153 |
/PageMode, /OpenAction, /Outlines |
Préférences et accès depuis le catalogue | §7.7.2, tableau 28 |
Ces références désignent la norme conservée dans le core, également utilisée par les définitions générées.
Variantes et limites
Passer expanded: false au constructeur d'un OutlineItem possédant des enfants pour le présenter fermé initialement. Remplacer Fit par XYZ pour préciser la vue ; les autres classes du namespace PDF\Document\Destinations représentent les modes d'ajustement de la norme.
Pour activer une destination nommée depuis une annotation, utiliser un DestEntry(new LiteralString('chapitre-2')) sur un document dont le catalogue publie cette clé sous /Names /Dests. Le tutoriel suivant montre la construction des annotations et la version avec une destination explicite.
Le plan n'écrit pas de sommaire visible dans les pages. Un sommaire imprimable demande des opérateurs de texte et, éventuellement, des annotations de lien. Les réglages de zoom et l'ouverture du panneau sont interprétés par chaque lecteur.
Retour au parcours · Continuer : liens internes et externes
Voir le résultat PDF
Ce fichier a été généré avec le code de cette documentation. Toutes ses pages sont reproduites ci-dessous ; ouvrir le PDF pour utiliser ses liens et ses signets.
Utiliser le lecteur PDF du navigateur
Ce lecteur dépend du navigateur. Si le document ne s’affiche pas, utiliser « Ouvrir le PDF » ou « Télécharger ».
Code source complet
Code de roukmoute/pdf · Consulter la source au commit documenté ↗
Lire le script commenté : navigation.php
<?php
declare(strict_types=1);
use PDF\Document\Destinations\Fit;
use PDF\Document\Destinations\XYZ;
use PDF\Document\Fonts\StandardFont;
use PDF\Document\Fonts\Type1Font;
use PDF\Document\OutlineDictionary;
use PDF\Document\OutlineItem;
use PDF\Document\PageMode;
use PDF\Document\ResourceDictionary;
use PDF\Document\Resources\Font;
use PDF\FileStructure;
use PDF\Syntax\ContentStreamsResources\ContentStreams;
use PDF\Syntax\ContentStreamsResources\Operators\BeginTextObject;
use PDF\Syntax\ContentStreamsResources\Operators\EndTextObject;
use PDF\Syntax\ContentStreamsResources\Operators\MoveTextPosition;
use PDF\Syntax\ContentStreamsResources\Operators\SetTextFontAndSize;
use PDF\Syntax\ContentStreamsResources\Operators\ShowText;
use PDF\Syntax\DataStructure\NameTree;
use PDF\Syntax\DataStructure\NameTreeEntry;
use PDF\Syntax\DataStructure\Rectangle;
use PDF\Syntax\DataStructure\TextString;
use PDF\Syntax\DocumentStructure\CatalogDictionary\Names;
use PDF\Syntax\DocumentStructure\CatalogDictionary\OpenAction;
use PDF\Syntax\DocumentStructure\CatalogDictionary\Outlines;
use PDF\Syntax\DocumentStructure\CatalogDictionary\PageMode as CatalogPageMode;
use PDF\Syntax\DocumentStructure\DocumentCatalog;
use PDF\Syntax\DocumentStructure\NameDictionary;
use PDF\Syntax\DocumentStructure\PageTree\PageObject;
use PDF\Syntax\DocumentStructure\PageTree\PageTreeNode;
use PDF\Syntax\FileStructure\FileBody;
use PDF\Syntax\FileStructure\FileHeader;
use PDF\Syntax\Object\DictionaryObject\Entry;
use PDF\Syntax\Object\DictionaryType;
use PDF\Syntax\Object\IndirectReference;
use PDF\Syntax\Object\NameType;
use PDF\Syntax\Object\NumericType;
use PDF\Syntax\Object\StreamObject;
use PDF\Syntax\Object\StringObject\LiteralString;
require dirname(__DIR__) . '/vendor/autoload.php';
// 1. All three pages share the same font and resource dictionary.
// /F1 is a local name used by Tf, not the font name.
$font = new Type1Font(StandardFont::Helvetica);
$resources = new ResourceDictionary(new Font(new DictionaryType(
new Entry(new NameType('F1'), new IndirectReference($font)),
)));
$mediaBox = new Rectangle(new NumericType(0), new NumericType(0), new NumericType(595), new NumericType(842));
$pages = new PageTreeNode();
$chapterPages = [];
// 2. addKid() order is reading order. Content strings remain
// ASCII here; TextString will be used for bookmark titles.
foreach (['Le graphe du document', 'Les destinations', 'Les arbres de noms'] as $index => $chapter) {
$contents = new StreamObject(new ContentStreams(
new BeginTextObject(),
new SetTextFontAndSize(new NameType('F1'), new NumericType(22)),
new MoveTextPosition(new NumericType(54), new NumericType(770)),
new ShowText(new LiteralString('Chapitre ' . ($index + 1) . ' : ' . $chapter)),
new SetTextFontAndSize(new NameType('F1'), new NumericType(12)),
new MoveTextPosition(new NumericType(0), new NumericType(-40)),
new ShowText(new LiteralString('Ouvrez le panneau des signets pour naviguer entre les pages.')),
new MoveTextPosition(new NumericType(0), new NumericType(-26)),
new ShowText(new LiteralString('Les signets referencent les objets Page, pas des numeros de page.')),
new EndTextObject(),
));
$page = new PageObject($mediaBox, $resources, $contents);
$pages->addKid($page);
$chapterPages[] = $page;
}
// 3. /Fit fits the whole page. /XYZ selects a point and zoom (1 = 100%).
// These destination arrays contain an indirect reference to the Page.
$firstPage = new Fit($chapterPages[0]);
$secondPage = new XYZ($chapterPages[1], new NumericType(0), new NumericType(842), new NumericType(1));
$thirdPage = new Fit($chapterPages[2]);
// 4. An outline (Outlines) is a hierarchy separate from the page tree.
// /Parent, /Prev, /Next, /First, /Last and /Count are derived from
// attachments. Each item must belong to only one parent.
$outline = new OutlineDictionary();
$overview = new OutlineItem(new TextString('Comprendre la navigation'), $firstPage);
$overview->addItem(new OutlineItem(new TextString('1. Le graphe du document'), $firstPage));
$overview->addItem(new OutlineItem(new TextString('2. Les destinations - zoom à 100 %'), $secondPage));
$overview->addItem(new OutlineItem(new TextString('3. Les arbres de noms'), $thirdPage));
$outline->addItem($overview);
// 5. /Names /Dests associates a stable string with an explicit destination.
// A NameTree key is a byte string, not a NameType (/Nom).
// NameTree sorts its keys; links can then target a key without
// knowing the chapter's position in the page tree.
$destinations = new NameTree(
new NameTreeEntry(new LiteralString('chapitre-3'), $thirdPage),
new NameTreeEntry(new LiteralString('chapitre-1'), $firstPage),
new NameTreeEntry(new LiteralString('chapitre-2'), $secondPage),
);
$names = new NameDictionary(new Entry(new NameType('Dests'), new IndirectReference($destinations)));
// 6. The catalog makes outlines and destinations accessible. /Outlines
// requires an indirect reference. /PageMode requests bookmark display;
// readers remain free to honor this interface preference.
$catalog = new DocumentCatalog(
$pages,
new Outlines(new IndirectReference($outline)),
new Names($names),
new CatalogPageMode(PageMode::UseOutlines),
new OpenAction($firstPage),
);
$file = new FileStructure(new FileHeader('1.7'), new FileBody($catalog), new IndirectReference($catalog));
$path = $argv[1] ?? __DIR__ . '/navigation.pdf';
$file->writeTo($path);
echo $path . PHP_EOL;


