跳转到内容
getnextpdf.com

图像、表格与列表

只有文本很难构成一份完整的文档。在本教程里,你会加入三个日常常用的组成部分:一张图片、一个表格和一个项目符号列表。你会写两段简短的脚本, 每一段都会生成一个可以立即打开的真实 PDF(Portable Document Format,便携式文档格式)文件。

  • 01-image.php —— 一个单页 PDF,包含一个标题和一张琥珀色方块图片, 放置在页面上的精确位置。
  • 02-table-and-lists.php —— 一个单页 PDF,包含一个小的书籍表格, 下面还有一个项目符号列表。

这两段脚本都是完整的程序。继续使用 第一个教程里的项目文件夹, 并把每段脚本保存在 composer.json 旁边。

这段脚本自带图片。一张很小的 8 x 8 像素 PNG(Portable Network Graphics,便携式网络图形)图片以 Base64 字符串的形式嵌在脚本内部。 Base64 是二进制数据的纯文本写法,因此它可以放进源代码里。在真实项目中你会跳过这一步,因为你的图片文件已经存在于磁盘上。

把它保存为 01-image.php,并用 php 01-image.php 运行:

<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// A tiny 8 x 8 pixel amber PNG, stored as text inside this script.
// In a real project the image file already exists on disk, so you
// would skip this block and point image() at your own file.
$pngBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAEUlEQVR42mP4Oo8bK2IYWhIAq6BngWh/dt4AAAAASUVORK5CYII=';
@mkdir(__DIR__ . '/out');
$pngPath = __DIR__ . '/out/amber-square.png';
$pngBytes = base64_decode($pngBase64, true);
if ($pngBytes === false) {
fwrite(STDERR, "The embedded image data is damaged.\n");
exit(1);
}
file_put_contents($pngPath, $pngBytes);
$document = Document::createStandalone();
$document->setTitle('Tutorial: images');
$document->addPage();
$document->setFont('helvetica', 'B', 20);
$document->cell(0, 26, 'A picture inside a PDF', newLine: true);
$document->setFont('helvetica', '', 12);
$document->cell(0, 18, 'The amber square below comes from a tiny PNG file.', newLine: true);
// Place the image 72 points (one inch) from the left edge and
// 160 points down from the top, printed as a 96 x 96 point square.
$document->image($pngPath, x: 72, y: 160, width: 96, height: 96);
$document->save(__DIR__ . '/out/image.pdf');
echo "Wrote out/image.pdf\n";

你应该会在屏幕上看到 Wrote out/image.pdf。打开 out/image.pdf, 你会看到标题、一行文本和一个琥珀色方块。

  • 脚本首先把 Base64 字符串解码回图片字节,并写入 out/amber-square.png。这只是让教程自成一体的小技巧。真正要学的内容从 image() 调用开始,它会以同样的方式读取任何已有的 PNG 或 JPEG(Joint Photographic Experts Group,联合图像专家组)文件。
  • @mkdir(__DIR__ . '/out') 会创建输出文件夹。@ 符号会隐藏当文件夹已存在时你会看到的无害警告,这样脚本就能连续运行两次。
  • image() 接收一个文件路径,外加一个位置和一个打印尺寸。位置和尺寸都以点(point)为单位,72 点等于 1 英寸。页面原点是左上角,所以 y: 160 表示从顶部向下 160 点。
  • 像素和点是两回事。这张图片只有 8 x 8 像素,但 width: 96, height: 96 会把它打印成一个宽约一又三分之一英寸的方块。 打印尺寸由你决定,而不是由像素数量决定。
  • image() 会把图片放在你指定的精确位置。它不会移动 cell() 所使用的书写位置。先写文本,再放置图片,可以避免两者相互冲突。
  • NextPDF 只从本地文件读取图片,绝不会从网址读取。在嵌入之前,它会检查该文件确实是一张受支持的真实图片。当文件名来自用户时,这道检查会保护你。

第 2 步:添加一个表格和一个项目符号列表

标题为“第 2 步:添加一个表格和一个项目符号列表”的章节

表格和列表是结构化内容。描述结构最快的方式是 HTML(Hypertext Markup Language,超文本标记语言),也就是网页所用的那种标记。NextPDF 会把一组受支持的 HTML 直接渲染到 PDF 页面上。整个过程不涉及浏览器。

把它保存为 02-table-and-lists.php,并用 php 02-table-and-lists.php 运行:

<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
@mkdir(__DIR__ . '/out');
$document = Document::createStandalone();
$document->setTitle('Tutorial: tables and lists');
$document->addPage();
$document->setFont('helvetica', 'B', 20);
$document->cell(0, 26, 'A reading list, two ways', newLine: true);
$document->ln(8);
$html = <<<'HTML'
<h2>The books as a table</h2>
<table border="1" cellpadding="6" style="width: 100%;">
<thead>
<tr style="background-color: #1E3A8A; color: #FFFFFF;">
<th style="width: 50%;">Title</th>
<th style="width: 20%; text-align: center;">Year</th>
<th style="width: 30%; text-align: right;">Pages</th>
</tr>
</thead>
<tbody>
<tr>
<td>The Paper Office</td>
<td style="text-align: center;">2019</td>
<td style="text-align: right;">312</td>
</tr>
<tr style="background-color: #F8FAFC;">
<td>Ink and Pixels</td>
<td style="text-align: center;">2023</td>
<td style="text-align: right;">208</td>
</tr>
</tbody>
</table>
<h2>Why these two books</h2>
<ul>
<li>Short chapters that fit into a lunch break</li>
<li>Worked examples on every page</li>
<li>No jargon without an explanation</li>
</ul>
HTML;
$document->writeHtml($html);
$document->save(__DIR__ . '/out/table-and-lists.pdf');
echo "Wrote out/table-and-lists.pdf\n";

你应该会看到 Wrote out/table-and-lists.pdf。这个 PDF 里有一个标题、 一个带深色表头行的三列表格,以及一个项目符号列表。

  • 这段脚本在同一页上混用了两种书写方式。cell() 在当前书写位置打印出大标题。writeHtml() 接着在标题下方把表格和列表沿页面向下排布。
  • ln(8) 会在标题和 HTML 内容之间加入一个八点高的小竖直间隙。没有它, 两者就会紧贴在一起。
  • 这个表格使用普通的表格标记:<thead> 表示表头行,<tbody> 表示数据行,<th> 表示表头单元格,<td> 表示数据单元格。border="1" 会画出网格线,cellpadding="6" 会在每个单元格内部留出一点空间。
  • 列宽是百分比,例如 width: 50%。它们会按比例分配表格的整体宽度, 因此即使页面尺寸变化,布局也能保持不变。 布局 HTML 表格一文深入介绍了表头、表尾和列宽设置。
  • style 属性使用 CSS(Cascading Style Sheets,层叠样式表)声明, 也就是网页的样式语言。颜色、对齐和宽度都会按这里所示生效。在你依赖其他属性之前,请查阅 CSS 支持矩阵
  • 项目符号列表就是一个 <ul>,每一项对应一个 <li>。NextPDF 会替你画出项目符号和缩进。
  • 提到 ImageProcessingException 的错误表示图片文件无法读取或解码。 请检查路径,并确认该文件是受支持的格式,例如 PNG 或 JPEG。
  • 提到 PageLayoutException 的错误表示某个位置或尺寸被拒绝了。宽度和高度必须大于零,而且路径必须指向本地文件,而不是网址。
  • 这两种错误都在 引擎错误参考中有说明。
  • 如果表格渲染得不正常,请先检查标记。每个 <tr> 行和 <td> 单元格都需要各自的闭合标签。
  • 遇到其他任何问题,请从 疑难解答指南开始。

现在你的页面已经可以承载图片、表格和列表了。在 下一个教程里, 你会让文档从单页扩展到多页,并用页眉、页脚和页码把它整理得井井有条。 想更深入了解今天的主题,可以看 在文档中嵌入图片一文,它介绍了图片格式和尺寸设置规则。