Core / Document 門面
Document 是 final 門面,所有 NextPDF 撰寫呼叫都會經過它。它組合了 21 個關注點 trait,並透過單一串流流程產生一份 PDF 2.0 檔案。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段NextPDF\Core\Document 宣告為 final class Document implements PdfDocumentInterface。類別本體刻意保持精簡:只持有 RenderingContext、DocumentData 模型、PdfWriter、那些急切建立的子引擎,以及少數私有輔助方法。所有公開撰寫方法都來自它透過 use Concerns\Has* 組合進來的 21 個 trait:
HasMetadata、HasPages、HasTypography、HasColors、HasTextOutput、HasDrawing、HasTransforms、HasLayout、HasNavigation、HasImages、HasBarcodes、HasFormFields、HasLayers、HasTemplates、HasTransactions、HasFileAttachments、HasJavaScript、HasViewerPreferences、HasSecurity、HasOutput、HasWarnings。
這樣的組合是刻意設計的。變更文字行為只會動到 HasTextOutput。變更頁面處理只會動到 HasPages。Document.php 本身維持小巧,因此單次變更的影響範圍會被限縮在單一 trait 之內。.ai/manifest.json 的 Core 條目也因此將 Document.php 標記為危險區:它是觸及範圍極廣的門面,所以任何結構性編輯都必須走 plan mode(計畫模式)。
它的生命週期是一次性的。類別 docblock 已明確寫出:每一個 Document 都只能使用一次;不要重用或重設。它沒有 reset() 方法,而承載游標、作用中頁面、圖形狀態與字型選擇的 RenderingContext 也沒有。對於長時間執行的 worker,文件記載的模式是將 FontRegistry 與 ImageRegistry 保留在 process 層級存續,並為每個請求透過 DocumentFactory 產生全新的 Document。文件只會讀取這些 registry,從不變更它們,因此單一請求的故障無法外洩到下一個請求。
建構子接受 FontRegistryInterface $fontRegistry、ImageRegistryInterface $imageRegistry,以及選用的 Config。省略 $config 時,會改用預設的 Config。建構子會建立那些急切建立的引擎 —— FontMetrics、DrawingEngine、TransformEngine、TextRenderer、HeaderFooter、ColumnLayout、BookmarkManager、LinkManager、TocBuilder、AnnotationManager、PageManager —— 因為幾乎每份文件都會用到它們。選用引擎會保持 null,直到第一次被存取為止。
頁面內容會以內容串流形式輸出。每一頁都由一個或多個內容串流表示(ISO 32000-2:2020 §7.8.2)。頁面樹透過由間接參考組成的 Kids 陣列串接各頁(§7.7.3)。
API 介面
標題為「API 介面」的區段| 符號 | 種類 | 穩定度 | 自版本 |
|---|---|---|---|
final class Document implements PdfDocumentInterface | 類別 | 穩定 | 1.0.0 |
Document::createStandalone(?Config $config = null): self | 靜態工廠 | 穩定 | 1.0.0 |
Document::__construct(FontRegistryInterface, ImageRegistryInterface, ?Config) | 建構子 | 穩定 | 1.0.0 |
Document::addTextPreprocessor(TextPreprocessorInterface): static | 方法 | 穩定 | 1.9.0 |
Document::fontMetrics(): FontMetrics | 方法 | 穩定 | 1.0.0 |
Document::setDPartRoot(DPartRoot): static | 方法 | 穩定 | 1.12.0 |
Document::strictPdf20FontEmbedding(): static | 方法 | 穩定 | 2.5.0 |
Document::allowNonEmbeddedBase14(): static | 方法 | 穩定 | 2.5.0 |
程式碼範例 —— 快速上手
標題為「程式碼範例 —— 快速上手」的區段<?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');
echo "Created: output/01-hello-world.pdf\n";來源:examples/01-hello-world.php。
程式碼範例 —— 正式環境
標題為「程式碼範例 —— 正式環境」的區段PdfFactory 是不可變的建構器。每次呼叫 with*() 都會回傳一個新的工廠,因此已設定好的建構器可以產生許多彼此獨立的文件。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\PdfFactory;use NextPDF\ValueObjects\{Margin, PageSize};
// Build a configured factory — each with*() returns a new instance$factory = PdfFactory::new() ->withPageSize(PageSize::A4()) ->withMargins(new Margin(15.0, 15.0, 15.0, 15.0)) ->withCompress(true) ->withLang('en');
// Create a document from the factory$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);
// The same factory can create multiple independent documents$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');來源:examples/02-pdf-factory.php。
邊界情況與陷阱
標題為「邊界情況與陷阱」的區段Document是final。請透過組合一個新的關注點 trait 或包裝門面來擴充行為,而不是用子類別繼承。- 門面實作了
PdfDocumentInterface。請在應用程式程式碼中相依於這個介面,這樣 worker 的接線才能保持可替換。 createStandalone()會建立停用快取的短暫 registry;它只適用於 CLI 與 PHP-FPM。在 RoadRunner、Swoole 或 Octane 中請改用DocumentFactory。setChromeRendererConfig()與writeHtmlChrome()需要nextpdf/artisan擴充套件;若未安裝該擴充套件,這個呼叫會丟出InvalidConfigException。addTextPreprocessor()會註冊前置處理器,在字形排版、字型子集化與結構樹流程之前攔截文字;執行順序就是註冊順序。
建構複雜度為 O(急切建立的引擎數),而且在呼叫 addPage() 之前不會配置任何頁面記憶體。除了進行一次 trait 方法分派之外,門面不會增加任何每次呼叫的額外負擔。由於不保留任何文件樹,尖峰記憶體會隨內容量增減(ADR-001)。預算:標準快速上手範例為 1500 ms / 64 MB。
安全性備註
標題為「安全性備註」的區段這兩個 registry 由外部注入,文件會把它們視為唯讀,藉此將故障限縮在單一請求之內。字型嵌入政策是顯式的。strictPdf20FontEmbedding() 會啟用 ISO 32000-2:2020 §9.6.2 的強制檢查(未嵌入的 Base 14 參考會在寫入時丟出例外)。allowNonEmbeddedBase14() 會固定採用舊版的勸告式警告路徑。透過 HasOutput::save() 進行的輸出是原子操作。加密與簽章位於 安全性 trait 中。
符合性
標題為「符合性」的區段| 宣稱 | 來源 | 條款 | 參考 ID |
|---|---|---|---|
| 每一頁都由一個或多個內容串流來表示 | ISO 32000-2:2020 | §7.8.2 | |
頁面樹透過由間接參考組成的 Kids 陣列來串接子項 | ISO 32000-2:2020 | §7.7.3 |
各條款均為改寫摘要;未重製任何規範性文字。
延伸閱讀
標題為「延伸閱讀」的區段/modules/core/core/Core 模組 — Core 模組概觀與關注點 trait 的組合模型/modules/core/core/has-pages/頁面 trait —HasPages:由門面驅動的頁面生命週期/modules/core/core/has-text-output/文字輸出 trait —HasTextOutput:門面上最龐大的撰寫關注點/modules/core/core/has-security/安全性 trait —HasSecurity:加密與簽章的交接點/modules/core/core/has-output/輸出 trait —HasOutput:最終序列化步驟/modules/core/contracts/介面契約 —PdfDocumentInterface門面所實作的介面/modules/core/valueobjects/值物件 — 正式環境範例所使用的PageSize與Margin<!— evidence: src/Core/Document.php (final class Document implements PdfDocumentInterface; 21use Concerns\Has*enumerated; class docblock “Disposable: each Document is use-once. Never reuse or reset.”; __construct(FontRegistryInterface, ImageRegistryInterface, ? Config), eager engines list; createStandalone; addTextPreprocessor @since 1.9.0; fontMetrics(); setDPartRoot @since 1.12.0; strictPdf20FontEmbedding/allowNonEmbeddedBase14 @since 2.5.0 ISO §9.6.2; setChromeRendererConfig throws InvalidConfigException w/o nextpdf/artisan); src/Core/PdfFactory.php (immutable builder, new/with*/create, @since 3.1.0); .ai/manifest.json Core (Document.php danger_zone “Facade with 21 concern traits”); ADR-001; examples/01-hello-world.php, examples/02-pdf-factory.php; glossary Document/Concern trait. RAG iso32000_2_sec7 §7.8.2 , §7.7.3 >