跳到內容

從 Dompdf 遷移到 NextPDF

本指南說明如何將一份以 Dompdf 為基礎的程式庫遷移到 NextPDF 的 Html pipeline。 Dompdf 與 NextPDF 的整體流程相同 —— 載入 HTML、render、輸出 PDF —— 因此大多數呼叫端都能以機械式方式改寫。 真正需要投入心力的是選項對照與 CSS 支援度差異。 NextPDF 與 Dompdf 是各自獨立的引擎,因此 Dompdf 產出的版面僅與 NextPDF 的結果 相容,並非與它逐位元組完全相同。 本指南涵蓋動詞對映、選項鍵對映、行為差異,以及一套安全的遷移步驟。

即使 NextPDF 支援某個 HTML/CSS 功能,也不保證能逐像素重現某份 Dompdf 文件。 哪些功能屬於 Verified,以 CSS 支援度對照表 為準。 本指南描述的是行為;它並不主張視覺上的等價。

Terminal window
composer require nextpdf/core:^3

過渡期間請先保留 dompdf/dompdf 的安裝(安全遷移步驟 會讓兩者並行,直到每個呼叫端都切換完成),待全部切換完成後再將它移除。

Dompdf 的 Dompdf 物件是單一 facade(外觀),同時掌管 DOM、樣式表、frame tree 與畫布。 NextPDF 把這些職責拆開:NextPDF\Core\Document 掌管頁面模型與輸出,並由單一方法 writeHtml() 驅動整條 HTML pipeline。 沒有「先 render、再 output」的兩階段步驟。 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 provenance(來源資訊)附註。 未重製任何上游文件文字。

