从 FPDF 迁移到 NextPDF
本指南帮助你把一个基于 FPDF 的代码库迁移到 NextPDF 核心。FPDF 是部署最广泛的旧式 PHP 可移植文档格式(PDF)库之一,而它的绘图界面——由一个手动 x/y 游标驱动的 AddPage、SetFont、Cell、MultiCell、Write、Text、Image、Output——干净地映射到 NextPDF 自己的 cell/text API 上,因为 NextPDF 的低层绘图方法沿袭了相同的 FPDF/TCPDF 血统。不过,NextPDF 并非 FPDF 的直接替代克隆:它是一个现代 PDF 2.0 引擎,具备严格类型、字体子集化、签名、PDF/A 以及无障碍性(tagged PDF)。两个真正的转变是单位模型(NextPDF 以 PDF 点为单位工作;FPDF 默认为毫米)以及输出动词(一个带类型的 OutputDestination enum,而不是 FPDF 的 'I'/'D'/'F'/'S' 字符)。
核心中没有 FPDF 类垫片。请使用动词映射重写每个调用点。如果你想为一个 TCPDF 6.x 代码库做最小的初始改动,请改看 TCPDF 兼容适配器,它出货一条近乎源码兼容的直接替代路径;FPDF 没有这样的适配器。
composer require nextpdf/core:^3在迁移期间保留 setasign/fpdf(或你的 fpdf/fpdf)已安装。在最终切换之后再移除它(参见安全迁移序列)。
概念总览
标题为“概念总览”的章节FPDF 与 NextPDF 共享相同的心智模型:一份由页面构成的文档、一个游标(当前的 x/y 位置),以及在该游标处绘制或推进它的动词。SetXY、Cell、Ln 与 MultiCell 在两个库中都会读取并改变游标,因此大多数过程式 FPDF 代码可以逐行翻译。
这些差异是刻意的,而非偶然的:
- 单位。 FPDF 的构造函数(
new FPDF($orientation, $unit, $size))默认为毫米。NextPDF 以 PDF 点为单位工作(1 pt = 1/72 in,ISO 32000-2 §7)。没有文档级的单位旋钮——把毫米一次性转换为点(pt = mm * 72 / 25.4)。 - Y 方向对你而言保持不变。 与 FPDF 一样,NextPDF 的用户坐标把
y = 0置于页面顶部并向下递增,因此游标算术可直接移植。NextPDF 在内部转换为 PDF 原生的左下角原点。 - 构造是显式的。 FPDF 把朝向、单位与尺寸折叠进构造函数;NextPDF 接受一个不可变的
NextPDF\Core\Config值对象(页面尺寸、边距、字体目录)以及一个显式的addPage()。 - 始终 Unicode,始终子集化。 FPDF 的核心构建是 Latin-1,需要 tFPDF/UTF-8 变体才能支持 Unicode。NextPDF 通篇是 UTF-8,并始终把字体作为子集程序嵌入(ISO 32000-2 §9)。FPDF 的
AddFont/字体度量文件没有对应物;注册一个 TrueType/OpenType 字体目录,并按名称选择字体家族。
API 接口
标题为“API 接口”的章节下文用到的核心入口点是 Document::createStandalone()、Document::addPage()、Document::setFont()、Document::cell()、Document::multiCell()、Document::text()、Document::write()、Document::ln()、Document::image()、游标访问器(setXY/setX/setY/getX/getY)、Document::output(?string, OutputDestination)、Document::save(string $path): void、Document::getPdfData(): string,以及 NextPDF\Core\Config 值对象。这些核心绘图、文本与输出方法的完整参考位于核心模块与参考索引中,由 PHPDoc 自动生成。Html 模块 是 HTML-to-PDF 的相关读物,而不是本页这些动词的参考。
API 动词映射
标题为“API 动词映射”的章节FPDF 的公共方法名由来已久且广为人知。下方的 NextPDF 列已对照核心源码签名核实(参见证据 / 可追溯性)。
| FPDF | NextPDF | 说明 |
|---|---|---|
new FPDF($orient, $unit, $size) | Document::createStandalone($config) | 朝向/单位/尺寸构造参数变成一个 NextPDF\Core\Config(pageSize、margins、fontsDirectory)。没有 $unit——以点为单位工作。默认 createStandalone() 页面是 A4 纵向。 |
$pdf->AddPage($orient, $size) | $doc->addPage($size, $orientation) | 直接映射。$size 是一个 PageSize 值对象;$orientation 是 Orientation enum(Portrait/Landscape)。 |
$pdf->SetFont($family, $style, $size) | $doc->setFont($family, $style, $size) | 直接映射。$style 使用相同的 ''/'B'/'I'/'BI'(外加 'U' 下划线)代码。 |
$pdf->Cell($w, $h, $txt, $border, $ln, $align, $fill) | $doc->cell($w, $h, $txt, $border, $newLine, $align, $fill) | 直接映射。$align 是 Alignment enum(Left/Center/Right/Justify);$border 接受 bool 或一个 'LTRB' 字符串;$ln 变成 bool $newLine。 |
$pdf->MultiCell($w, $h, $txt, $border, $align, $fill) | $doc->multiCell($w, $h, $txt, $border, $align) | 基于真实字体度量进行单词换行。没有 $fill 参数;如果你需要背景,请先绘制一个填充的 rect()。 |
$pdf->Write($h, $txt, $link) | $doc->write($h, $txt, $link) | 从游标开始的流动文本;$link 附加一个 URL 链接注释。 |
$pdf->Text($x, $y, $txt) | $doc->text($x, $y, $txt) | 绝对位置文本。直接映射。 |
$pdf->Ln($h) | $doc->ln($h) | 换行到左边距;0 = 默认行高。 |
$pdf->Image($file, $x, $y, $w, $h) | $doc->image($file, $x, $y, $w, $h) | 直接映射;$x/$y/$w/$h 可空(null = 当前游标 / 固有尺寸)。 |
$pdf->SetXY($x, $y) / SetX / SetY | $doc->setXY($x, $y) / setX / setY | 直接映射。getX()/getY() 读取游标。 |
$pdf->SetMargins($l, $t, $r) | $doc->setMargins(new Margin($t, $r, $bottom, $l)) | 一个 Margin 值对象;构造顺序是 (top, right, bottom, left)——不是 FPDF 的 (left, top, right)。FPDF SetMargins 没有底部参数(其底部边距来自 SetAutoPageBreak($auto, $margin)),因此请自行选择 $bottom——通常等于顶部边距,或传入自动分页边距。 |
$pdf->SetAutoPageBreak($auto, $margin) | $doc->setAutoPageBreak($auto, $margin) | 直接映射。 |
$pdf->SetDrawColor / SetFillColor / SetTextColor | $doc->setDrawColor / setFillColor / setTextColor | RGB (r, g, b),或单个值表示灰度。 |
$pdf->Line / Rect / SetLineWidth | $doc->line / rect / setLineWidth | 直接映射。rect() 接受一个样式字符串('S'/'F'/'DF')。 |
$pdf->SetTitle/SetAuthor/SetSubject/SetKeywords/SetCreator | $doc->setTitle/setAuthor/setSubject/setKeywords/setCreator | 直接映射。落入 ISO 32000-2 §14 信息字典 / 可扩展元数据平台(XMP)。 |
$pdf->Output($dest, $name) | $doc->output($name, OutputDestination::…) | FPDF 目标字符(I/D/F/S)映射到 OutputDestination enum;注意参数顺序互换(在 NextPDF 中名称在前)。 |
$pdf->Output('S') | $doc->getPdfData() | 返回 PDF 字节。 |
$pdf->Output('F', $path) | $doc->save($path) | 写入到一个文件路径。 |
$pdf->GetStringWidth($s) | (无公共方法) | 字符串宽度在 cell()/multiCell() 换行期间于内部计算;没有公共的按字符串测量动词。请通过 multiCell() 驱动换行,而不是手动测量。 |
代码示例 — 快速上手
标题为“代码示例 — 快速上手”的章节<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\Alignment;use NextPDF\Core\Document;
// FPDF:// $pdf = new FPDF(); // mm, A4 portrait// $pdf->AddPage();// $pdf->SetFont('Arial', 'B', 16);// $pdf->Cell(40, 10, 'Invoice');// $pdf->Output('F', 'out.pdf');
// NextPDF — points, default page is A4 portrait:$doc = Document::createStandalone();$doc->setTitle('Invoice');$doc->addPage();$doc->setFont('Helvetica', 'B', 16.0);$doc->cell(113.4, 28.3, 'Invoice', false, true, Alignment::Left); // ~40mm x ~10mm in points$doc->save(__DIR__ . '/out.pdf');
echo "Wrote out.pdf\n";代码示例 — 生产环境
标题为“代码示例 — 生产环境”的章节这个示例与 examples/04-text-and-fonts.php 对齐。它使用一个显式的页面尺寸、边距、一个已注册的字体目录,以及一个 FPDF 代码库已在使用的游标驱动 cell 模型。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\Alignment;use NextPDF\Contracts\OutputDestination;use NextPDF\Core\Config;use NextPDF\Core\Document;use NextPDF\ValueObjects\Margin;use NextPDF\ValueObjects\PageSize;
// Equivalent of: new FPDF('P', 'mm', 'A4') + SetMargins(20, 16, 20)// i.e. FPDF left=20mm, top=16mm, right=20mm. FPDF SetMargins has no bottom// argument, so we pick bottom = top = 16mm. Convert each mm to points// (pt = mm * 72 / 25.4): 16mm = 45.354pt, 20mm = 56.693pt.// Margin constructor order is (top, right, bottom, left) — NOT FPDF's (L, T, R).$config = new Config( pageSize: new PageSize(595.276, 841.890, 'A4'), margins: new Margin(45.354, 56.693, 45.354, 56.693), // top,right,bottom,left in points fontsDirectory: __DIR__ . '/fonts',);
$doc = Document::createStandalone($config);$doc->setTitle('Quarterly Report');$doc->setAuthor('Finance');$doc->addPage();
// SetFont + Cell, the FPDF way — but in points and with a real Unicode font.$doc->setFont('DejaVuSans', 'B', 18.0);$doc->setTextColor(30, 58, 138);$doc->cell(0, 24.0, 'Quarterly Report', false, true, Alignment::Left);
$doc->setFont('DejaVuSans', '', 11.0);$doc->setTextColor(0, 0, 0);$doc->multiCell(0, 16.0, "Body text wraps on real font metrics. Unicode is " . "native, so accented and non-Latin characters need no tFPDF variant — " . "register the family in the fonts directory and select it by name.");
// Equivalent of $pdf->Output('D', 'report.pdf'):$doc->output('report.pdf', OutputDestination::Download);边界情况与陷阱
标题为“边界情况与陷阱”的章节- 单位。 你从 FPDF 复制过来的每个数值坐标、宽度、高度与边距,默认都以毫米计。在移植期间,把它们乘以
72 / 25.4一次以得到点。两者混用会悄无声息地把一切尺寸算错。 Output()参数顺序。 FPDF 是Output($dest, $name);NextPDF 是output($name, $dest)。目标是OutputDestinationenum,而不是一个字符。对于文件 / 字符串输出,请优先使用save()/getPdfData()。SetMargins顺序。 FPDF 是(left, top, right);NextPDF 的Margin值对象是(top, right, bottom, left)。请重新排序,不要照搬。- 字体。 FPDF 的
AddFont()+.php度量文件没有对应物。把 TrueType/OpenType 文件放进字体目录,并用家族名称调用setFont()。核心 Base14 名称(Helvetica、Times、Courier)无需文件即可解析;在 PDF/A 或 tagged PDF 下,它们会被自动替换为一个可嵌入字体。 GetStringWidth。 没有公共的字符串测量方法。如果你的 FPDF 代码通过测量字符串来手动布局列,请把那一段切换到multiCell()(它基于度量换行)或固定宽度的cell()调用。
NextPDF 以单遍流式(架构决策记录 ADR-001)发出内容;峰值内存跟踪文档大小,而不是一个被保留的对象树。本指南示例的预算是 wall_ms: 2000, peak_mb: 128。对于长文档,请跨 addPage() 调用驱动内容——这与一个 FPDF 报表已在使用的循环形态相同。
安全注意事项
标题为“安全注意事项”的章节- 元数据。
SetTitle()/SetAuthor()映射到写入 ISO 32000-2 §14 信息字典 / XMP 的带类型 setter。绝不要在那里存储秘密。 - 图像路径。
image()在读取前拒绝流包装器协议与内嵌的 NUL 字节。请传入应用控制的路径。 - 没有文档内代码。 NextPDF 不执行任何文档内脚本;FPDF 中的任何东西都不会改变这一点。
合规性
标题为“合规性”的章节| 陈述 | 规范 | 条款 |
|---|---|---|
| 页面格式/朝向映射到页面边界框。 | ISO 32000-2 | §7 |
| 字体作为嵌入的/子集的字体程序写入。 | ISO 32000-2 | §9 |
| 标题 / 元数据落入信息字典 / XMP。 | ISO 32000-2 | §14 |
| 线条、矩形与图像是内容流绘制。 | ISO 32000-2 | §8 |
NextPDF 产出 ISO 32000-2 内容;它不主张与 FPDF 的视觉一致性。每当你更换渲染器时,请重新审阅输出。
商业背景
标题为“商业背景”的章节不适用。NextPDF 核心覆盖此处所述的 FPDF 迁移路径。
另请参阅
标题为“另请参阅”的章节迁移细节(R6 必需章节)
标题为“迁移细节(R6 必需章节)”的章节这是为谁准备的
标题为“这是为谁准备的”的章节运行 FPDF(或 tFPDF)进行服务器端、过程式 PDF 生成的团队。如果你的代码是一连串由 SetXY 与 Ln 驱动的 AddPage / SetFont / Cell / MultiCell / Image / Output 调用,那么动词映射就覆盖了你的整个界面。
在范围内:FPDF 绘图动词、游标模型、字体、颜色、线条与矩形、元数据,以及输出。不在范围内:FPDF 的 AddFont 度量文件工具,以及第三方 FPDF 脚本扩展(条码、旋转、书签)——请把那些映射到相应的 NextPDF 模块(Barcode、Transforms、Navigation),此处不予涵盖。
兼容性映射
标题为“兼容性映射”的章节行为上的兼容性,而非直接替代垫片:核心不提供 FPDF 类垫片。请重写每个调用点。这些动词高度对齐,是因为 NextPDF 的 cell/text API 共享 FPDF/TCPDF 血统,但单位模型、Output 参数顺序,以及 Margin/enum 类型有所不同——所以照搬是错的,翻译才是对的。
单位与配置映射
标题为“单位与配置映射”的章节| FPDF 构造 | NextPDF | 说明 |
|---|---|---|
$unit(默认 'mm') | (无对应物) | 以 PDF 点为单位工作。在移植期间用 pt = mm * 72 / 25.4 一次性转换尺寸。 |
$orientation('P'/'L') | addPage() 上的 Orientation enum,或交换 PageSize 宽/高 | 横向 = 宽 > 高。 |
$size('A4'、[w,h]) | Config->pageSize(PageSize 值对象) | 命名格式变成显式的点尺寸;存在 PageSize::A4()…A0() 以及 Letter/Legal 工厂。 |
SetMargins($l, $t, $r) | Config->margins(Margin VO) | 构造顺序 (top, right, bottom, left)。 |
AddFont($family, $style, $file) | 字体目录 + 按名称 setFont() | 丢弃度量文件;把 TTF/OTF 放进 Config->fontsDirectory。 |
字体处理差异
标题为“字体处理差异”的章节- 字体目录。 FPDF 的每字体
AddFont注册坍缩为一个字体目录加上setFont()家族匹配。从Config->fontsDirectory(默认搜索路径)开始;当字体分布在多处时,通过FontRegistry::addFontDirectory()或Document::addFontDirectory()注册额外目录。 - 始终 Unicode。 没有 Latin-1 默认值,也没有单独的 tFPDF 构建;UTF-8 输入是常态。
- 始终子集化。 NextPDF 始终子集化嵌入字体(ISO 32000-2 §9);FPDF 的字体嵌入选择没有对应物,也不需要。
- 重新基线化字形。 字体匹配与回退是引擎特定的;一个 FPDF 字体别名可能需要一个确切的家族名称。替换差异是预期的,而非缺陷。
行为差异
标题为“行为差异”的章节- 单位转换(mm → pt)——最常见的移植错误;见上文。
Output参数顺序互换,且目标变成一个 enum。Margin/Alignment/Orientation是带类型的对象/enum,而不是字符或位置式的(l, t, r)三元组。- 没有公共的
GetStringWidth——通过multiCell()驱动换行。 - 独立的栅格化——在密集内容上,行换行与分页可能有所不同;请重新基线化视觉差异。
这些是有记录的行为差异,而非任一引擎的缺陷。
不支持 / 无直接对应物
标题为“不支持 / 无直接对应物”的章节- FPDF
$unit选择器——未建模(始终为点)。 AddFont()+.php/.z度量文件——由一个字体目录替代。GetStringWidth()——没有公共的字符串测量动词。- FPDF 的
'I'/'D'/'F'/'S'目标字符——由OutputDestinationenum +save()/getPdfData()替代。
依赖这些的代码不会逐字“迁移”。请用上面的各行重新表达它。
安全迁移序列
标题为“安全迁移序列”的章节- 在 FPDF 旁边加入
nextpdf/core;暂且保留 FPDF 已安装。 - 选择一份低风险文档。通过单位映射转换构造函数,然后用动词映射移植每个动词。把每个毫米坐标转换为点。
- 把该文档的字体放进
Config->fontsDirectory并按家族名称选择它们;丢弃AddFont调用。 - 为相同输入生成两份 PDF,并对它们做视觉差异比对。差异(字体替换、行换行)对于独立引擎是预期的——请逐文档接受它们。
- 把任何基于
GetStringWidth的手动布局替换为multiCell()或固定宽度的cell()调用。 - 逐文档重复,风险最低者优先;在最后一次切换之前保持 FPDF 已安装。
- 在最终切换之后,从
composer.json中移除 FPDF。
测试迁移
标题为“测试迁移”的章节- 在你更改代码之前,为有代表性的文档快照 FPDF 输出(黄金输入;字节将会不同)。
- 对每份已迁移的文档,用你自己的检查(视觉差异 + 文本提取)来断言验收。NextPDF 的 cell/font 行为由
examples/04-text-and-fonts.php加上核心tests/的 Font 与文本输出套件演练。迁移验收是文档特定的,并仍由你负责。 - 为每份已迁移的文档添加一个回归测试。
证据 / 可追溯性
标题为“证据 / 可追溯性”的章节本页上每个 NextPDF 行为陈述,都由一个仓库内的源码签名、示例或架构决策记录(ADR)支撑,或者,对于 PDF 格式属性,由 frontmatter citations: 中的 ISO 32000-2 条款以及合规性表支撑。FPDF 行为仅被断言为“独立引擎——预期有记录在案的差异”;本页不主张任何仓库内制品无法证明的对等性。
| NextPDF 行为主张 | 仓库内证据(路径) |
|---|---|
AddPage 映射到 addPage(?PageSize, Orientation): static。 | src/Core/Concerns/HasPages.php(addPage())。 |
SetFont($family, $style, $size) 映射到 setFont(string, string, float): static;''/'B'/'I'/'BI'/'U' 样式。 | src/Core/Concerns/HasTypography.php(setFont())。 |
Cell 映射到 cell($w, $h, $txt, $border, $newLine, $align, $fill): static。 | src/Core/Concerns/HasTextOutput.php(cell())。 |
MultiCell 映射到 multiCell($w, $h, $txt, $border, $align): static(基于度量的换行)。 | src/Core/Concerns/HasTextOutput.php(multiCell()、wrapText())。 |
Write/Text/Ln 映射到 write()/text()/ln()。 | src/Core/Concerns/HasTextOutput.php(write()、text()、ln())。 |
SetXY/SetX/SetY/GetX/GetY 直接映射;SetMargins 接受一个 Margin VO。 | src/Core/Concerns/HasPages.php(setXY()、getX()、setMargins());src/ValueObjects/Margin.php((top, right, bottom, left))。 |
Image 映射到 image($file, ?$x, ?$y, ?$w, ?$h): static;拒绝 scheme/NUL 路径。 | src/Core/Concerns/HasImages.php(image()、assertImageFilePath())。 |
Line/Rect/SetLineWidth/SetDrawColor/SetFillColor/SetTextColor 直接映射。 | src/Core/Concerns/HasDrawing.php(line()、rect()、setLineWidth());src/Core/Concerns/HasColors.php(setDrawColor()、setFillColor()、setTextColor())。 |
createStandalone() 默认页面是 A4 纵向(595.276 × 841.890 pt)。 | src/Core/Document.php(createStandalone());src/ValueObjects/PageSize.php(A4())。 |
输出目标是 OutputDestination enum(Inline/Download/File/String);Output('S') → getPdfData(),Output('F', $p) → save($p)。 | src/Contracts/OutputDestination.php;src/Core/Concerns/HasOutput.php(output())。 |
SetTitle/SetAuthor/… 映射到带类型的元数据 setter;落入信息字典 / XMP。 | src/Core/Concerns/HasMetadata.php(setTitle()、setAuthor());ISO 32000-2 §14(frontmatter citations:)。 |
| 字体始终作为子集程序嵌入。 | src/Core/Concerns/HasTypography.php(buildFontData());ISO 32000-2 §9(frontmatter citations:)。 |
| 内容以单遍发出。 | docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md。 |
两个包在最终切换之前都保持安装,因此按调用点回滚意味着把那个调用点还原到 FPDF 路径。在最终切换之后,回滚意味着从版本控制恢复 FPDF 与先前的代码。不涉及任何数据迁移。
性能考量
标题为“性能考量”的章节参见性能。单遍模型移除了任何被保留的缓冲成本。新增的每文档成本是急切的字体解析(第 3 步),它可通过字体目录缓存。
常见陷阱
标题为“常见陷阱”的章节- 把毫米坐标照搬为点而不做
* 72 / 25.4转换。 - 让
Output()保持 FPDF 的($dest, $name)顺序,或传入一个字符而不是OutputDestinationenum。 - 把
SetMargins($l, $t, $r)直接照搬进Margin(其顺序是top, right, bottom, left)。 - 指望
AddFont度量文件能移植;请改为把 TTF/OTF 放进字体目录。 - 去找一个
GetStringWidth对应物;请用multiCell()做换行。 - 指望字节/像素一致的输出(独立引擎——本指南从不主张直接替代或 100% 兼容)。