契約 / 文件
快速一覽
標題為「快速一覽」的區段document 領域涵蓋你建構 PDF 時需遵循的契約:PdfDocumentInterface 負責內容,DocumentFactoryInterface 負責適用於 worker 的安全建立流程,另有 font 與 image 註冊表契約,以及用於輸出與版面的三個列舉。全部自 1.0.0 或 1.7.0 起即為 stable(穩定)。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段PdfDocumentInterface 是主要 API 介面。它定義頁面管理、字型選擇、cell 與 multi-cell 文字版面、HTML 繪製、影像嵌入,以及最終輸出。每個方法都回傳 static,因此呼叫可以串接。Document::createStandalone() 會回傳滿足該介面的具體實例。在你自己的服務中,型別提示請指向這個介面;如此一來,引擎內部仍可隨時抽換實作。
建立文件有兩條路徑。在傳統的 PHP-FPM 請求中,createStandalone() 會建立一份自我包含、帶有私有註冊表的文件。長時間執行的 worker 則走另一條路徑,這類情境包含 RoadRunner、Swoole 與 Laravel Octane。此時,DocumentFactoryInterface::create() 會回傳一份全新、用過即丟的 Document。這份文件會從行程生命週期的註冊表讀取資料,但絕不會改動它們。工廠持有 FontRegistryInterface 與 ImageRegistryInterface 這兩個單例(singleton)。每份文件都會取得各自的繪製 context 與 writer。這就是故障隔離:一份文件無法破壞另一份文件所依賴的共享狀態。
註冊表契約正是 worker 能維持高速的原因。FontRegistryInterface 只會剖析字型檔一次,並在整個行程生命週期內快取已剖析的中繼資料。暖機後即可將它鎖定,避免生產環境流量再變更它。ImageRegistryInterface 會在有界的最近最少使用(LRU)策略下,快取已解碼的影像二進位資料。即使二進位資料已被逐出,影像中繼資料仍常駐於記憶體。兩者都提供 memoryUsage() 供容量規劃使用。ImageRegistryInterface 繼承 ResettableService。該契約會逐出快取資料,但不會摧毀結構性中繼資料。worker 可以在記憶體吃緊時丟棄影像快取,並持續提供服務。
另有三個列舉補齊這個領域。OutputDestination 用來選擇行內顯示、強制下載、寫入檔案系統,或回傳原始字串。Orientation 用來選擇直向或橫向。Alignment 用來選擇靠左、置中、靠右或左右對齊的文字。每個列舉都以舊版 TCPDF 代碼作為列舉值,支撐各個 case,因此 compat-tcpdf 橋接層能乾淨地對映。這些列舉的向後相容承諾屬於累加性質,不會移除任何 case。新的 case 可能會在次要版本中加入。
API 介面
標題為「API 介面」的區段| 型別 | 種類 | 主要成員 | 穩定性 | 自版本 |
|---|---|---|---|---|
PdfDocumentInterface | 介面 | addPage(), setMargins(), setFont(), cell(), multiCell(), writeHtml(), image(), output(), save() | 穩定 | 1.0.0 |
DocumentFactoryInterface | 介面 | create(?Config): Document | 穩定 | 1.7.0 |
ResettableService | 介面 | reset(): void | 穩定 | 1.7.0 |
FontRegistryInterface | 介面 | register(), get(), warmup(), lock(), isLocked(), registerFromBinary(), memoryUsage() | 穩定 | 1.7.0 |
ImageRegistryInterface | 介面 | load(), loadFromString(), getMetadata(), memoryUsage()(繼承 ResettableService) | 穩定 | 2.0.0 |
OutputDestination | 列舉(字串) | Inline, Download, File, String | 穩定 | 1.0.0 |
Orientation | 列舉(字串) | Portrait, Landscape | 穩定 | 1.0.0 |
Alignment | 列舉(字串) | Left, Center, Right, Justify | 穩定 | 1.0.0 |
FontRegistryInterface 與 ImageRegistryInterface 在 typography 頁有完整說明;本 document 頁則聚焦於它們在建立生命週期中的角色。
程式碼範例 — 快速開始
標題為「程式碼範例 — 快速開始」的區段<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Hello World');$doc->addPage();$doc->setFont('helvetica', '', 24);$doc->cell(0, 15, 'Hello, NextPDF!', newLine: true);$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'This is a minimal PDF generated with NextPDF.', newLine: true);$doc->save(__DIR__ . '/output/01-hello-world.pdf');程式碼範例 — 正式環境
標題為「程式碼範例 — 正式環境」的區段<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\PdfFactory;use NextPDF\ValueObjects\{Margin, PageSize};
$factory = PdfFactory::new() ->withPageSize(PageSize::A4()) ->withMargins(new Margin(15.0, 15.0, 15.0, 15.0)) ->withCompress(true) ->withLang('en');
// The same configured factory creates independent documents.$doc = $factory->create();$doc->setTitle('PdfFactory Example');$doc->setAuthor('NextPDF');$doc->addPage();$doc->setFont('helvetica', '', 16);$doc->cell(0, 12, 'Created via PdfFactory', newLine: true);
$doc2 = $factory->create();$doc2->addPage();$doc2->setFont('helvetica', '', 12);$doc2->cell(0, 10, 'Second document from the same factory.');
$doc->save(__DIR__ . '/output/02-pdf-factory.pdf');PdfFactory 是不可變的建構器;每次 with*() 都會回傳一個新實例。它在底層組合出 DocumentFactoryInterface,因此總覽中提到的 worker 註冊表模型不需要額外串接即可套用。
邊界情況與陷阱
標題為「邊界情況與陷阱」的區段createStandalone()會建立私有註冊表。在 worker 迴圈中,這會導致每次請求都重新剖析每個字型。請改用搭配共享註冊表的DocumentFactoryInterface。- 一份
Document在設計上就是用過即丟。跨多份邏輯文件重複使用同一個實例會洩漏狀態。請為每份文件呼叫一次create(),並讓垃圾回收機制回收它。 FontRegistryInterface::lock()會使register()、addFontDirectory()與warmup()拋出LogicException。請在暖機後鎖定,絕不要在處理請求時鎖定。OutputDestination::File會寫入伺服器檔案系統,並回傳原始位元組。save()則明確指定檔案路徑。同一份文件請勿混用這兩者。cell()為了相容 TCPDF,邊框引數接受bool|string;空字串並不等同於false。請傳入你真正要表達的型別值。
font 與 image 註冊表讓 document 領域成為一個記憶體有界的系統,而不是逐請求重建的系統。首次請求的字型剖析占去大部分時間。在 worker 範例中,跨三份文件的 performance_budget 為 1500 ms 牆鐘時間與 64 MB 尖峰記憶體。這份預算幾乎全部都花在首次字型剖析上。暖機後,每份文件可歸因於契約的工作量為 O(1) — 一次註冊表查詢加上一次 context 配置。對任一註冊表呼叫 memoryUsage(),都會回傳一份可供即時容量規劃使用的 MemoryReport。ResettableService::reset() 會在持續負載下將尖峰記憶體控制在界限之內。
安全性注意事項
標題為「安全性注意事項」的區段document 契約本身不帶任何密碼學介面,但仍有兩項維運風險需要留意。第一,image() 接受路徑或 URL。在不可信輸入的情境下,請透過 ExternalResourcePolicyInterface(見 security-policy 頁)約束遠端抓取,而不是直接傳入使用者可控的 URL。第二,writeHtml() 是 HTML 管線的進入點。不可信的 markup(標記內容)在繪製前必須先通過 HtmlSecurityPolicyInterface。document 層本身並不會進行淨化。這是 security-policy 領域的職責;而且它是一個契約,因此你可以提供更嚴格的政策,無需分叉。
符合性
標題為「符合性」的區段document 契約實作 ISO 32000-2 定義的 PDF 2.0 文件結構。輸出、頁面與字型處理會依 ISO 32000-2 §7 產生間接物件與一個交叉參照串流(cross-reference stream)。內容依引擎層契約(ADR-010)透過 writer 層發出。本頁除了結構符合性之外,不主張任何條款層級的宣稱。PDF/A 與 PDF/UA 符合性記載於 extraction 與 accessibility 兩頁,規範性表格也在那裡。
另請參閱
標題為「另請參閱」的區段- 契約:41 個公開介面(SPI) — SPI 總覽與穩定性層級。
- 契約 / Typography —
FontRegistryInterface的完整說明。 - 契約 / Security Policy — 用來把關
writeHtml()與image()的各項政策。 - Core —
Document與PdfFactory具體類別。 - Document — 文件建構模組。
- Writer — 為這些契約發出 PDF 物件的層。