跳到內容
getnextpdf.com

從舊式函式庫遷移:TCPDF、FPDF 及其同類

Spec: ISO 32000-2Spec: ISO 19005-4Spec: ETSI EN 319 142-1

如果你的 PDF 是由 TCPDF、FPDF、mPDF 或 dompdf 產生的,那些程式碼大概還能用。這正是麻煩之所以容易被忽略的原因。函式庫照常執行,檔案照常打開,而那道落差只會在某天有人要一份已簽署、可封存或無障礙的文件、而答案是「我們從這裡做不出來」時,才會浮現。

本頁是那則遷移故事:那些牆是什麼、為什麼它們是結構性的而非偶發性的,以及 NextPDF 如何給你一條分階段、避開它們的路徑——包括一個 TCPDF 相容層,它是一項遷移輔助,而非一個位元組逐一相同、可直接替換的承諾。

一個 PDF 函式庫不是你呼叫一次的算繪呼叫。它是你的文件只要還存在、就會一直繼承下去的相依套件。當那個相依套件停止前進時,你的文件就無法再做新的事情——而你會在最糟糕的時刻發現這件事,也就是某個客戶、某位稽核員或某個監理機關把門檻設下來的那一刻。

那些牆長這個樣子。格式向前走了:PDF 2.0 是這項標準的現行版本(Spec: ISO 32000-2),而一個卡在 1.x 結構上的寫入器,已落後於你工具鏈其餘部分所預設的格式。簽署能力薄弱或是事後硬接上去的,遠遠搆不上那些讓簽章站得住腳的 PAdES 基準設定檔(Spec: ETSI EN 319 142-1, §4)。輸出至 PDF/A 系列的封存格式,以及為無障礙而設的標記結構,不是缺席就是脆弱。而 API 本身缺乏型別——字串式的方向、位置式的布林值、靠意外才發現的預設值——所以編譯器幫不了你,審查者也幫不了你。

這些都不是你能繞過去修補的臭蟲。它們是一個為更早年代而打造的工具所具有的形狀,而其中好幾個工具,如今已不再積極地朝你文件現在必須符合的那些標準前進。

  • 舊式 PHP PDF 函式庫多半仍能執行。問題在於它們通常無法以完整的現代一致性產出什麼:PDF 2.0、符合基準的簽章、經驗證的 PDF/A、已標記的無障礙結構——這些在那幾個指名的函式庫之間,支援程度有限或付之闕如。
  • NextPDF 是一個預設寫入 PDF 2.0 的 PHP 8.4 引擎,並把嚴格型別、封存設定檔,以及 PAdES 簽署作為第一級的輸出。
  • 你不必在第一天就把一切重寫。TCPDF 相容層讓熟悉的呼叫繼續運作,而你同時去搬動那些真正重要的文件邏輯。
  • 那一層與 TCPDF 相容,但並非位元組逐一相同。它是一座跨越遷移過程的橋,附帶已載明的行為差異——而非主張每個指令稿都能原封不動地執行。
  • 誠實的檢驗是:那些新能力是否值得這趟搬遷。對某些工作負載而言並不值得,而我們會直白地這麼說。

這套做法是把遷移變成一個序列,而非一次飛躍。你一路上都持續產出文件,並且一次只換掉一項舊有的限制,而不是把一次發行押注在一場一次到位的大改寫上。

  1. InventoryCatalogue what your documents actually need to emit — signatures, archival profiles, tagged structure, fonts — not just which calls you make today.
  2. BridgeAdopt the TCPDF-compatibility surface so the existing call sites keep producing files while the engine underneath becomes NextPDF.
  3. PortMove the document logic that matters onto the native typed API, where intent is explicit and the compiler checks it.
  4. UpgradeTurn on the outputs many legacy libraries cannot reach with full modern conformance: PDF 2.0 structure, validated PDF/A, PAdES signatures, tagged accessibility.
  5. VerifyConfirm the result against a real validator, so 'archival' or 'signed' means a tool agrees, not just that the file opened.
A staged migration off a legacy PDF library: start on the compatibility surface so existing calls keep working, then move document logic onto the typed native API, then turn on the standards-grade outputs (PDF 2.0, PDF/A, PAdES, accessibility) that many legacy libraries cannot produce with full modern conformance.

PDF 2.0 是基準,不是一個功能旗標。 NextPDF 預設寫入這個格式的現行版本(Spec: ISO 32000-2),並能在某個設定檔有此要求時序列化較舊的結構。一個凍結在 1.x 結構上的函式庫無法在這裡與你會合;那不是它漏掉的一項設定,而是一個它早於其問世的年代。

封存與無障礙是寫入器的性質。 產出一個驗證器會接受為 PDF/A 的檔案,是引擎在寫入過程中就必須做到的事——它無法在事後被硬釘上去(Spec: ISO 19005-4)。讓 PDF 變得無障礙的那套標記結構也是如此。NextPDF 在產生過程中就建構這些,而那恰恰是許多舊式工具無法踏出的那一步——或者只能部分做到,搆不上驗證器所接受的程度。

簽署搆得上基準的門檻。 PDF 中的進階電子簽章遵循 PAdES 設定檔(Spec: ETSI 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 中一個真實、已載明的切片,好載你跨過這趟搬遷;有些呼叫的行為不同,而少數幾個不在範圍內。把它當成一座附有公開地圖的橋,而不是每個舊式指令稿都能原封不動執行的保證。

TCPDF-compatibility surface as a migration aid — edition availability
EditionAvailability
Core

相容層與 TCPDF 相容,但並非位元組逐一相同。它涵蓋了 API 中一個已載明的子集,好讓既有的呼叫處在遷移過程中持續產出檔案。它是一座橋,而非可直接替換品:有些行為不同,有些呼叫不受支援,而這些全都列在方法涵蓋與遷移頁面中。目的地是具型別的原生 API,而符合標準等級的輸出就住在那裡。

ProAvailable
EnterpriseAvailable

遷移是一種手段,而非一種美德。如果你的文件很單純、你的函式庫仍有維護、而你永遠不會需要 PDF 2.0、簽署、PDF/A 或無障礙,那麼誠實的答案也許是留在原地——切換成本是真實的,而一趟你不需要的搬遷是一趟你不該動的搬遷。何時不該使用 NextPDF那一頁毫不退縮地畫出那條界線。

本頁說明的是遷移路徑與引擎的目標。確切的 API 涵蓋範圍、行為差異,以及一步步的程序,都住在相容性文件裡,而那份文件才是每個呼叫做什麼的權威。此處沒有任何東西承諾一個任意的舊式指令稿能原封不動執行。

  • PDF 2.0——可攜式文件格式標準的現行版本(ISO 32000-2)。於首次出現時展開;NextPDF 預設寫入的格式。
  • PDF/A——封存一致性系列(ISO 19005 系列),它定義了什麼讓一個 PDF 能安全地長期保存。這是寫入器必須產出的性質,而非呼叫端事後能加上的東西。
  • PAdES——PDF Advanced Electronic Signatures(PDF 進階電子簽章),用於在 PDF 中內嵌符合標準等級簽章的 ETSI 設定檔系列(EN 319 142)。於首次出現時展開;在簽署相關頁面中有深入涵蓋。
  • 相容層——一個形狀仿照來源函式庫(此處為 TCPDF)的 API 層,讓既有的呼叫處在遷移過程中持續運作。與原版相容、但並非位元組逐一相同——是一座橋,而非可直接替換品。
  • 直接替換品——一個能讓既有程式碼原封不動執行的替代物。TCPDF 相容層刻意以這種方式來描述;它是一項帶有已知行為差異、已載明的遷移輔助。