跳到內容

Core / Document 門面

Documentfinal 門面,所有 NextPDF 撰寫呼叫都會經過它。它組合了 21 個關注點 trait,並透過單一串流流程產生一份 PDF 2.0 檔案。

Terminal window
composer require nextpdf/core:^3

NextPDF\Core\Document 宣告為 final class Document implements PdfDocumentInterface。類別本體刻意保持精簡:只持有 RenderingContextDocumentData 模型、PdfWriter、那些急切建立的子引擎,以及少數私有輔助方法。所有公開撰寫方法都來自它透過 use Concerns\Has* 組合進來的 21 個 trait:

HasMetadataHasPagesHasTypographyHasColorsHasTextOutputHasDrawingHasTransformsHasLayoutHasNavigationHasImagesHasBarcodesHasFormFieldsHasLayersHasTemplatesHasTransactionsHasFileAttachmentsHasJavaScriptHasViewerPreferencesHasSecurityHasOutputHasWarnings

這樣的組合是刻意設計的。變更文字行為只會動到 HasTextOutput。變更頁面處理只會動到 HasPagesDocument.php 本身維持小巧,因此單次變更的影響範圍會被限縮在單一 trait 之內。.ai/manifest.json 的 Core 條目也因此將 Document.php 標記為危險區:它是觸及範圍極廣的門面,所以任何結構性編輯都必須走 plan mode(計畫模式)。

它的生命週期是一次性的。類別 docblock 已明確寫出:每一個 Document 都只能使用一次;不要重用或重設。它沒有 reset() 方法,而承載游標、作用中頁面、圖形狀態與字型選擇的 RenderingContext 也沒有。對於長時間執行的 worker,文件記載的模式是將 FontRegistryImageRegistry 保留在 process 層級存續,並為每個請求透過 DocumentFactory 產生全新的 Document。文件只會讀取這些 registry,從不變更它們,因此單一請求的故障無法外洩到下一個請求。

建構子接受 FontRegistryInterface $fontRegistryImageRegistryInterface $imageRegistry,以及選用的 Config。省略 $config 時,會改用預設的 Config。建構子會建立那些急切建立的引擎 —— FontMetricsDrawingEngineTransformEngineTextRendererHeaderFooterColumnLayoutBookmarkManagerLinkManagerTocBuilderAnnotationManagerPageManager —— 因為幾乎每份文件都會用到它們。選用引擎會保持 null,直到第一次被存取為止。

頁面內容會以內容串流形式輸出。每一頁都由一個或多個內容串流表示(ISO 32000-2:2020 §7.8.2)。頁面樹透過由間接參考組成的 Kids 陣列串接各頁(§7.7.3)。

符號種類穩定度自版本
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

  • Documentfinal。請透過組合一個新的關注點 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/ 頁面 traitHasPages:由門面驅動的頁面生命週期
  • /modules/core/core/has-text-output/ 文字輸出 traitHasTextOutput:門面上最龐大的撰寫關注點
  • /modules/core/core/has-security/ 安全性 traitHasSecurity:加密與簽章的交接點
  • /modules/core/core/has-output/ 輸出 traitHasOutput:最終序列化步驟
  • /modules/core/contracts/ 介面契約PdfDocumentInterface 門面所實作的介面
  • /modules/core/valueobjects/ 值物件 — 正式環境範例所使用的 PageSizeMargin <!— evidence: src/Core/Document.php (final class Document implements PdfDocumentInterface; 21 use 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 >