Aller au contenu

Ajouter des signets et une table des matières

Cette recette ajoute au document un plan hiérarchique sous forme de signets. Le lecteur PDF affiche ces entrées dans sa barre latérale de navigation, où elles font aussi office de table des matières cliquable. Un niveau entier détermine l’imbrication. Cette recette suit examples/12-bookmarks-and-toc.php.

ISO 32000-2 appelle cela le plan du document : une hiérarchie arborescente d’éléments de plan qui sert de table des matières visuelle.

Fenêtre de terminal
composer require nextpdf/core:^3

Aucune extension optionnelle n’est nécessaire. L’API des signets est stable depuis la version 1.0.0 et fonctionne avec la matrice de backport 8.1–8.4.

bookmark($title, $level, $y) ajoute un élément de plan lié à la page courante. La liaison utilise la position Y courante, ou bien une position Y explicite. $level définit la profondeur d’imbrication : le niveau 0 est un chapitre de premier niveau, le niveau 1 est une section rattachée à l’élément de niveau 0 le plus récent, et ainsi de suite. Le moteur construit pour toi l’arbre du plan. Dans ISO 32000-2, les éléments sont chaînés via Prev/Next à chaque niveau et imbriqués via First/Last, avec l’entrée Outlines du catalogue comme racine.

Chaque élément contient une destination, ce qui permet au lecteur d’ouvrir la bonne page lorsque tu cliques sur le signet. ISO 32000-2 §12.3.2 indique que des destinations peuvent être associées aux éléments de plan. Le même appel alimente également le générateur de table des matières de NextPDF, afin que le plan et la table des matières rendue restent synchronisés.

La surface de l’API est générée automatiquement à partir du PHPDoc. Cette recette s’appuie sur une seule méthode :

  • bookmark(string $title, int $level = 0, float $y = -1): static — ajoute, au niveau $level, un élément de plan lié à la page courante. $y = -1 utilise la position Y courante du curseur. Passe une position Y non négative pour fixer une destination précise.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->addPage();
$doc->bookmark('Chapter 1', level: 0);
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Chapter 1', newLine: true);
$doc->bookmark('Section 1.1', level: 1); // nested under Chapter 1
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Section body.');
$doc->addPage();
$doc->bookmark('Chapter 2', level: 0);
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Chapter 2', newLine: true);
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/bookmarks.pdf');

Voici l’exemple complet, prêt à être exécuté par le harnais. Il respecte NEXTPDF_COOKBOOK_OUTPUT et n’introduit aucune entropie propre.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Bookmarks and Navigation');
$doc->setPrintHeader(false);
$doc->setPrintFooter(false);
$doc->setAutoPageBreak(true, margin: 25);
// Chapter 1
$doc->addPage();
$doc->bookmark('Chapter 1: Introduction', level: 0);
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Chapter 1: Introduction', newLine: true);
$doc->ln(3);
$doc->bookmark('What is NextPDF?', level: 1);
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, 'What is NextPDF?', newLine: true);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'NextPDF is a modern PDF 2.0 library for PHP 8.4+. '
. 'It generates standards-targeting documents with typography, '
. 'graphics, and signatures.');
$doc->ln(5);
$doc->bookmark('Key Features', level: 1);
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, 'Key Features', newLine: true);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'HTML rendering, barcodes, PAdES signatures, '
. 'and a worker-safe architecture.');
// Chapter 2
$doc->addPage();
$doc->bookmark('Chapter 2: Getting Started', level: 0);
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Chapter 2: Getting Started', newLine: true);
$doc->ln(3);
$doc->bookmark('Installation', level: 1);
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, 'Installation', newLine: true);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Install via Composer: composer require nextpdf/core');
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/bookmarks.pdf';
$doc->save($out);
echo "Created bookmarks.pdf with a 2-level outline\n";
  • Sauts de niveau. Un saut du niveau 0 au niveau 2 sans niveau 1 intermédiaire produit, dans certains lecteurs, une hiérarchie qui paraît malformée. Augmente le niveau d’au plus un à la fois.
  • Place le signet avant le contenu. Appelle bookmark() à l’endroit où tu veux la destination. Dans la plupart des cas, place l’appel juste avant le titre. Un appel placé après le titre fixe la destination légèrement en dessous de celui-ci.
  • Y explicite pour des destinations précises. Avec $y = -1, la destination est la position Y courante du curseur. Passe une position Y explicite pour obtenir une destination stable. Par exemple, une position Y explicite épingle le haut d’une section, quelle que soit la quantité de contenu qui la précède.
  • La prise en charge des plans est universelle, mais la présentation varie. Les lecteurs peuvent afficher le plan replié ou déplié. Cette API ne peut pas forcer l’état ouvert ou fermé de chaque élément.

Chaque appel à bookmark() ajoute un élément de plan et une entrée de table des matières, ce qui représente un travail en O(1). L’arbre du plan est finalisé une seule fois, au moment du save(). Des centaines de signets restent largement dans le budget de 2000 ms / 64 Mo.

Les titres des signets sont affichés dans l’interface de navigation du lecteur. Si un titre contient des données contrôlées par l’utilisateur, limite sa longueur et assainis-le comme tu le ferais pour n’importe quelle chaîne affichée. Cette recette n’effectue aucune analyse d’entrée et aucun accès réseau.

ÉnoncéSpécificationClausereference_id
Le plan du document est un arbre d’éléments de plan faisant office de table des matières visuelle.ISO 32000-2§12.3.3
Les éléments de plan se chaînent via Prev/Next et s’imbriquent via First/Last.ISO 32000-2§12.3.3
Le dictionnaire de plan est l’entrée Outlines du catalogue.ISO 32000-2§7.7.2
Des destinations peuvent être associées aux éléments de plan.ISO 32000-2§12.3.2

Profil de reproductibilité — structurel. Les atomes /ID et de date du trailer varient à chaque sauvegarde, donc le harnais retire ces atomes et compare la structure normalisée par qpdf. Cette recette décrit comment NextPDF produit cette structure. Elle ne présente pas la conformité ISO 32000-2 comme une revendication générale.

Non applicable. Le plan du document et la table des matières sont des fonctionnalités Core, sans verrou Premium.