跳转到内容

添加书签与目录

本 recipe(示例)会添加一份分层文档大纲,也就是书签。阅读器应用会把这些条目列在导航侧栏中,它们同时构成可点击的目录。嵌套深度由整数层级控制。本示例遵循 examples/12-bookmarks-and-toc.php

ISO 32000-2 将其称为文档大纲:由大纲项组成的树状层级,用作可视化目录。

Terminal window
composer require nextpdf/core:^3

无需任何可选扩展。书签 API 自 1.0.0 起即为稳定版,可在 8.1–8.4 backport 兼容矩阵上运行。

bookmark($title, $level, $y) 会添加一个绑定到当前页面的大纲项。这个绑定会使用当前 Y 坐标,也可以使用你显式指定的 Y 坐标。$level 用于设置嵌套深度:层级 0 是最顶层的章,层级 1 是隶属于最近一个层级 0 项的节,依此类推。引擎会替你构建大纲树。ISO 32000-2 在每一层使用 Prev/Next 串接各项,并通过 First/Last 形成嵌套,以 catalog 的 Outlines 项作为根节点。

每个项都带有一个目标位置,因此当你点击书签时,阅读器会跳转到正确页面。ISO 32000-2 §12.3.2 规定,目标位置可以与大纲项关联。同一次调用也会传给 NextPDF 的目录构建器,因此大纲与渲染出的目录会保持同步。

API 接口由 PHPDoc 自动生成。本示例只依赖一个方法:

  • bookmark(string $title, int $level = 0, float $y = -1): static — 在 $level 层级添加一个绑定到当前页面的大纲项。$y = -1 会使用当前光标的 Y 坐标。传入非负的 Y 值,即可固定精确的目标位置。
<?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');

下面是完整、可直接用于测试框架的示例。它会遵循 NEXTPDF_COOKBOOK_OUTPUT,并且自身不会引入任何不确定性。

<?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";
  • 层级跳跃。 从层级 0 直接跳到层级 2,且中间没有层级 1,会在某些阅读器中呈现出结构看似错误的层级。每次最多只将层级增加一级。
  • 在内容之前设置书签。 在你希望作为目标位置的地方调用 bookmark()。在大多数情况下,请把这个调用放在标题正前方。若把调用放在标题之后,目标位置会落在标题略下方。
  • 以显式的 Y 值获得精确目标位置。$y = -1 时,目标位置即为当前光标的 Y 坐标。传入一个显式的 Y 值,可获得稳定的目标位置。举例来说,显式的 Y 值能固定一节的顶端,无论它前面有多少内容都不受影响。
  • 大纲支持是普遍的,但呈现方式各异。 阅读器可能以折叠或展开状态渲染大纲。这个 API 无法强制每个项的展开或折叠状态。

每次 bookmark() 调用都会追加一个大纲项和一条目录条目,工作量为 O(1)。大纲树只会在 save() 时最终定型一次。数百个书签仍能轻松控制在 2000 ms / 64 MB 的预算内。

书签标题会渲染到阅读器的导航界面中。如果标题包含用户可控的数据,请像处理任何将被显示的字符串一样,对其进行长度限制和净化。本示例不会执行任何输入解析,也不会进行任何网络访问。

陈述规范条款参考 ID
文档大纲是一棵由大纲项组成的树,用作可视化目录。ISO 32000-2§12.3.3
大纲项通过 Prev/Next 串接,并通过 First/Last 形成嵌套。ISO 32000-2§12.3.3
大纲字典是 catalog 的 Outlines 项。ISO 32000-2§7.7.2
目标位置可以与大纲项关联。ISO 32000-2§12.3.2

可重现性配置 — 结构层级。 trailer 的 /ID 和日期原子每次保存都会变动,因此测试框架会剥除这些原子,再比对经 qpdf 规范化后的结构。本示例说明 NextPDF 如何产生这样的结构。它并不作出全面符合 ISO 32000-2 的笼统声明。

不适用。文档大纲和目录都是 Core 功能,没有 Premium 限制。