从 Dompdf 迁移到 NextPDF
本指南说明如何将基于 Dompdf 的代码库迁移到 NextPDF Html 流水线。Dompdf 和 NextPDF 的调用形态相同——加载 HTML、渲染、输出 PDF——所以大多数调用点都可以机械转换。真正的工作量在于选项映射和 CSS 支持差异。NextPDF 和 Dompdf 是相互独立的引擎,因此 Dompdf 产出的版面与 NextPDF 的结果是兼容关系,而不是逐字节相同。本指南涵盖动词映射、选项键映射、行为差异,以及一套安全的迁移顺序。
NextPDF 支持某项 HTML/CSS 特性,并不保证某个特定的 Dompdf 文档能逐像素重现。哪些特性属于「已验证」,以 CSS 支持矩阵 为准。本指南描述的是行为;它并不断言视觉等价。
composer require nextpdf/core:^3过渡期内请保留已安装的 dompdf/dompdf(安全迁移顺序 会让两者并行运行,直到每个调用点都切换完毕),切换完成后再将其移除。
概念概览
标题为“概念概览”的章节Dompdf 的 Dompdf 对象是一个单一门面,同时持有 DOM、样式表、帧树和画布。NextPDF 把这些职责拆开:NextPDF\Core\Document 持有页面模型和输出,而单个方法 writeHtml() 驱动 HTML 流水线。这里没有单独的「先渲染再输出」两阶段步骤。writeHtml() 在写入内容的同时完成排版,你可以用 save()、output() 或 getPdfData() 输出文档。
NextPDF 写入的页面内容采用 ISO 32000-2 内容流绘制(ISO 32000-2 §8,iso32000_2_sec8#x1.x3.p14)。纸张尺寸选项控制的页面几何会映射到页面对象的 MediaBox(ISO 32000-2 §7,iso32000_2_sec7#x1.x104.p10)。这些都是任何合规写入器共有的基础引擎原理。不过,将 CSS 转换为这些内容的版面算法属于 NextPDF 自身实现,和 Dompdf 不同(见 行为差异)。
API 表面
标题为“API 表面”的章节NextPDF Html API 见 Html 模块 参考文档(由 PHPDoc 自动生成)。下文用到的关键入口点包括:Document::createStandalone()、Document::writeHtml(string $html): static、Document::writeHtmlCell(...)、Document::output(?string, OutputDestination)、Document::save(string $path): void、Document::getPdfData(): string,以及 NextPDF\Core\Config 值对象(pageSize、margins、fontsDirectory)。
API 动词映射
标题为“API 动词映射”的章节下面的 Dompdf 公共 API 名称已对照上游公共仓库(dompdf/dompdf,master)核实——见仓库内的 _source-sidecar-upstream-api.md 来源边车文件。这里没有复制任何上游文档文本。
| Dompdf | NextPDF | 说明 |
|---|---|---|
new Dompdf($options) | Document::createStandalone($config) | Dompdf 接收一个 Options 对象;NextPDF 接收一个 NextPDF\Core\Config。对于长期运行的工作进程,请使用 DocumentFactory 而非 createStandalone()。 |
$dompdf->loadHtml($html, $encoding) | $doc->writeHtml($html) | NextPDF 将输入视为 UTF-8;请在调用前转码非 UTF-8 输入,而不是传入编码参数。 |
$dompdf->loadHtmlFile($file) | $doc->writeHtml(file_get_contents($file)) | NextPDF 没有文件加载变体;请自行读取文件,让 I/O 策略留在你的代码里。 |
$dompdf->setPaper($size, $orientation) | ConfigpageSize(一个 PageSize 值对象) | 见 选项映射。 |
$dompdf->render() | (隐式) | NextPDF 在 writeHtml() 期间完成排版;没有单独的渲染阶段。请移除 render() 调用。 |
$dompdf->output() | $doc->getPdfData() | 返回 PDF 字节。 |
$dompdf->stream($name, $opts) | $doc->output($name, OutputDestination::Download) | NextPDF 通过 OutputDestination 枚举来区分输出目标。 |
$dompdf->setBasePath($p) / setProtocol() / setBaseHost() | (资源解析方式不同) | NextPDF 将相对资源解析到文档工作集,而不是基于一个 path/protocol 三元组——见 行为差异。 |
$dompdf->addInfo($label, $value) | $doc->setTitle() / setAuthor() / 元数据 API | Dompdf 的自由格式信息对,映射到带类型的元数据 setter(ISO 32000-2 §14 文档信息,iso32000_2_sec14#x1.x5.p5)。 |
$dompdf->setHttpContext($ctx) | (无对应项) | NextPDF 不通过流上下文获取远程资源;见 不支持 / 无直接对应项。 |
代码示例——快速上手
标题为“代码示例——快速上手”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// Dompdf:// $dompdf = new Dompdf();// $dompdf->loadHtml('<h1>Invoice</h1>');// $dompdf->setPaper('A4', 'portrait');// $dompdf->render();// file_put_contents('out.pdf', $dompdf->output());
// NextPDF — the createStandalone() default page size is A4 portrait:$doc = Document::createStandalone();$doc->setTitle('Invoice');$doc->addPage();$doc->writeHtml('<h1>Invoice</h1>');$doc->save(__DIR__ . '/out.pdf');
echo "Wrote out.pdf\n";代码示例——生产环境
标题为“代码示例——生产环境”的章节这对应 examples/08-html-basic.php(本指南的配套可运行示例),它显式设置了非默认的纸张尺寸和页边距。它等价于一次 Dompdf setPaper() 加上一份 Options 页边距配置。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\OutputDestination;use NextPDF\Core\Config;use NextPDF\Core\Document;use NextPDF\ValueObjects\Margin;use NextPDF\ValueObjects\PageSize;
// Equivalent of: $dompdf->setPaper('letter','portrait') + margin options.// US Letter portrait = 612 x 792 pt.// Margin constructor order is (top, right, bottom, left) — all 0.5in here.$config = new Config( pageSize: new PageSize(612.0, 792.0, 'Letter'), margins: new Margin(36.0, 36.0, 36.0, 36.0), // top,right,bottom,left; 0.5in in points);
$doc = Document::createStandalone($config);$doc->setTitle('Quarterly Report');$doc->setAuthor('Finance');$doc->addPage();
$html = <<<'HTML'<h1 style="color:#1E3A8A;">Quarterly Report</h1><p>This report renders through the NextPDF Html pipeline. The CSS subset thatis <strong>Verified</strong> for production is the support-matrix authority,not this page.</p><table border="1"> <tr><th>Region</th><th>Total</th></tr> <tr><td>EMEA</td><td>1,204</td></tr></table>HTML;
$doc->writeHtml($html);
// Equivalent of $dompdf->stream('report.pdf'):$doc->output('report.pdf', OutputDestination::Download);边界情况与陷阱
标题为“边界情况与陷阱”的章节- 没有两阶段渲染。 在
render()和output()之间检查状态(例如读取页数)的 Dompdf 代码,在 NextPDF 中没有对应的精确边界点。请改为在writeHtml()之后查询文档。 - 编码。 NextPDF 去掉了 Dompdf 的
$encoding参数:请在writeHtml()之前把输入转换为 UTF-8。传入 Latin-1 字节会产生乱码,而不是报错。 render()残留。 遗留的$dompdf->render()风格调用在 NextPDF 中没有对应方法,会导致致命的「undefined method」错误。切换期间请删除它,不要用桩代码保留。- 内联 PHP。 Dompdf 的
enable_php会执行<script type="text/php">。NextPDF 在设计上没有文档内 PHP 执行(那是一个注入攻击面)。请把那段逻辑移到writeHtml()之前的 PHP 代码中。 - 相对资源解析。 Dompdf 将
<img src>解析到基础 path/protocol/host 三元组。NextPDF 则解析到文档工作集。迁移期间,请传入绝对路径或预先解析好的 data URI,以消除这个变量。
writeHtml() 在单次流式遍历中完成排版(ADR-001)。排版后不会保留中间的帧树对象,所以峰值内存跟随文档大小,而非 DOM 节点数。本指南示例的性能预算是 wall_ms: 2000, peak_mb: 128。大型文档请按 addPage() 边界把 HTML 分块,而不是构建单个数兆字节的字符串。
安全说明
标题为“安全说明”的章节- 不通过流上下文获取远程资源。 NextPDF 不实现 Dompdf 的
setHttpContext()/enable_remote远程获取路径。请在你的应用中解析并校验远程资产,然后传入字节或 data URI。这消除了enable_remote带来的 SSRF 攻击面。 - 没有文档内代码执行。 没有
enable_php的对应项,是有意的加固设计,而非缺失。 - 你通过带类型的 setter 设置的文档元数据,会写入 ISO 32000-2 §14 信息字典 / XMP(
iso32000_2_sec14#x1.x5.p5)。不要把密钥放在那里。
合规性
标题为“合规性”的章节| 陈述 | 规范 | 条款 | reference_id |
|---|---|---|---|
| 页面内容是 opaque/transparent 模型下的内容流绘制。 | ISO 32000-2 | §8 | |
| 纸张尺寸映射到页面对象的边界框。 | ISO 32000-2 | §7 | |
| HTML 字体写入为 embedded/subset 字体程序。 | ISO 32000-2 | §9 | |
| 空白 / 断行处理是引擎特定的。 | CSS Text 3 | §6.5 |
NextPDF 产出 ISO 32000-2 内容。它并不声明迁移后的 Dompdf 文档在视觉上完全相同。更换渲染器始终需要重新审查输出。
商业语境
标题为“商业语境”的章节不适用。核心版涵盖了这里描述的 HTML 转 PDF 迁移路径。
另请参阅
标题为“另请参阅”的章节迁移细节(R6 必备章节)
标题为“迁移细节(R6 必备章节)”的章节本指南适用对象
标题为“本指南适用对象”的章节本指南适用于正在用 dompdf/dompdf 做服务端 HTML 转 PDF、并希望换用 NextPDF 引擎的团队。如果你只调用 loadHtml / setPaper / render / output,那么 动词映射 就涵盖了你的整个 API 表面。
在范围内:Dompdf 门面动词、Options 键、CSS 特性对等预期、资源解析、元数据。不在范围内:Dompdf 的内部 FrameTree/Canvas/Stylesheet 对象(NextPDF 没有公开的对应物——不要迁移那些深入这些对象内部的代码;请用公共 API 重写它)。
兼容性映射
标题为“兼容性映射”的章节覆盖范围是行为兼容性,而不是可直接替换的垫片。NextPDF 没有 Dompdf 类垫片(不同于 TCPDF 路径——见 /migration/tcpdf-compat/)。你要用 动词映射 重写每一个调用点。CSS 支持矩阵 中的「已验证」行完全决定了 CSS 特性预期。本指南不再逐属性复述其状态。
选项与配置映射
标题为“选项与配置映射”的章节| Dompdf 选项(键 / setter) | NextPDF | 说明 |
|---|---|---|
default_paper_size / setDefaultPaperSize() ;setPaper($size,...) | Config->pageSize(PageSize VO) | 具名尺寸会变成显式的点(point)尺寸;new PageSize(595.276, 841.890, 'A4') 是 createStandalone() 的默认值。 |
default_paper_orientation / setDefaultPaperOrientation() | 交换 PageSize width/height | NextPDF 没有方向标志;横向页面就是一个宽度 > 高度的 PageSize。 |
dpi / setDpi() | (不是全局开关) | NextPDF 以 PDF 点为单位工作(1/72 in);图像尺寸按单张图像计算,而非文档级 DPI 倍率。请将固定像素尺寸重新换算成点。 |
enable_remote / setIsRemoteEnabled() | (无对应项——有意为之) | 在你的代码中解析远程资产;见 安全说明。 |
enable_html5_parser / setIsHtml5ParserEnabled() | (始终解析 HTML) | 没有开关;解析器就是流水线本身。 |
enable_php / setIsPhpEnabled() | (无对应项——有意为之) | 不支持文档内 PHP;请把逻辑移出模板。 |
font_dir / setFontDir() | Config->fontsDirectory | 单个字体目录字符串。 |
chroot | (在应用中解析) | NextPDF 不接收文件系统隔离选项;请在传入字节之前完成路径校验。 |
default_font / setDefaultFont() | CSS font-family / 已注册字体 | 请通过你的基础样式表或字体注册来设置默认值,而不是通过一个全局选项。 |
enable_font_subsetting / setIsFontSubsettingEnabled() | (始终子集化) | NextPDF 始终对嵌入字体进行子集化(ISO 32000-2 §9,iso32000_2_sec9#x1.x45.p7);没有「关闭」选项——Dompdf 关闭该标志的路径没有对应项,也不需要。 |
行为差异
标题为“行为差异”的章节- 版面引擎。 Dompdf 和 NextPDF 是相互独立的 CSS 版面实现。空白折叠和断行虽有规范,但对引擎敏感(CSS Text 3 §6.5,
css_text_3#x1.x6.x5.p20)。在密集文本中,应预期会有断行和分页差异。迁移后请重新建立视觉差异基线。 - 渲染接缝。 没有
render()/output()两阶段边界(见 边界情况)。 - 资源解析。 基础路径/协议/主机,对比文档工作集。
- DPI 模型。 点,对比 Dompdf 的 DPI 倍率。
- 元数据。 自由格式的
addInfo()对,对比带类型的 setter(ISO 32000-2 §14,iso32000_2_sec14#x1.x5.p5)。
这些是已记录的行为差异,而非任一引擎的缺陷。
不支持 / 无直接对应项
标题为“不支持 / 无直接对应项”的章节enable_php(文档内 PHP)——有意不提供。setHttpContext()/enable_remote远程获取——有意不提供。- 对
FrameTree/Canvas/Stylesheet的公开访问——没有公开对应物。 dpi作为文档级全局倍率——未建模。
依赖这些的代码无法「迁移」。你需要按上面各行所述将其移除,或在应用代码中重新表达。
安全的迁移顺序
标题为“安全的迁移顺序”的章节- 同时添加
nextpdf/core与dompdf/dompdf(暂不移除 Dompdf)。 - 挑选一个低风险文档。 用 动词映射 重写它的调用点;删除
render()调用。 - 用相同输入生成两份 PDF,并对它们做视觉差异比对。 将差异视为预期之内(相互独立的引擎),并逐文档决定是否接受。
- 通过 选项映射 转换选项用法;把由 DPI 推导出的尺寸重新换算成点。
- 预先把 remote/relative 资产解析为绝对路径或 data URI,以消除解析变量。
- 逐文档重复,从最低风险到最高风险。 在最后一个调用点切换完成之前,保持两个引擎都已安装。
- 只有在最后一次切换完成后,才从
composer.json中移除dompdf/dompdf。
测试迁移
标题为“测试迁移”的章节- 在改动代码之前,对代表性文档的 Dompdf 输出做快照(黄金输入,而非黄金字节——字节会不同)。
- 对每个已迁移的文档,用你自己的验收检查跑一遍 NextPDF 输出(视觉差异、文本提取断言)。NextPDF 自身的 HTML 流水线行为由
examples/08-html-basic.php和核心tests/Html 测试套件覆盖。你的迁移验收是文档特定的,由你自己负责断言。 - 为每个已迁移的文档添加一个回归测试,以便将来引擎更新时能够捕获变化。
证据 / 可追溯性
标题为“证据 / 可追溯性”的章节本页每条 NextPDF 行为陈述,都由仓库内的测试、示例、源码签名或 ADR 支撑——若属于 PDF 格式属性,则由前置元数据中 RAG 锚定的 ISO 32000-2 citations: / CSS 条款以及 合规性 表支撑。Dompdf 行为仅断言为「相互独立的引擎——请预期已记录的差异」。凡是仓库内构件无法证明的对等性,本指南均不声明。
| NextPDF 行为声明 | 仓库内证据(路径) |
|---|---|
createStandalone() 默认页面为 A4 纵向(595.276 × 841.890 pt)。 | src/Core/Config.php(默认 PageSize(595.276, 841.890, 'A4'));tests/Unit/Core/DocumentCreateStandaloneAndConfigWithersEdgeCaseTest.php(createStandaloneWithNullConfigBuildsDocumentWithA4Defaults)。 |
writeHtml() 在单次流式遍历中完成排版;排版后不保留 DOM。 | docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md;src/Core/Concerns/HasTextOutput.php(writeHtml())。 |
writeHtml() 在没有页面时会自动创建第一页。 | tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php(writeHtmlAutoCreatesFirstPageWhenNoPagesExist)。 |
output() / save() / getPdfData() 是输出动词(没有 render/output 两阶段)。 | src/Core/Concerns/HasOutput.php(output()、save()、getPdfData());tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php。 |
输出目标是 NextPDF\Contracts\OutputDestination 枚举(Inline/Download/File/String)。 | src/Contracts/OutputDestination.php;tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php。 |
| HTML 字体始终写入为 embedded/subset 程序。 | tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.php(recordUsedCharactersAffectsFontSubsetting);ISO 32000-2 §9(前置元数据 citations:)。 |
带类型的元数据 setter(setTitle/setAuthor)替代自由格式的 addInfo()。 | src/Core/Concerns/HasMetadata.php(setTitle()、setAuthor());tests/Unit/Core/Concerns/DocumentInfoMetadataSetterBaselineTest.php。 |
| 端到端 HTML 流水线(本指南可运行的支撑示例)。 | examples/08-html-basic.php;核心 tests/Unit/Html/ 套件。 |
| 空白 / 断行是引擎特定的(版面差异)。 | CSS Text 3 §6.5(前置元数据 citations: + 合规性)。 |
因为两个包在最终切换之前都保持安装,所以对一个尚未转换的调用点回滚,就是把那一个调用点恢复到 Dompdf 路径。最终切换之后,回滚意味着从版本控制中恢复 dompdf/dompdf 以及之前的调用点。不涉及任何数据迁移——只涉及代码。
性能考量
标题为“性能考量”的章节见 性能。单次遍历模型意味着迁移不会引入帧树保留成本。每文档的主要成本变化是提前解析资产(步骤 5),这一点你可以做缓存。
常见陷阱
标题为“常见陷阱”的章节- 留下
render()未删(致命的 undefined method 错误)。 - 去掉
$encoding后传入非 UTF-8 字节(静默乱码)。 - 期望逐字节或逐像素相同的输出(相互独立的引擎——本指南从不声明可直接替换或 100% 兼容)。
- 依赖
enable_php模板(必须重构移除)。 - 把 CSS 支持矩阵当作仅供参考——它是关于预期的「已验证特性」权威。