跳到內容
getnextpdf.com

從 FPDF 遷移到 NextPDF

本指南幫你把一份以 FPDF 為基礎的程式碼搬移到 NextPDF core。FPDF 是部署最廣的舊式 PHP 可攜式文件格式(PDF)函式庫之一,而它的繪圖介面——由一個手動 x/y 游標驅動的 AddPageSetFontCellMultiCellWriteTextImageOutput—— 乾淨地對映到 NextPDF 自己的 cell/text API,因為 NextPDF 的低階繪圖方法遵循同樣的 FPDF/TCPDF 血統。不過 NextPDF 是一個可直接替換的 FPDF 複製品:它是一個現代 PDF 2.0 引擎,帶有嚴格型別、 字型子集化、簽章、PDF/A 與無障礙性(tagged PDF)。兩個真正的轉變是 單位模型(NextPDF 以 PDF 點工作;FPDF 預設為公釐)與 輸出動詞(一個有型別的 OutputDestination 列舉,取代 FPDF 的 'I'/'D'/'F'/'S' 字元)。

core 中沒有 FPDF 類別的相容層。請用 動詞對映 改寫每一處呼叫點。如果你想要為一份 TCPDF 6.x 程式碼做最小的初始改動,請改見 TCPDF 相容轉接器,它隨附一條近乎原始碼相容的可直接替換路徑;FPDF 沒有這樣的轉接器。

Terminal window
composer require nextpdf/core:^3

遷移期間請保留 setasign/fpdf(或你的 fpdf/fpdf)的安裝。等到最終切換之後再移除它(見 安全遷移順序)。

FPDF 與 NextPDF 共用同樣的心智模型:一份由頁面構成的文件、一個 游標(目前的 x/y 位置),以及在游標處繪製或推進游標的動詞。SetXYCellLnMultiCell 在兩個函式庫中都會讀取並變更游標,所以大多數程序式 FPDF 程式碼能逐行翻譯。

這些差異是刻意的,不是意外:

  • 單位。 FPDF 的建構式(new FPDF($orientation, $unit, $size))預設為公釐。NextPDF 以 PDF 點工作(1 pt = 1/72 in,ISO 32000-2 §7)。 沒有文件全域的單位旋鈕——把 mm 轉成點一次 (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 字型目錄,並以名稱選取字族。

下面用到的核心進入點是 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): voidDocument::getPdfData(): string,以及 NextPDF\Core\Config 值物件。這些核心繪圖、文字與輸出方法的完整參考,位於 核心模組參考索引,由 PHPDoc 自動產生。 Html 模組 是 HTML 轉 PDF 的相關閱讀,而不是本頁這些動詞的參考。

FPDF 的公開方法名稱由來已久且廣為人知。下面的 NextPDF 欄已對照核心原始碼簽章確認(見 佐證/可追溯性)。

