跳转到内容
getnextpdf.com

页眉、页脚与多页文档

一页往往不够用。在本教程中,你将编写一份能自行扩展到三页的文档。你还会为每一页添加一个重复出现的页眉,以及一个显示页码的页脚。

你将编写一个脚本 01-multipage.php。它会生成一份三页的旅行日志,输出为 PDF(Portable Document Format,可移植文档格式)文件:

  • 日志条目会自动在页面之间流动。你从不需要指定某一页在哪里结束;这由引擎来决定。
  • 页眉是页面顶部的一条区域,会在每一页上重复出现。你的页眉显示文档标题。
  • 页脚是与之对应的底部区域。你的页脚以 1 / 32 / 33 / 3 的形式显示页码。

所有内容都仅依靠 nextpdf/core 包运行:不需要字体文件,不需要额外扩展, 也不需要联网。

这个脚本靠两个概念完成全部工作。

第一个是自动分页。引擎在写入文本时,会跟踪一个光标:即页面上下一行将要落下的位置。setAutoPageBreak(true, margin: 25) 告诉引擎留意这个光标。当它接近底部边缘、距离在 25 点(约三分之一英寸)以内时,引擎就会关闭当前页并另起崭新的一页。

第二个是页面上的固定元素:页眉和页脚。你只需在第一页之前描述它们一次。 setHeaderData() 会为顶部区域记录标题和一段简短描述。字体与边距的调用则决定字样以及与页面边缘的距离。此后,引擎会在它创建的每一页上绘制这些元素,包括它自行添加的页面。

在你的项目文件夹中创建 01-multipage.php

<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// Make sure the output folder exists next to this script.
@mkdir(__DIR__ . '/out');
$document = Document::createStandalone();
$document->setTitle('NextPDF Travel Journal');
// Configure the header and footer BEFORE the first page.
// The engine repeats them on every page for you.
$document->setHeaderData(title: 'NextPDF Travel Journal', description: 'A multi-page tutorial document');
$document->setHeaderFont('helvetica', 10);
$document->setHeaderMargin(5);
$document->setFooterFont('helvetica', 8);
$document->setFooterMargin(10);
// Turn on automatic page breaks. When the text cursor gets within
// 25 points of the bottom edge, the engine starts a new page.
$document->setAutoPageBreak(true, margin: 25);
$document->addPage();
$document->setFont('helvetica', 'B', 18);
$document->cell(0, 12, 'Travel journal: three days on the coast', newLine: true);
$document->ln(5);
$days = [
1 => 'We followed the shoreline north and counted seventeen lighthouses.',
2 => 'Rain moved in before noon, so we sketched the harbor from a cafe window.',
3 => 'On the last morning the fog lifted and the whole bay turned silver.',
];
foreach ($days as $day => $highlight) {
$document->setFont('helvetica', 'B', 14);
$document->cell(0, 10, "Day {$day}", newLine: true);
$document->setFont('helvetica', '', 11);
for ($entry = 1; $entry <= 12; $entry++) {
$text = "Entry {$entry} of day {$day}. {$highlight} "
. 'The trail hugged the cliffs for most of the afternoon, and every '
. 'turn opened another view of the water. We stopped often to take '
. 'notes, compare maps, and argue happily about where to eat dinner. '
. 'By the time we reached the guesthouse, our boots were soaked and '
. 'our notebooks were full.';
$document->multiCell(0, 7, $text);
$document->ln(3);
}
$document->ln(5);
}
// Ask the engine how many pages the layout produced, then save.
$pages = $document->getNumPages();
$document->save(__DIR__ . '/out/travel-journal.pdf');
echo "Wrote out/travel-journal.pdf with {$pages} pages\n";

php 01-multipage.php 运行它。脚本会打印出一行:

Wrote out/travel-journal.pdf with 3 pages

打开 out/travel-journal.pdf 并滚动浏览。标题区域位于全部三页的顶部, 页码则在右下角依次递增。

写入循环从未提到页面。它用 cell() 写出每一天的标题,cell() 会把一行放进一个不可见的方框里。它用 multiCell() 写出各条目,multiCell() 会把长文本按需换行到任意多行。三十六个条目无法容纳在一页上,因此自动分页触发了两次。日志正是这样变成了恰好三页。

页眉和页脚是在第一次调用 addPage() 之前配置的。这个顺序很重要:已经绘制好的页面,之后就不会再更新。先把这些固定元素配置好,每一页才会保持一致。

你从未亲手绘制页码。页脚默认就会打印它;你只是选择了它的字体以及与页面边缘的距离。引擎在写入各页时,还无法知道最终的页数。因此它会在每个页脚里留下一个占位符,等你保存时再填入真正的总数。

还有两个小细节让脚本更加完整。@mkdir 会创建 out 文件夹,而 @ 符号能在该文件夹已存在时让 PHP 保持安静。getNumPages() 会向引擎询问版面生成了多少页,这样最终的消息就能报告真实的页数。

  • 如果 require 那一行报出 “failed to open stream” 错误,说明脚本找不到 vendor/autoload.php。请在包含 vendor/ 的项目文件夹内运行它。
  • 所有内容都挤在一页上,而且文本末尾被截断了?那说明自动分页被关闭了。 请保留 setAutoPageBreak(true, margin: 25) 这一行,并确保它位于写入循环的上方。
  • 字体族名拼写错误会抛出一个异常,其中会指出它找不到的那个字体。请检查 helvetica 的拼写,遇到更深层的字体问题可参阅 字体与标记
  • 其他任何情况下,疑难排解中心会把症状对应到原因,而错误参考则解释了引擎的通用异常,以及如何从每一种异常中恢复。

现在你的文档可以扩展到任意长度,同时仍然显得完整。在 下一篇教程中,你将用 HTML(Hypertext Markup Language,超文本标记语言)编写内容,并让引擎把它转换成页面。

当你想对今天这些主题有更精细的控制时,有两篇 recipe(示例)讲得更深入: