将 HTML 渲染为 PDF 页面
重点速览
标题为“重点速览”的章节这则 recipe(示例)使用 writeHtml() 将一段 HTML 与 CSS 片段转换为 PDF 页面内容。你提供 markup(标记语言),它就会绘制出排好版的页面。完整、可直接运行的代码版本在 examples/08-html-basic.php。你可以按下面的步骤操作,也可以直接复制这个示例。
NextPDF 会一趟读完你的 HTML,并把结果直接以流式方式写入页面。这是一条单趟流式(streaming)流水线。你不必理解这套模型也能使用这则示例,但它值得记住,因为它影响了本页后面的几条规则。
composer require nextpdf/core:^3这条命令会安装 nextpdf/core 包。本页示例在 PHP 8.4 上运行,支持的运行环境为 >=8.4 <9.0。
概念总览
标题为“概念总览”的章节writeHtml() 接收一段 HTML 字符串,从当前光标位置开始,将其绘制到当前页面。以下逐步说明内部发生了什么。首先,引擎会扫描一次你的 HTML,并将它拆成一串 token(HtmlTokenizer)。接着,它从左到右遍历这份列表(HtmlParser)。对于每个元素,它会把对应的 PDF 绘图指令(也就是内容流运算符)写入缓冲区。在两次调用之间,引擎不会在内存中构建或保留元素树。这是刻意的设计,也就是 ADR-001 记录的单趟流式模型。
每个受支持的块级元素会变成一个布局框,每段文本会变成一个显示文本(text-show)运算符。来自行内 style 属性与 <style> 块的样式,会通过 CSS 层叠(cascade)完成 resolve(解析):当多条规则同时适用时,决定哪个样式胜出的那套标准规则。文本换行、对齐与间距遵循 CSS Text 模型;该模型规范原始文本如何转换为排版后、已换行的文本(W3C CSS Text Level 3)。
如果你不选择字体,正文会使用一套默认字体。该默认字体是标准 Type 1 字体,也就是 ISO 32000-2 列出的 14 个标准字体之一。只有在你注册并选用自己的字体,或某个一致性配置文件要求 NextPDF 嵌入替代字体时,这套默认才会改变。
先明确一项预期:NextPDF 支持的是 HTML 与 CSS 的一个子集,而不是两者的全部。这则示例涵盖的就是该受支持子集,并不声称支持完整的 HTML 或完整的 CSS。每个模块经过验证的确切状态,请参见 CSS 支持矩阵一节。
API 接口
标题为“API 接口”的章节这个方法的签名是 writeHtml(string $html): static。它声明于 NextPDF\Contracts\PdfDocumentInterface 接口,并实现于 NextPDF\Core\Concerns\HasTextOutput。它会绘制到当前页面;如果还没有任何页面,它会自动创建一页。这个方法的完整 PHPDoc 表格由源代码生成。
代码示例 — 快速上手
标题为“代码示例 — 快速上手”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('HTML Basic');$doc->addPage();
$doc->writeHtml('<h1>HTML Rendering in NextPDF</h1><p>Rendered with <strong>writeHtml()</strong>.</p>');
$doc->save(__DIR__ . '/out.pdf');代码示例 — 生产环境
标题为“代码示例 — 生产环境”的章节这是一个完整、自包含的示例,也是测试套件(harness)实际运行的版本。它对应 examples/08-html-basic.php。它不会写死输出路径,而是写入测试套件提供的位置,这样可重现性套件才能将脚本运行两次并比对结果。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('HTML Basic');$doc->addPage();
$html = <<<'HTML'<h1 style="color: #1E3A8A;">HTML Rendering in NextPDF</h1>
<p>NextPDF renders <strong>HTML content</strong> directly into PDF pages.This is the recommended approach for <em>mixed formatting</em>.</p>
<h2>Supported elements</h2>
<ul> <li>Headings (h1-h6)</li> <li>Paragraphs with <strong>bold</strong> and <em>italic</em></li> <li>Ordered and unordered lists</li> <li>Tables with borders and alignment</li> <li>Inline styles (color, font-size, margin)</li></ul>
<h2>Ordered list</h2>
<ol> <li>Create a Document instance</li> <li>Add pages and content</li> <li>Call save() or output()</li></ol>HTML;
$doc->writeHtml($html);
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script twice.// Honour it: do not hard-code a path, do not echo the PDF to STDOUT.$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/render-html-to-pdf.pdf');
echo "Wrote render-html-to-pdf.pdf\n";预期的 STDOUT:
Wrote render-html-to-pdf.pdf边界情况与陷阱
标题为“边界情况与陷阱”的章节- 光标交接。
writeHtml()会将光标推进到已绘制内容的结尾。后续的cell()或第二次writeHtml()会从该位置继续,而不是从页面顶端开始。 - 还没有页面。 如果不存在任何页面,
writeHtml()会在绘制前先添加一页。当你需要先设定特定页面尺寸时,请明确调用addPage()。 - 元素数与嵌套深度上限。 这个流式引擎施加
50,000个元素与 100 层嵌套的上限(ADR-001)。超过上限的文档会被拒绝,而不会被无声截断。 - 不支持的标记。 落在受支持子集之外的元素与属性会被忽略或回退(fall back);它们不会抛出异常。在依赖某个属性之前,请对照 CSS 支持矩阵确认其覆盖范围。
- 外部资源。 远程图像与样式表受外部资源策略管理;默认策略不会抓取任意远程 URL。
由于分词(tokenization)与绘制会对输入进行单趟处理,成本会随 token 数量线性增长,也就是 O(n)。这则示例的默认预算是 wall_ms: 1500, peak_mb: 96。由于引擎以流式方式输出、不会在内存中保留 DOM,峰值内存取决于内容流缓冲区与活跃样式栈的大小,而不是整份文档的大小。
CSS 支持矩阵摘录(仅列 Verified 的行)
标题为“CSS 支持矩阵摘录(仅列 Verified 的行)”的章节这里只列出评为 Verified、且出现在经过真实性审核的 CSS 支持矩阵中的行。「Verified」表示存在一份 src/Html/ 实现,以及一组实质性的专属 fixture 测试,并且能在结构配置文件下确定性地通过。
| W3C 模块 | 级别 | 状态 | 佐证 |
|---|---|---|---|
CSS 弹性盒布局(css_flexbox_1) | 1 | 已验证(Verified) | src/Html/Flex/、tests/Unit/Html/Flex/ |
CSS 网格布局(css_grid_1) | 1 | 已验证(Verified) | src/Html/Grid/、WPT 语料 |
CSS Cascading and Inheritance 层叠与继承(css_cascade_3) | 3 | 已验证(Verified) | src/Html/Cascade/、tests/Unit/Html/Cascade/ |
CSS 表格(css_tables_3) | 3 | 已验证(Verified) | src/Html/Table/、表格 fixture 与黄金 PDF |
CSS 字体(css_fonts_4) | 4 | 已验证(Verified) | src/Html/FontFace/、tests/Unit/Html/FontFace/ |
像 text-align、text-indent 与 color 这类属性,在矩阵中评为「Claimed」(已实现,但没有专属模块 fixture),因此这里刻意不列为 Verified。
单趟流式的约束(ADR-001)
标题为“单趟流式的约束(ADR-001)”的章节这个 HTML 引擎不保留任何 DOM。它的状态由一个标量光标和一个 push/pop 样式栈组成;只包含空白的文本节点会在分词阶段被丢弃。一个后果是:较晚出现的元素无法重新设置较早元素的样式,而需要完整树状上下文的选择器(例如复杂的 :has() 情况)会依 ADR-006 受到约束。规划布局时,请只依赖文档顺序。
分层契约(ADR-010)
标题为“分层契约(ADR-010)”的章节解析、布局与绘制是彼此分离的层。解析器不发出原始绘制运算符,布局分派也不解析 CSS;跨越这些边界正是 ADR-010 所禁止的耦合债。对示例作者而言,这意味着公开入口点是 writeHtml(),不要直接操作解析器内部。
大型文档的内存预算
标题为“大型文档的内存预算”的章节依 ADR-020,以容器为范围的格式化上下文(flex、表格)可能会构建一棵短暂的子树,但每个上下文受限于 5,000 个节点、20 层深度,并且所有活跃上下文合计有 50 MB 的活跃内存上限与 10 层嵌套限制。在这些上下文之外,流式模型不保留任何树。让单个表格与 flex 容器保持在节点上限之内,内存用量才会可预期。
安全须知
标题为“安全须知”的章节把 HTML 输入视为不可信。NextPDF 不执行任何脚本,默认的外部资源策略也不会抓取任意远程 URL,因此引擎本身采取保守做法。即便如此,对于任何由用户输入组装出的 HTML,在绘制之前都要验证或清理。元素与嵌套上限也会保护你:它们限制了一份恶意或格式错误的文档能够要求的工作量。
一致性
标题为“一致性”的章节| 陈述 | 规范 | 条款 | 参考 ID |
|---|---|---|---|
| CSS Text 规范定义了原始文本转换为排版后、已换行文本的过程。 | W3C CSS Text Level 3(文本模块第 3 级) | 条款 css_text_3#x1.x2.p4 | |
| 默认正文字体解析为一个标准 Type 1 字体。 | ISO 32000-2 | 条款 iso32000_2_sec9#x1.x29 |
这则示例演示 NextPDF 如何绘制一个受支持的 HTML 与 CSS 子集。它并不声称支持完整的 HTML 或完整的 CSS;每个模块经过验证的状态都列在 CSS 支持矩阵中。
商业背景
标题为“商业背景”的章节不适用。