FPDFNextPDFNotes
new FPDF($orient, $unit, $size)Document::createStandalone($config)方向/單位/尺寸建構式引數變成一個 NextPDF\Core\ConfigpageSizemarginsfontsDirectory)。沒有 $unit——以點工作。預設 createStandalone() 頁面是 A4 直向。
$pdf->AddPage($orient, $size)$doc->addPage($size, $orientation)直接對映。$size 是一個 PageSize 值物件;$orientationOrientation 列舉(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)直接對映。$alignAlignment 列舉(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(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 沒有 bottom 引數(它的 bottom 邊界來自 SetAutoPageBreak($auto, $margin)),所以請自己選 $bottom——常見做法是等於 top 邊界,或傳入自動分頁邊界。
$pdf->SetAutoPageBreak($auto, $margin)$doc->setAutoPageBreak($auto, $margin)直接對映。
$pdf->SetDrawColor / SetFillColor / SetTextColor$doc->setDrawColor / setFillColor / setTextColorRGB (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 列舉;注意 引數順序互換(在 NextPDF 中 name 在前)。
$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)。目的地是 OutputDestination 列舉,而非一個字元。檔案 / 字串輸出請優先用 save() / getPdfData()
  • SetMargins 順序。 FPDF 是 (left, top, right);NextPDF 的 Margin 值物件是 (top, right, bottom, left)。重新排序,不要照抄。
  • 字型。 FPDF 的 AddFont() + .php 度量檔沒有對等項。把 TrueType/OpenType 檔放進字型目錄,並用字族名稱呼叫 setFont()。 核心 Base14 名稱(HelveticaTimesCourier)不需檔案即可解析; 在 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() 會在讀取前拒絕 stream-wrapper schemes 與內嵌的 NUL 位元組。請傳入應用程式掌控的路徑。
  • 沒有文件內的程式碼。 NextPDF 不執行任何文件內指令碼;FPDF 中沒有任何東西能改變這一點。
StatementSpecClause
頁面格式/方向對映到頁面邊界框。ISO 32000-2§7
字型寫成內嵌/子集字型程式。ISO 32000-2§9
標題 / 中繼資料落在資訊字典 / XMP 中。ISO 32000-2§14
線條、矩形與影像是內容串流繪製。ISO 32000-2§8

NextPDF 產生 ISO 32000-2 內容;它並未主張與 FPDF 視覺上完全一致。每當你更換渲染器時,請重新審查輸出。

不適用。NextPDF core 涵蓋這裡描述的 FPDF 遷移路徑。


在伺服器端、以 FPDF(或 tFPDF)做程序式 PDF 產生的團隊。如果你的程式碼是一連串由 SetXYLn 驅動的 AddPage / SetFont / Cell / MultiCell / Image / Output 呼叫,那麼 動詞對映 就涵蓋你的整個介面。

範圍內:FPDF 繪圖動詞、游標模型、字型、色彩、線條與矩形、中繼資料,以及輸出。範圍外:FPDF 的 AddFont 度量檔工具,以及第三方 FPDF 指令碼擴充功能(條碼、旋轉、書籤) ——把那些對映到對應的 NextPDF 模組(Barcode、Transforms、 Navigation),本文不涵蓋。

行為相容,並非可直接替換的 shim:core 不提供 FPDF 類別 shim。請改寫每一處呼叫點。這些動詞之所以對得這麼齊,是因為 NextPDF 的 cell/text API 共享 FPDF/TCPDF 血統,但單位模型、Output 引數順序,以及 Margin/列舉型別都不同——所以照抄是錯的, 翻譯才是對的。

FPDF constructNextPDFNotes
$unit(預設 'mm'(無對等項)以 PDF 點工作。在移植時用 pt = mm * 72 / 25.4 轉換尺寸一次。
$orientation'P'/'L'addPage() 上的 Orientation 列舉,或對調 PageSize 寬/高橫向 = 寬 > 高。
$size'A4'[w,h]Config->pageSizePageSize 值物件)具名格式變成明確的點尺寸;PageSize::A4()A0()Letter/Legal 工廠存在。
SetMargins($l, $t, $r)Config->marginsMargin 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 引數順序互換,且目的地變成一個列舉。
  • Margin / Alignment / Orientation 是有型別的物件/列舉,而非字元或位置式的 (l, t, r) 三元組。
  • 沒有公開的 GetStringWidth — 透過 multiCell() 驅動斷字。
  • 獨立的點陣化 — 密集內容上的斷行與分頁可能不同;請重新建立視覺差異的基準。

這些是有記錄的行為差異,不是任一引擎的缺陷。

  • FPDF $unit 選擇器 — 未建模(一律是點)。
  • AddFont() + .php/.z 度量檔 — 由一個字型目錄取代。
  • GetStringWidth() — 沒有公開的字串量測動詞。
  • FPDF 的 'I'/'D'/'F'/'S' 目的地字元 — 由 OutputDestination 列舉 + save()/getPdfData() 取代。

依賴這些的程式碼無法逐字「遷移」。請用上面的列重新表達它。

  1. nextpdf/core 與 FPDF 並存安裝;暫時保留 FPDF。
  2. 選一份低風險的文件。透過 單位對映 轉換建構式,再用 動詞對映 移植每個動詞。把每一個 mm 座標轉成點。
  3. 把該文件的字型放進 Config->fontsDirectory,並以字族名稱選取它們;丟掉 AddFont 呼叫。
  4. 對同一份輸入產生兩份 PDF 並做視覺差異比對。差異 (字型替換、斷行)對獨立引擎而言是預期的——請以文件為單位接受它們。
  5. 把任何以 GetStringWidth 為基礎的手動排版,換成 multiCell() 或固定寬度的 cell() 呼叫。
  6. 逐份文件重複,從最低風險開始;保留 FPDF 安裝直到最後一次切換。
  7. 最終切換之後,把 FPDF 從 composer.json 移除。
  • 在你改動程式碼之前,先快照代表性文件的 FPDF 輸出 (golden 輸入;位元組會有所不同)。
  • 對每一份遷移的文件,用你自己的檢查斷言驗收(視覺差異比對 + 文字擷取)。NextPDF 的 cell/font 行為由 examples/04-text-and-fonts.php 加上核心 tests/ 的 Font 與 text-output 套件實際驗證。遷移驗收因文件而異,並由你自行負責。
  • 為每一份遷移的文件加上一個回歸測試。

本頁每一項 NextPDF 行為陳述,都有儲存庫內的原始碼簽章、範例或架構決策記錄(ADR)支持,或對 PDF 格式特性而言,由 front-matter citations: 中的 ISO 32000-2 條款與 符合性 表佐證。FPDF 行為僅以 「獨立引擎 — 預期會有記錄在案的差異」來陳述;本頁不主張任何儲存庫內成品無法證明的對等性。

NextPDF behavioral claimIn-repo evidence (path)
AddPage 對映到 addPage(?PageSize, Orientation): staticsrc/Core/Concerns/HasPages.phpaddPage())。
SetFont($family, $style, $size) 對映到 setFont(string, string, float): static''/'B'/'I'/'BI'/'U' 樣式。src/Core/Concerns/HasTypography.phpsetFont())。
Cell 對映到 cell($w, $h, $txt, $border, $newLine, $align, $fill): staticsrc/Core/Concerns/HasTextOutput.phpcell())。
MultiCell 對映到 multiCell($w, $h, $txt, $border, $align): static(依度量斷字)。src/Core/Concerns/HasTextOutput.phpmultiCell()wrapText())。
Write/Text/Ln 對映到 write()/text()/ln()src/Core/Concerns/HasTextOutput.phpwrite()text()ln())。
SetXY/SetX/SetY/GetX/GetY 直接對映;SetMargins 接受一個 Margin VO。src/Core/Concerns/HasPages.phpsetXY()getX()setMargins());src/ValueObjects/Margin.php(top, right, bottom, left))。
Image 對映到 image($file, ?$x, ?$y, ?$w, ?$h): static;拒絕 scheme/NUL 路徑。src/Core/Concerns/HasImages.phpimage()assertImageFilePath())。
Line/Rect/SetLineWidth/SetDrawColor/SetFillColor/SetTextColor 直接對映。src/Core/Concerns/HasDrawing.phpline()rect()setLineWidth());src/Core/Concerns/HasColors.phpsetDrawColor()setFillColor()setTextColor())。
createStandalone() 的預設頁面是 A4 直向(595.276 × 841.890 pt)。src/Core/Document.phpcreateStandalone());src/ValueObjects/PageSize.phpA4())。
輸出目的地是 OutputDestination 列舉(Inline/Download/File/String);Output('S')getPdfData()Output('F', $p)save($p)src/Contracts/OutputDestination.phpsrc/Core/Concerns/HasOutput.phpoutput())。
SetTitle/SetAuthor/… 對映到有型別的中繼資料 setter;落在資訊字典 / XMP 中。src/Core/Concerns/HasMetadata.phpsetTitle()setAuthor());ISO 32000-2 §14(front-matter citations:)。
字型一律以子集程式的形式內嵌。src/Core/Concerns/HasTypography.phpbuildFontData());ISO 32000-2 §9(front-matter citations:)。
內容以單次處理發射。docs/architecture/adr/ADR-001-stream-based-rendering-pipeline.md

兩個套件都會保留安裝直到最終切換,所以逐一呼叫點的回滾,就是把該呼叫點還原回 FPDF 路徑。最終切換之後, 回滾就是從版本控制還原 FPDF 與先前的程式碼。不涉及任何資料遷移。

效能。單次處理模型移除了任何保留緩衝區的成本。新增的逐文件成本是急切的字型解析(步驟 3), 可透過字型目錄快取。

  • 把公釐座標照抄成點,卻沒做 * 72 / 25.4 轉換。
  • Output() 留在 FPDF 的 ($dest, $name) 順序,或傳入一個字元而非 OutputDestination 列舉。
  • SetMargins($l, $t, $r) 直接照抄進 Margin(它的順序是 top, right, bottom, left)。
  • 期望 AddFont 度量檔能移植;改把 TTF/OTF 放進字型目錄。
  • 去找一個 GetStringWidth 對等項;改用 multiCell() 來斷字。
  • 期望輸出 byte/pixel-identical(獨立引擎 — 本指南從不主張可直接替換或 100% 相容)。