跳转到内容

从 Dompdf 迁移到 NextPDF

本指南说明如何将基于 Dompdf 的代码库迁移到 NextPDF Html 流水线。Dompdf 和 NextPDF 的调用形态相同——加载 HTML、渲染、输出 PDF——所以大多数调用点都可以机械转换。真正的工作量在于选项映射和 CSS 支持差异。NextPDF 和 Dompdf 是相互独立的引擎,因此 Dompdf 产出的版面与 NextPDF 的结果是兼容关系,而不是逐字节相同。本指南涵盖动词映射、选项键映射、行为差异,以及一套安全的迁移顺序。

NextPDF 支持某项 HTML/CSS 特性,并不保证某个特定的 Dompdf 文档能逐像素重现。哪些特性属于「已验证」,以 CSS 支持矩阵 为准。本指南描述的是行为;它并不断言视觉等价。

Terminal window
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 不同(见 行为差异)。

NextPDF Html API 见 Html 模块 参考文档(由 PHPDoc 自动生成)。下文用到的关键入口点包括:Document::createStandalone()Document::writeHtml(string $html): staticDocument::writeHtmlCell(...)Document::output(?string, OutputDestination)Document::save(string $path): voidDocument::getPdfData(): string,以及 NextPDF\Core\Config 值对象(pageSizemarginsfontsDirectory)。

下面的 Dompdf 公共 API 名称已对照上游公共仓库(dompdf/dompdfmaster)核实——见仓库内的 _source-sidecar-upstream-api.md 来源边车文件。这里没有复制任何上游文档文本。

DompdfNextPDF说明
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() / 元数据 APIDompdf 的自由格式信息对,映射到带类型的元数据 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 that
is <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 迁移路径。


本指南适用于正在用 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->pageSizePageSize VO)具名尺寸会变成显式的点(point)尺寸;new PageSize(595.276, 841.890, 'A4')createStandalone() 的默认值。
default_paper_orientation / setDefaultPaperOrientation()交换 PageSize width/heightNextPDF 没有方向标志;横向页面就是一个宽度 > 高度的 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 作为文档级全局倍率——未建模。

依赖这些的代码无法「迁移」。你需要按上面各行所述将其移除,或在应用代码中重新表达。

  1. 同时添加 nextpdf/coredompdf/dompdf(暂不移除 Dompdf)。
  2. 挑选一个低风险文档。 用 动词映射 重写它的调用点;删除 render() 调用。
  3. 用相同输入生成两份 PDF,并对它们做视觉差异比对。 将差异视为预期之内(相互独立的引擎),并逐文档决定是否接受。
  4. 通过 选项映射 转换选项用法;把由 DPI 推导出的尺寸重新换算成点。
  5. 预先把 remote/relative 资产解析为绝对路径或 data URI,以消除解析变量。
  6. 逐文档重复,从最低风险到最高风险。 在最后一个调用点切换完成之前,保持两个引擎都已安装。
  7. 只有在最后一次切换完成后,才从 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.phpcreateStandaloneWithNullConfigBuildsDocumentWithA4Defaults)。
writeHtml() 在单次流式遍历中完成排版;排版后不保留 DOM。docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.mdsrc/Core/Concerns/HasTextOutput.phpwriteHtml())。
writeHtml() 在没有页面时会自动创建第一页。tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.phpwriteHtmlAutoCreatesFirstPageWhenNoPagesExist)。
output() / save() / getPdfData() 是输出动词(没有 render/output 两阶段)。src/Core/Concerns/HasOutput.phpoutput()save()getPdfData());tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php
输出目标是 NextPDF\Contracts\OutputDestination 枚举(Inline/Download/File/String)。src/Contracts/OutputDestination.phptests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php
HTML 字体始终写入为 embedded/subset 程序。tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.phprecordUsedCharactersAffectsFontSubsetting);ISO 32000-2 §9(前置元数据 citations:)。
带类型的元数据 setter(setTitle/setAuthor)替代自由格式的 addInfo()src/Core/Concerns/HasMetadata.phpsetTitle()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 支持矩阵当作仅供参考——它是关于预期的「已验证特性」权威。