跳转到内容

从 mPDF 迁移到 NextPDF

本指南将帮助你把基于 mPDF 的代码迁移到 NextPDF core。mPDF 的核心操作是 WriteHTML(),可直接映射到 Document::writeHtml()。真正费工的是构造函数 config 数组的映射(mPDF 将所有设置都放在传给 new Mpdf([...]) 的单个关联数组中),以及字体处理差异。NextPDF 与 mPDF 是相互独立的引擎,因此迁移后的文档与 mPDF 输出 兼容,但不会逐字节相同。本指南涵盖动词映射、config 数组映射、字体差异、CSS 支持差异、行为差异,以及一套安全的迁移顺序。

哪些 HTML/CSS 功能已经验证(Verified),以 CSS 支持对照表 为准。本指南描述的是行为,并不主张与 mPDF 在视觉上等价。

Terminal window
composer require nextpdf/core:^3

过渡期间请保留 mpdf/mpdf 的安装。在最终切换之后再移除它(见 安全迁移顺序 一节)。

mPDF 的 Mpdf 对象是一个庞大的单一 facade(外观):通过一个构造函数数组完成配置,再由 WriteHTML() 加上分页操作(AddPageSetHTMLHeaderSetHTMLFooter)来驱动。NextPDF 把设置拆分到不可变的 NextPDF\Core\Config 值对象中,并以 Document::writeHtml() 驱动内容。它没有构造函数层面的「mode」字符串。NextPDF 会解析你传入的 HTML,再以 save()output()getPdfData() 输出文档。