DompdfNextPDF說明
new Dompdf($options)Document::createStandalone($config)Dompdf 接受一個 Options 物件;NextPDF 則接受一個 NextPDF\Core\Config。 對於長時間執行的 worker,請使用 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 階段。 請移除 render() 這個呼叫。
$dompdf->output()$doc->getPdfData()回傳 PDF 位元組。
$dompdf->stream($name, $opts)$doc->output($name, OutputDestination::Download)NextPDF 透過 OutputDestination enum 來區分輸出目的地。
$dompdf->setBasePath($p) / setProtocol() / setBaseHost()(資源的 resolve(解析)方式不同)NextPDF 是針對文件工作集來解析相對資源,而不是依賴一組基底 path/protocol 三元組 —— 見 行為差異 一節。
$dompdf->addInfo($label, $value)$doc->setTitle() / setAuthor() / 中繼資料 APIDompdf 的自由格式 info 配對,會對映到型別化的中繼資料 setter(ISO 32000-2 §14 文件資訊,iso32000_2_sec14#x1.x5.p5)。
$dompdf->setHttpContext($ctx)(無對應項)NextPDF 不會透過 stream context 抓取遠端資源;見 不支援/無直接對應項 一節。
<?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。 Dompdf 程式碼若會在 render()output() 之間檢查狀態(例如讀取頁數),NextPDF 沒有與那個確切邊界對應的做法。 請改在 writeHtml() 之後查詢文件。
  • 編碼。 NextPDF 捨棄了 Dompdf 的 $encoding 參數:請在 writeHtml() 之前先把輸入轉成 UTF-8。 傳入 Latin-1 位元組會產生亂碼,而不是丟出錯誤。
  • render() 沒移除。 若殘留 $dompdf->render() 這類呼叫,NextPDF 中沒有對應方法,會造成致命的「undefined method」錯誤。 切換時請直接刪掉它,不要做成 stub。
  • 行內 PHP。 Dompdf 的 enable_php 會執行 <script type="text/php">。 NextPDF 在設計上 沒有 任何文件內 PHP 執行(那是一個注入攻擊面)。 請把那段邏輯移到 writeHtml() 之前的 PHP 程式碼中。
  • 相對資源解析。 Dompdf 會依基底 path/protocol/host 三元組解析 <img src> 的 src。 NextPDF 則是針對文件工作集來解析。 遷移期間,請傳入絕對路徑或已預先解析的 data URI,把這個變數消除掉。

writeHtml() 以單一串流走訪完成版面配置(ADR-001)。 版面完成後不會保留任何中間的 frame tree 物件,因此尖峰記憶體取決於文件大小,而非 DOM 節點數量。 本指南範例的效能預算是 wall_ms: 2000, peak_mb: 128。 大型文件:請沿 addPage() 的邊界把 HTML 分塊,而不要組成單一數 MB 的字串。

  • 不會透過 stream context 做遠端抓取。 NextPDF 並未實作 Dompdf 的 setHttpContext() / enable_remote 遠端抓取路徑。 請在你的應用程式裡解析並驗證遠端資產,再把位元組或 data URI 傳進來。 這樣可移除 enable_remote 所帶來的 SSRF 攻擊面。
  • 不做文件內程式碼執行。 沒有 enable_php 的對應項,是刻意的強化,並非缺漏。
  • 你透過型別化 setter 設定的文件中繼資料,會寫入 ISO 32000-2 §14 的資訊字典/XMP(iso32000_2_sec14#x1.x5.p5)。 請勿把祕密放在那裡。
聲明規範條款參考 ID
頁面內容是 opaque/transparent 模型中的內容串流繪製。ISO 32000-2§8
紙張尺寸對映到頁面物件的邊界框。ISO 32000-2§7
HTML 字型會寫成 embedded/subset 字型程式。ISO 32000-2§9
空白字元/斷行處理是各引擎特有的。CSS Text 3(CSS 文字模組第 3 級)§6.5

NextPDF 產出 ISO 32000-2 內容。 它並不主張一份遷移過來的 Dompdf 文件在視覺上完全相同。 一旦更換 renderer(渲染器),就一定要重新審查輸出。

不適用。 核心版已涵蓋此處所述的 HTML 轉 PDF 遷移路徑。


目前使用 dompdf/dompdf 進行伺服器端 HTML 轉 PDF,並想改用 NextPDF 引擎的團隊。 如果你只呼叫 loadHtml / setPaper / render / output,那麼 動詞對映 就涵蓋了你的整個介面。

涵蓋範圍:Dompdf facade 動詞、Options 鍵、CSS 功能對等性的預期、資源解析、中繼資料。 不涵蓋範圍:Dompdf 內部的 FrameTree/Canvas/Stylesheet 物件(NextPDF 沒有公開的對應物 —— 不要遷移會直接存取這些內部物件的程式碼,請改用公開 API 重寫)。

涵蓋的是 行為相容性,而非可直接替換的 shim(相容層)。 NextPDF 沒有 Dompdf 類別 shim(不像 TCPDF 路徑 —— 見 遷移指南 /migration/tcpdf-compat/)。 你需要用 動詞對映 把每個呼叫端重寫一遍。 CSS 支援度對照表 裡標為 Verified 的列,直接決定 CSS 功能上的預期。 本指南不會逐一重述每個屬性的狀態。

Dompdf 選項(鍵/setter)NextPDF說明
default_paper_size / setDefaultPaperSize() ; setPaper($size,...)Config->pageSizePageSize 值物件)具名尺寸改為明確的點數尺寸;new PageSize(595.276, 841.890, 'A4')createStandalone() 的預設值。
default_paper_orientation / setDefaultPaperOrientation()互換 PageSize 的 width/heightNextPDF 沒有方向旗標;橫向頁面就是一個寬度 > 高度的 PageSize
dpi / setDpi()(不是全域旋鈕)NextPDF 以 PDF 點(1/72 吋)為單位運作;影像尺寸是逐張影像決定的,而非套用文件層級的 DPI 倍率。 請把固定的像素尺寸重新換算成點數。
enable_remote / setIsRemoteEnabled()(無對應項 —— 刻意設計)請在你的程式碼裡解析遠端資產;見 安全性注意事項 一節。
enable_html5_parser / setIsHtml5ParserEnabled()(一律剖析 HTML)沒有開關;parser 就是這條 pipeline。
enable_php / setIsPhpEnabled()(無對應項 —— 刻意設計)不支援文件內 PHP;請把邏輯移出範本。
font_dir / setFontDir()Config->fontsDirectory單一字型目錄字串。
chroot(在應用程式裡解析)NextPDF 不接受檔案系統 jail 選項;請在傳入位元組之前先做路徑驗證。
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 接縫。 沒有 render()/output() 的兩階段邊界(見 邊角案例 一節)。
  • 資源解析。 基底 path/protocol/host 對上文件工作集。
  • 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/core 加到 dompdf/dompdf 旁邊(先不要移除 Dompdf)。
  2. 挑一份低風險的文件。 用 動詞對映 重寫它的呼叫端;刪掉 render() 呼叫。
  3. 對相同輸入產生兩份 PDF,再做視覺差異比對。 把差異視為預期之內(各自獨立的引擎),並逐份文件決定是否接受。
  4. 透過 選項對照表 轉換選項用法;把 DPI 推算出的尺寸重新換算成點數。
  5. 請把 remote/relative 資產預先解析成絕對路徑或 data URI,消除解析行為這個變數。
  6. 逐份文件重複進行,從風險最低做到最高。 在最後一個呼叫端切換完成之前,請保持兩個引擎都安裝著。
  7. 只有在最後一次切換完成後,才把 dompdf/dompdfcomposer.json 移除。
  • 在改動程式碼 之前,先將具代表性文件的 Dompdf 輸出建立為快照(golden input,而非 golden bytes —— 位元組一定會不同)。
  • 對每一份遷移過的文件,把 NextPDF 的輸出交給你自己的驗收檢查(視覺差異、文字擷取斷言)。 NextPDF 自身的 HTML pipeline 行為由 examples/08-html-basic.php 以及核心的 tests/ Html 測試套件涵蓋。 你的遷移驗收是逐份文件而定,且要由你自行負責斷言。
  • 請為每一份遷移過的文件加入一個回歸測試,讓未來引擎更新時能被攔截到。

本頁每一條 NextPDF 行為陳述,都有倉庫內的測試、範例、原始碼簽章或 ADR 支撐 —— 或者,當它是一項 PDF 格式屬性時,則由前置設定 citations:相容性聲明 表中由 RAG 釘選的 ISO 32000-2 / 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 enum(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 pipeline(本指南可執行性的佐證)。examples/08-html-basic.php;核心 tests/Unit/Html/ 測試套件。
空白字元/斷行是各引擎特有的(版面差異)。CSS Text 3 §6.5(前置設定 citations: + 相容性聲明)。

由於兩個套件在最終切換完成前都會維持安裝,因此若要回復尚未轉換的呼叫端,就是把那個呼叫端還原成 Dompdf 路徑。 在最終切換完成之後,回復則表示從版本控制還原 dompdf/dompdf 以及先前的呼叫端。 這不涉及任何資料遷移 —— 只有程式碼。

效能 一節。 單一走訪的模型意味著遷移不會引入 frame tree 保留成本。 每份文件主要的成本變化,是預先解析資產(步驟 5),而這個你可以快取起來。

  • render() 留在原處(致命的 undefined method)。
  • 捨棄 $encoding 後又傳入非 UTF-8 位元組(未報錯的亂碼)。
  • 期待輸出逐位元組或逐像素完全相同(各自獨立的引擎 —— 本指南從未主張可直接替換或 100% 相容)。
  • 依賴 enable_php 範本(必須重構移除)。
  • 把 CSS 支援度對照表當成僅供參考 —— 它是判定該預期什麼的 Verified 功能權威。