从遗留方案迁移:TCPDF、FPDF 及同类
Spec: ISO 32000-2ISO 32000-2Spec: ISO 19005-4ISO 19005-4Spec: ETSI EN 319 142-1ETSI EN 319 142-1
如果你的 PDF 是由 TCPDF、FPDF、mPDF 或 dompdf 生成的,那段代码很可能还能用。这恰恰是麻烦容易被忽视的原因。库在跑,文件能打开,而那道鸿沟只在某天有人要一份已签署、可归档或可无障碍访问的文件、而答案是“从这里我们做不到”时才显现出来。
本页就是这则迁移故事:那些墙是什么,为什么它们是结构性的而非偶然的,以及 NextPDF 如何给你一条分阶段离开它们的路径——其中包括一个 TCPDF 兼容层,它是一种迁移辅助,而不是一个字节级一致直接替换的承诺。
为什么这很重要
标题为“为什么这很重要”的章节一个 PDF 库不是你做一次的渲染调用。它是一份你的文件只要还存在就会一直继承的依赖。当那份依赖停止前进时,你的文件就再也无法去做新的事情——而你会在最糟糕的时刻才发现,正当一位客户、一位审计师,或一位监管者把标准摆出来的时候。
那些墙长成这样。格式向前走了:PDF 2.0 是这个标准的当前版本(Spec: ISO 32000-2ISO 32000-2),而一个卡在 1.x 结构上的写入器,落后于你工具链其余部分所假定的那个格式。签名薄弱或是硬贴上去的,远不及那些让签名站得住脚的 PAdES 基线配置文件(Spec: ETSI EN 319 142-1, §4ETSI EN 319 142-1 §4)。面向 PDF/A 系列的归档输出,以及用于无障碍的标记结构,要么缺失,要么脆弱。而 API 本身是无类型的——字符串形式的方向值、位置布尔参数、靠意外才发现的默认值——于是编译器帮不了你,审阅者也帮不了。
这些都不是你能打补丁绕过去的 bug。它们是一件为更早年代而造的工具的固有形态,而且其中好几件工具已不再积极地朝着你的文件如今必须满足的那些标准前进。
精简版
标题为“精简版”的章节- 遗留的 PHP PDF 库大多还能跑。问题在于它们通常无法以完整的现代合规度生产出什么:PDF 2.0、符合基线的签名、经验证的 PDF/A、带标记的无障碍结构——在那几个被点名的库里,对这些的支持有限或完全缺失。
- NextPDF 是一个 PHP 8.4 引擎,它默认写入 PDF 2.0,并把严格类型、归档配置文件以及 PAdES 签名作为一等输出。
- 你不必在第一天就重写一切。TCPDF 兼容层让熟悉的调用继续工作,与此同时你把真正要紧的文件逻辑迁移过去。
- 那个兼容层是与 TCPDF 兼容,而非字节级一致。它是一座横跨这次迁移的桥,带有书面记录的行为差异——而不是声称每个脚本都能原封不动地跑。
- 诚实的检验标准,是这些新能力是否值得这次迁移。对某些工作负载而言并不值得,我们也直说。
NextPDF 如何处理它
标题为“NextPDF 如何处理它”的章节这套做法是把迁移变成一个序列,而不是一次跳跃。你在整个过程中始终在生产文件,并且一次只置换掉一项旧约束,而不是把一次发布押注在一场一次性的大爆炸式重写上。
- InventoryCatalogue what your documents actually need to emit — signatures, archival profiles, tagged structure, fonts — not just which calls you make today.
- BridgeAdopt the TCPDF-compatibility surface so the existing call sites keep producing files while the engine underneath becomes NextPDF.
- PortMove the document logic that matters onto the native typed API, where intent is explicit and the compiler checks it.
- UpgradeTurn on the outputs many legacy libraries cannot reach with full modern conformance: PDF 2.0 structure, validated PDF/A, PAdES signatures, tagged accessibility.
- VerifyConfirm the result against a real validator, so 'archival' or 'signed' means a tool agrees, not just that the file opened.
PDF 2.0 是基准,而不是一个功能开关。 NextPDF 默认写入这个格式的当前版本(Spec: ISO 32000-2ISO 32000-2),并能在某个配置文件提出要求时序列化较旧的结构。一个冻结在 1.x 结构上的库无法在这里与你相会;这不是它缺了一个设置,而是它早于一个时代而生。
归档与无障碍是写入器的属性。 生产出一个验证器认可为 PDF/A 的文件,是引擎必须在写入时就做到的事——它无法事后被钉上去(Spec: ISO 19005-4ISO 19005-4)。让 PDF 变得可无障碍访问的那种标记结构,同样如此。NextPDF 在生成期间构建这些,而这恰恰是许多遗留工具迈不出的那一步——或者只能部分迈出,达不到验证器所认可的程度。
签名跨过了基线那道门槛。 一个 PDF 中的高级电子签名遵循 PAdES 配置文件(Spec: ETSI EN 319 142-1, §4ETSI EN 319 142-1 §4),其中摘要覆盖一个所声明的字节范围,而签名携带着验证器会检查的元数据。一个硬贴上去的签名辅助很少能够到那道门槛。NextPDF 把它当作一等输出,而不是事后补丁。
兼容层就是那座桥,诚实地表述出来。 TCPDF 兼容层之所以存在,是为了让你既有的调用点在你迁移那些要紧部分的同时继续生产文件。它遵循每一份 NextPDF 迁移指南都遵循的同一个模型:与源库兼容,而非字节级一致,并把行为差异写下来。这份诚实正是关键所在——一句无声的“99% 直接替换”宣称,正是这个引擎生来就要拒绝的那种猜测。
实践示例
标题为“实践示例”的章节一次迁移在调用点上的形态是很小的。旧代码通过兼容层继续生产一个文件;新代码通过类型化的原生 API 陈述意图,并要求一个遗留库够不到、或只能以有限合规度够到的输出。
<?php
declare(strict_types=1);
use NextPDF\Compat\Tcpdf\TCPDF;use NextPDF\Contracts\Orientation;use NextPDF\Contracts\OutputDestination;use NextPDF\Core\Document;use NextPDF\ValueObjects\PageSize;
// 1) The bridge: a familiar TCPDF-shaped call keeps producing a file// while the engine underneath is already NextPDF. Behaviour is// compatible, not byte-identical — differences are documented.$legacy = new TCPDF();$legacy->AddPage();$legacy->SetFont('helvetica', 'B', 16);$legacy->Cell(0, 12, 'Migrated invoice', ln: 1);$bridgedBytes = $legacy->Output('', 'S');
// 2) The destination: the same document expressed natively, where intent// is typed and the engine can emit what many legacy tools cannot.$document = Document::createStandalone();$document->setTitle('Migrated invoice');$document->addPage(PageSize::a4(), Orientation::Portrait);$document->setFont('helvetica', 'B', 16);$document->cell(0, 12, 'Migrated invoice', newLine: true);
// Bytes only, no HTTP headers, no file side effect — stated, not inferred.$nativeBytes = $document->output(dest: OutputDestination::String);第一段是那个立足点:你应用里的任何东西都无需改动,文件就能继续流动。第二段是那个目的地:一次类型化的调用,其中“纵向”“字符串输出”和字体都是显式的,而归档、签名和无障碍则成为你可以打开的输出,而不是你会撞上去的墙。
常见误解
标题为“常见误解”的章节人们常抱的希望是“一定有个开关能让我的旧库做出 PDF 2.0 和签名”。并没有。这些不是一个成熟的库忘了暴露的选项;它们是其架构从来就没有围绕之构建的能力。你无法靠配置去得到一个写入器并未实现的格式版本或签名配置文件。
与之相反的误解是,以为 NextPDF 是一个 100% TCPDF 直接替换,所以迁移是免费的。它不是,我们也不会假装它是。兼容层覆盖了 API 中一个真实、有书面记录的切片,把你带过这次迁移;有些调用行为不同,还有一些则在范围之外。请把它当作一座有公开地图的桥,而不是一个“每个遗留脚本都能原封不动跑”的保证。
限制与边界
标题为“限制与边界”的章节| Edition | Availability |
|---|---|
| Core | 这个兼容层是与 TCPDF 兼容,而非字节级一致。 它覆盖 API 的一个有书面记录的子集,以便在迁移期间让既有调用点持续生产文件。它是一座桥,而不是一个直接替换:有些行为不同, 有些调用不受支持,全部都列在方法覆盖页和迁移页里。 那个目的地是类型化的原生 API,标准级输出就住在那里。 |
| Pro | Available |
| Enterprise | Available |
迁移是一种手段,而不是一种美德。如果你的文件很简单、你的库仍在维护、而且你永远不会需要 PDF 2.0、签名、PDF/A 或无障碍,那么诚实的答案也许是留在原地——切换成本是真实的,而一次你不需要的迁移,是一次你不该做的迁移。何时不该使用 NextPDF 一页毫不回避地划出了那条线。
本页描述的是迁移路径和引擎的目标。确切的 API 覆盖范围、行为差异,以及一步步的操作流程,都住在兼容文档里,它才是“每个调用做什么”的权威。这里没有任何内容承诺一个任意的遗留脚本能原封不动地跑。
相关文件
标题为“相关文件”的章节- PDF 2.0 变了什么——许多遗留库无法发出的那个格式版本,以及它为何要紧。
- TCPDF 兼容层——关于这座桥覆盖什么、在哪里存在差异的权威指南。
- 何时不该使用 NextPDF——那条诚实的边界,好让一次你不需要的迁移成为一次你可以跳过的迁移。
- 一个引擎,适配每个框架——你迁移到的那个引擎,如何接入你已经在运行的技术栈。
词汇表
标题为“词汇表”的章节- PDF 2.0——可移植文件格式标准的当前版本(ISO 32000-2)。首次出现时给出全称;是 NextPDF 默认写入的格式。
- PDF/A——归档合规系列(ISO 19005 系列),它定义了让一个 PDF 可被长期安全保存的要素。这是写入器必须生产出来的属性,而不是调用方能事后添加的属性。
- PAdES——PDF 高级电子签名,是用于在 PDF 中嵌入标准级签名的 ETSI 配置文件系列(EN 319 142)。首次出现时给出全称;在签名相关页面里有深入讲解。
- 兼容层——一个形状像某个源库(此处为 TCPDF)的 API 层,让既有调用点在迁移期间继续工作。与原库兼容,而非字节级一致——是一座桥,而不是一个直接替换。
- 直接替换(drop-in replacement)——一个能原封不动跑既有代码的替代品。TCPDF 兼容层有意不这样描述自己;它是一种有书面记录、带有已知行为差异的迁移辅助。