字体方面:mPDF 随附一个字体目录,并配有一份 fontdata 映射表和一组「核心字体」回退集。NextPDF 通过单一字体目录加 CSS font-family 匹配来 resolve(解析)字体,并 一律 对嵌入字体做子集化(ISO 32000-2 §9,iso32000_2_sec9#x1.x45.p7)。字体的 matching/fallback(匹配/回退)由各引擎自行定义(CSS Fonts 4 §5.5,css_fonts_4#x1.x5.x5.x1.p13),因此字形替换结果可能不同。这是主要可见的差异,详见 字体处理差异 一节。

NextPDF 的 HTML API 请参见 Html 模块 参考文档。主要入口包括:Document::createStandalone()Document::writeHtml(string $html): staticDocument::writeHtmlCell(...)Document::addPage()Document::output(?string, OutputDestination)Document::save(string $path): voidDocument::getPdfData(): string,以及 NextPDF\Core\Config 值对象。

下方的 mPDF 公开方法名称已逐一对照上游公开仓库(mpdf/mpdfdevelopment)确认;请参见仓库内的 _source-sidecar-upstream-api.md 来源 sidecar 文件。未复刻任何上游文档文本。

mPDFNextPDF说明
new Mpdf([...])Document::createStandalone($config)mPDF 的 config 数组映射到 NextPDF\Core\Config;见 config 映射 一节。长生命周期 worker 请使用 DocumentFactory
$mpdf->WriteHTML($html)$doc->writeHtml($html)直接映射。mPDF 的第二个 $mode 参数(用于区分完整文档、仅 CSS、单一元素)在 NextPDF 没有对应项,请改传完整 HTML。
$mpdf->WriteFixedPosHTML(...)$doc->writeHtmlCell(...)带定位/尺寸的 HTML 区域;映射 x/y/width/height 等参数。
$mpdf->AddPage(...)$doc->addPage()mPDF 那种逐次调用中的 orientation/format/页边距覆盖,在 NextPDF 中不是参数;请改在调用之间调整文档模型。
$mpdf->SetHTMLHeader($html) / SetHTMLFooter($html)通过 Layout API 设置 header/footer(页眉/页脚)mPDF 的运行时 HTML 页眉映射到 NextPDF 的 header/footer 机制,而不是映射到正文顶端的内联 HTML。
$mpdf->Output($name, $dest)$doc->output($name, OutputDestination::…)mPDF 的目标字符(I/D/F/S)映射到 OutputDestination 枚举(Inline/Download/file);文件输出改用 save(),字符串输出改用 getPdfData()
$mpdf->Output('','S')$doc->getPdfData()返回字节。
$mpdf->Output($path,'F')$doc->save($path)写入文件路径。
$mpdf->SetTitle($t)$doc->setTitle($t)写入 ISO 32000-2 §14 的 info 字典/XMP(iso32000_2_sec14#x1.x5.p5)。
$mpdf->SetProtection($perms,...)$doc->setEncryption(...)(Security API,安全性 API)权限依赖阅读器配合,并非访问控制,见 安全性说明 一节。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
// mPDF:
// $mpdf = new \Mpdf\Mpdf();
// $mpdf->WriteHTML('<h1>Invoice</h1>');
// $mpdf->Output('out.pdf', \Mpdf\Output\Destination::FILE);
// NextPDF — default page 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/04-text-and-fonts.php 一致,是本指南字体处理概念的可运行示例。它使用明确的页面尺寸、页边距,以及一段会触发字体选择的正文内容。

<?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: new Mpdf(['format'=>'A4','margin_left'=>20, ...]).
// Margin constructor order is (top, right, bottom, left) — NOT L,R,T,B.
$config = new Config(
pageSize: new PageSize(595.276, 841.890, 'A4'),
margins: new Margin(16.0, 20.0, 16.0, 20.0), // top,right,bottom,left in points
fontsDirectory: __DIR__ . '/fonts',
);
$doc = Document::createStandalone($config);
$doc->setTitle('Quarterly Report');
$doc->addPage();
$html = <<<'HTML'
<h1 style="font-family:'DejaVu Sans';color:#1E3A8A;">Quarterly Report</h1>
<p>Body text resolves through CSS font-family matching against the configured
fonts directory. mPDF's fontdata map has no direct analogue — register the
family via CSS and the fonts directory instead.</p>
HTML;
$doc->writeHtml($html);
// Equivalent of $mpdf->Output('report.pdf', Destination::DOWNLOAD):
$doc->output('report.pdf', OutputDestination::Download);
  • $mode 参数属于 WriteHTML mPDF 的 WriteHTML($html, $mode)(2 = 仅 CSS,1 = 元素)并无对等项。请把你的 CSS 内联到传入的 HTML 中;不存在「先写 CSS 再写正文」的流程。
  • 逐次 AddPage 覆盖。 mPDF 允许 AddPage() 通过参数在文档中途变更 format/orientation。NextPDF 的 addPage() 不接受这类参数;尺寸变更应通过文档模型表达,而不是通过页面调用传参。
  • 页眉/页脚 HTML。 mPDF 的运行时页眉是另外注册的 HTML 片段;请勿把它们粘进正文里。请把它们映射到 NextPDF 的 header/footer 机制。
  • 字体名称。 mPDF 通过它的 fontdata/core-font 表来规范化字体名称。NextPDF 会把 CSS 的 font-family 与字体目录匹配;某个在 mPDF 中能默默解析的别名,可能需要明确的 @font-face/family。
  • 目标字符。 不接受 'I'/'D'/'F'/'S';请使用 OutputDestination 枚举,或 save()/getPdfData()

writeHtml() 是单次处理(ADR-001);峰值内存随文档大小变动,而不是随保留的 DOM 变动。本指南示例的预算:wall_ms: 2000, peak_mb: 128。对于长文档,请把内容分页到多个 addPage(),不要塞进一个巨大的字符串。这与 mPDF 的 $mode 分段做法是同一套建议,只是改用页面模型来表达。

  • 权限依赖阅读器配合。 SetProtection()setEncryption() 提供的是保密性,而非访问控制:ISO 权限位是否生效取决于阅读器是否配合。请勿把加密向用户描述成访问控制。
  • 元数据。 SetTitle() 与文档 info 会写入 ISO 32000-2 §14 的信息字典/XMP(iso32000_2_sec14#x1.x5.p5);切勿在那里保存任何秘密信息。
  • NextPDF 不会运行文档内的脚本;这里没有任何 mPDF 指令会激活这类功能。
陈述规范条款参考 ID
字体会写为 embedded/subset(嵌入/子集化)字体程序。ISO 32000-2§9
页面的 format/margins(格式/页边距)映射到页面边界框。ISO 32000-2§7
标题/保护元数据会写入 info 字典/XMP 中。ISO 32000-2§14
字体匹配/回退由各引擎自行定义。CSS Fonts 4(CSS 字体规范)§5.5

NextPDF 产生的是 ISO 32000-2 内容;它并不主张与 mPDF 在视觉上完全一致。只要 renderer(渲染器)发生变更,就需要重新复核输出。

不适用。core 已涵盖这里描述的 mPDF 迁移路径。


适用于在服务器端使用 mpdf/mpdf 做 HTML 转 PDF 的团队。如果你的接口是 new Mpdf([...]) + WriteHTML + Output(以及可选的 header/footer),那么 动词映射config 映射 就已经涵盖。

范围内:Mpdf 构造函数 config 数组、WriteHTML/Output/AddPage、headers/footers、字体、保护、元数据。范围外:mPDF 的内部类,以及 QR/条码/水印的辅助接口(请把这些映射到对应的 NextPDF 模块,即 Barcode、Graphics;本文不涵盖)。

这里说的是行为兼容,并非可直接替换的 shim(兼容层):不存在 Mpdf 类的兼容层。每一个调用点都需要改写。CSS 功能预期以 CSS 支持对照表 的 Verified 列为准。

mPDF 的 config 键已对照上游公开仓库(mpdf/mpdfdevelopment)确认。未复刻任何上游文档文本。

mPDF config 键NextPDF说明
mode(无对等项)mPDF 的 mode 字符串('utf-8''c''+aCJK' 等)选择 font/script(字体/书写系统)行为。NextPDF 始终使用 Unicode;CJK 由字体选择处理,而不是由某个 mode 处理。请删除这个键。
formatConfig->pageSizePageSize VO)命名格式会转换为明确的点(point)尺寸;数组 [w,h] 映射到一个 PageSize
orientation对调 PageSize 的 width/height没有方向标志;横向 = 宽 > 高。
default_font_sizeCSS 基准 font-size通过你的基准样式表设置,而不是通过构造函数的键。
default_fontCSS font-family/已注册字体通过 CSS/字体注册来设置默认字族。
margin_left / margin_right / margin_top / margin_bottomConfig->marginsMargin VO),以点为单位单一 Margin 值对象;它的构造函数顺序是 Margin(top, right, bottom, left)(请对照 src/ValueObjects/Margin.php 确认),而不是 mPDF 的键顺序。
margin_header / margin_footer通过 Layout API 设置 header/footer 偏移请映射到 NextPDF 的 header/footer 设置,而不是构造函数的键。
  • 单一字体目录。 mPDF 的字体目录列表 + fontdata 映射表 + 核心字体回退,全部统一为 Config->fontsDirectory 加 CSS font-family 匹配。
  • 一律子集化。 NextPDF 一律对嵌入字体做子集化(ISO 32000-2 §9,iso32000_2_sec9#x1.x45.p7);mPDF 的子集化标志并无对等项,也不需要。
  • 匹配由各引擎自行定义。 字体的 matching/fallback 因引擎而异(CSS Fonts 4 §5.5,css_fonts_4#x1.x5.x5.x1.p13);某个 mPDF 字体别名可能需要明确的 @font-face 或精确的字族名称。迁移后请重新建立字形渲染基准;替换差异是预期内的,并非缺陷。
  • 字体替换(见上文)——主要可见的差异。
  • WriteHTML 上没有 $mode——请传入含内联 CSS 的完整 HTML。
  • 没有逐次 AddPage 的 format 覆盖——尺寸变更通过文档表达。
  • 权限依赖阅读器配合(见 安全性说明 一节)。
  • 独立的版面引擎——在密集内容上,断行/分页会有所不同;请重新建立视觉差异基准。

这些是有记录的行为差异,而非任一引擎的缺陷。

  • mode 构造函数字符串——未建模(始终使用 Unicode)。
  • AddPage() 的 format/orientation/页边距参数——在 NextPDF 中不是参数。
  • mPDF 的 fontdata 映射表——由字体目录加 CSS 匹配取代。
  • mPDF 的 'I'/'D'/'F'/'S' 目标字符——由 OutputDestination 枚举加上 save()/getPdfData() 取代。
  1. nextpdf/corempdf/mpdf 并存安装(暂时保留 mPDF)。
  2. 挑一份低风险的文档。把 new Mpdf([...]) 通过 config 映射 转换,并把 WriteHTML/Output 通过 动词映射 转换。
  3. 把该文档用到的字体注册到 Config->fontsDirectory,并为任何 mPDF 别名加上明确的 @font-face/family 声明。
  4. 对同一份输入产生两份 PDF;做视觉差异比对。差异(字体替换、断行)对独立引擎而言是预期行为——请逐份文档确认接受。
  5. 把任何 header/footer HTML 映射到 NextPDF 的 header/footer 机制。
  6. 逐份文档重复,最低风险的先做;保留 mPDF 安装直到最后一次切换。
  7. 在最终切换之后,把 mpdf/mpdfcomposer.json 移除。
  • 在改动代码之前,先快照代表性文档的 mPDF 输出(作为 golden 参考;字节会有所不同)。
  • 对逐份迁移的文档,用你自己的验收检查来断言(视觉差异比对 + 文本截取)。NextPDF 的字体/HTML 行为由 examples/04-text-and-fonts.phpexamples/08-html-basic.php 加上内核 tests/ 的 Html/Font 测试套件实际验证。迁移验收因文档而异,由你自行负责。
  • 为每一份迁移的文档加上一个回归测试。

本页每一项 NextPDF 行为陈述,都有仓库内的测试、示例、源码签名或 ADR 支持;若属 PDF 格式特性,则由 front-matter(前置数据)citations: 中 RAG 固定的 ISO 32000-2 / CSS 条款,以及 符合性 表佐证。mPDF 的行为仅以「独立引擎——预期会有记录在案的差异」来陈述;不会主张任何仓库内产物无法证明的对等性。

NextPDF 行为主张仓库内佐证(路径)
WriteHTML() 直接映射到 Document::writeHtml(string $html): staticsrc/Core/Concerns/HasTextOutput.phpwriteHtml());examples/08-html-basic.php
WriteFixedPosHTML(...) 映射到 writeHtmlCell(...)src/Core/Concerns/HasTextOutput.phpwriteHtmlCell())。
createStandalone() 的默认页面为 A4 纵向(595.276 × 841.890 pt)。src/Core/Config.php(默认 PageSize);tests/Unit/Core/DocumentCreateStandaloneAndConfigWithersEdgeCaseTest.php
Margin 的构造函数顺序是 (top, right, bottom, left)src/ValueObjects/Margin.php(属性提升顺序)。
输出目的地是 NextPDF\Contracts\OutputDestination 枚举;不接受 'I'/'D'/'F'/'S'src/Contracts/OutputDestination.php(枚举项 Inline/Download/File/String);tests/Unit/Core/Concerns/DocumentOutputDestinationDispatchTest.php
Output('','S')getPdfData()Output($path,'F')save($path)src/Core/Concerns/HasOutput.phpgetPdfData()save()output())。
SetProtection() 映射到 setEncryption(...);权限依赖阅读器配合。src/Core/Concerns/HasSecurity.phpsetEncryption());ISO 32000-2 §14(前置数据 citations:)。
SetTitle()setTitle();元数据会写入 info 字典/XMP 中。src/Core/Concerns/HasMetadata.phpsetTitle());tests/Unit/Core/Concerns/DocumentInfoMetadataSetterBaselineTest.php;ISO 32000-2 §14(前置数据 citations:)。
字体始终以子集化程序的形式嵌入。tests/Unit/Core/Concerns/DocumentTextOutputFontSubsettingAndBorderEdgeCaseTest.phpexamples/04-text-and-fonts.php;ISO 32000-2 §9(前置数据 citations:)。
字体匹配/回退由各引擎自行定义(替换差异)。CSS Fonts 4 §5.5(front-matter citations: + 符合性 一节)。
writeHtml() 是单次处理;尖峰内存随文档大小变动。docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md

两个包都会保留安装直到最终切换,因此单个调用点的回滚,就是将该调用点还原到 mPDF 路径。最终切换之后,回滚就是从版本控制还原 mpdf/mpdf 与先前代码。不涉及任何数据迁移。

性能 一节。单次处理模型移除了 mPDF 那种保留缓冲区带来的成本。新增的逐文档成本是急切(eager)字体解析(步骤 3),可通过字体目录缓存降低。

  • 保留 mode 键并期望它带来 CJK 行为(应删除它;CJK 由字体选择决定)。
  • 传入 WriteHTML($html, 2)(仅 CSS 模式)——请改用内联 CSS。
  • 把 header/footer HTML 粘进正文里。
  • 在没有明确字族的情况下,期望某个 mPDF 别名获得相同字体。
  • 期望输出 byte/pixel-identical(逐字节/逐像素相同)(独立引擎 — 本指南从未主张可直接替换或 100% 兼容)。
  • 把 CSS 支持对照表当成仅供参考——它是已验证功能的权威依据。