构建支持自动分页的多页文档
重点速览
标题为“重点速览”的章节构建一份内容横跨多页的文档。在生成过程中逐步加入内容。启用 setAutoPageBreak() 后,当光标抵达底部边距时,排版引擎会自动为你开启新页。调用 save() 后,可用 getNumPages() 读取最终页数。本示例基于 examples/05-multi-page.php。
在 save() 期间,引擎会把每一页的绘制指令写入一个内容流。ISO 32000-2 §7.7.3.3 将一页的 Contents 定义为单一流,或按顺序串联的流数组。因此,多页输出是一连串页对象,而不是单一缓冲区。
composer require nextpdf/core:^3无需任何可选扩展。本示例可在 PHP 8.1–8.4 的 backport 兼容矩阵上运行。getNumPages() 与 setAutoPageBreak() 自 1.0.0 起即为稳定 API。
概念总览
标题为“概念总览”的章节每份 NextPDF 文档都是一棵页面树(page tree)。随着你添加内容,内部光标(getY())会继续推进。启用自动分页后,引擎会在每个内容块之前检查剩余的垂直空间。如果该块无法放入底部边距上方的空间,引擎会刷写当前页,并为你调用 addPage()。传给 setAutoPageBreak() 的底部边距就是触发阈值。
页级属性(例如 media box)可以继承。ISO 32000-2 §7.7.3.4 规定,页对象中省略的属性会从上层页面树节点解析(resolve)取得。NextPDF 会在整份文档中应用一致的页面尺寸,因此每一页生成时都共享相同几何,你不必逐页重复设置。
API 接口
标题为“API 接口”的章节API 接口由 PHPDoc 自动生成。本示例会用到下列方法:
Document::createStandalone(): self— 创建一份独立的文档。setAutoPageBreak(bool $enabled, float $margin = 20): static— 启用自动分页。$margin是以毫米为单位的底部边距触发值。addPage(?PageSize $size = null, Orientation $orientation = Orientation::Portrait): static— 创建第一页,以及任何显式新增的页面。multiCell(...): static/cell(...): static— 输出可流动或固定的文本块。分页检查会测量这些块。getNumPages(): int— 排版后的页数。
代码示例 — 快速上手
标题为“代码示例 — 快速上手”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setAutoPageBreak(true, margin: 25);$doc->addPage();
$doc->setFont('helvetica', '', 11);for ($i = 1; $i <= 60; $i++) { $doc->multiCell(0, 7, "Line {$i}: content flows until the page is full, " . 'then the engine starts a new page automatically.');}
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf');echo 'Pages: ' . $doc->getNumPages() . "\n";代码示例 — 生产环境
标题为“代码示例 — 生产环境”的章节这是完整示例,可直接配合 harness(测试装置)运行。它会使用 harness 设置的 NEXTPDF_COOKBOOK_OUTPUT,所以不要把 PDF 打印到 STDOUT。它本身不会固定任何熵源(entropy)。当 harness 运行它时,DeterministicMode 会固定时钟、/ID 与品牌信息。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Multi-Page Document');
// Enable automatic page breaks. The 25 mm bottom margin is the trigger:// when the cursor would cross it, the engine flushes the page and adds// a new one before the next block is drawn.$doc->setAutoPageBreak(true, margin: 25);$doc->addPage();
$doc->setFont('helvetica', 'B', 18);$doc->cell(0, 12, 'Multi-Page Document Example', newLine: true);$doc->ln(5);
for ($chapter = 1; $chapter <= 3; $chapter++) { $doc->setFont('helvetica', 'B', 14); $doc->cell(0, 10, "Chapter {$chapter}: Lorem Ipsum", newLine: true); $doc->setFont('helvetica', '', 11);
for ($para = 1; $para <= 5; $para++) { $text = "Paragraph {$para} of Chapter {$chapter}. " . 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' . 'Sed do eiusmod tempor incididunt ut labore et dolore magna ' . 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation ' . 'ullamco laboris nisi ut aliquip ex ea commodo consequat.'; $doc->multiCell(0, 7, $text); $doc->ln(3); } $doc->ln(5);}
// The harness sets NEXTPDF_COOKBOOK_OUTPUT; honour it. STDOUT stays free// for progress text only.$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf';$doc->save($out);
echo 'Created multi-page.pdf with ' . $doc->getNumPages() . " pages\n";边界情形与陷阱
标题为“边界情形与陷阱”的章节- 禁用自动分页。 使用
setAutoPageBreak(false, …)时,超过底部边距的内容会被裁剪在该页上,而不会继续流动,文档也会保持为单页。若内容需要流动,请启用它。 - 单一块高于整页。 引擎会在内部拆分文本超过可打印高度的
multiCell。但如果是一个高于可用区域且不可分割的单一块(例如一张很高的图像),它只会被放置一次并发生溢出。此时请自行手动分页。 - 首次调用
addPage()仍然必要。 当还没有任何页面时,cell()会按需调用addPage()。即便如此,仍请显式调用addPage(),以确保第一页的尺寸和方向是确定的。 - 边距单位。 在默认单位系统下,
setAutoPageBreak()的边距以毫米为单位,而不是以点(point)为单位。
getNumPages() 是 O(1) 操作。它只读取一个计数器,不会重新排版。内存用量随保留的内容量而增长,而不是随页数增长。引擎会在每一页完成时,将已完成页面刷写到输出缓冲区 — 这就是单趟(single-pass)流式模型(ADR-001)。2000 ms / 64 MB 的预算足以在参考主机上处理包含数百页文本的文档。
安全说明
标题为“安全说明”的章节本示例只会写入你代码提供的文本。它不会进行任何输入解析、网络访问或反序列化。请将任何来自外部来源的文本都视为不受信任,并在渲染之前先限制其长度。引擎不会代你施加应用级别的内容大小上限。
符合性
标题为“符合性”的章节| 陈述 | 规范 | 条款 | 参考 ID |
|---|---|---|---|
一页的 Contents 是单一流,或按顺序串联的流数组。 | ISO 32000-2 | §7.7.3.3 | |
| 页对象中省略的可继承页面属性,会从上层页面树节点解析取得。 | ISO 32000-2 | §7.7.3.4 | |
trailer 的 /ID 是由两个字节字符串组成的文件标识符(在 PDF 2.0 中为必填)。 | ISO 32000-2 | §7.5.5 |
可复现性配置 — 结构级别(为什么不是逐字节)。 每一份保存的文档都带有一个 trailer /ID,其两个字节字符串即为文件标识符(ISO 32000-2 §7.5.5,如上)。第二个元素并非跨运行稳定,因此即使内容完全相同,每次运行的原始字节仍会不同。harness 会比对经过 qpdf 规范化的结构;该流程会剥除 /ID、/CreationDate 与 /ModDate。本示例描述的是 NextPDF 如何生成这个结构。它并未笼统宣称符合 ISO 32000-2。
商业情境
标题为“商业情境”的章节不适用。支持自动分页的多页排版属于 Core 能力,不受 Premium 限制。