穩定性: 實驗性
PageBackfill:保留頁面緩衝區
可選擇啟用的預覽。 保留頁面緩衝區預設為關閉。在它關閉的情況下,寫入器就是它一直以來的串流序列化器——位元組完全相同。只在你確實需要繪製到一個較早的頁面上時才將它開啟,並請先閱讀下方的 fail-closed 清單。
預設情況下,寫入器會串流頁面並依序將它們沖出;一個頁面一旦沖出,就無法再被繪製。保留頁面緩衝區是用來持有已沖出頁面的選擇性啟用,使一個先前已沖出的頁面能在文件被序列化之前被回填——亦即繪製到一個較早的頁面上。經典的用途是一個總計或一個摘要框,你只能在較後的頁面都配置完成之後才放置它。
composer require nextpdf/core:^3保留頁面緩衝區隨核心套件一起出貨。Config::withRetainedPageBuffer() 與 Document 的回填方法為 @since 6.1.0。預設值維持為串流寫入器。先前曾延後此能力的 ADR-037,現已記錄為已實作。
概念總覽
標題為「概念總覽」的區段Config::withRetainedPageBuffer() 讓一份文件選擇進入保留頁面。一旦開啟,Document::setActiveBackfillPage(int $pageIndex) 會將繪製重新導向到一個較早的、已沖出的頁面;Document::endPageBackfill() 則讓繪製回到正常的附加位置。你在這兩次呼叫之間寫入的內容會落在那個較早的頁面上。緩衝區會持有頁面直到 save(),因此回填會在交叉參照表與尾端被寫出之前套用(ISO 32000-2 §7.5)。
Fail-closed 邊界——被拒絕的組合
標題為「Fail-closed 邊界——被拒絕的組合」的區段回填是一個隨機存取操作,而有數項文件功能假定的是僅附加、串流的位元組。保留頁面緩衝區會拒絕與它們任一者結合,且不受順序影響、在序列化之前進行,因此它絕不會默默破壞一個簽章或一個一致性主張:
- 數位簽章。
- 標記式 PDF(結構樹)。
- PDF/A。
- 線性化。
- 物件串流封裝。
- 加密。
- Safe CSS 算繪模式。
一個逐文件的未壓縮位元組預算會為緩衝區可持有的量設限;一個超出該預算的文件會硬性失敗,而不是消耗無上限的記憶體。在沒有選擇性啟用的情況下,串流預設仍會在呼叫端嘗試一個隨機存取切換的那一刻 fail closed——開啟緩衝區是取得回填的唯一方式,而依其建構方式,它與上述各功能不相容。
API 介面
標題為「API 介面」的區段| 符號 | 位置 | 角色 |
|---|---|---|
Config::withRetainedPageBuffer(bool $enabled = true): self | src/Core/Config.php | 讓一份文件選擇進入保留頁面緩衝區。 |
Document::setActiveBackfillPage(int $pageIndex): static | src/Core/Document.php | 將繪製重新導向到一個較早的、已沖出的頁面。 |
Document::endPageBackfill(): static | src/Core/Document.php | 讓繪製回到正常的附加位置。 |
一個違反某項被拒絕組合的回填嘗試,會在邊界處引發一個具型別的設定例外,而不是一份毀損的文件。
程式碼範例——快速開始
標題為「程式碼範例——快速開始」的區段在第一頁保留一個位置,填完文件的其餘部分,然後以一個在最後才計算出的值回填那個被保留的位置。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Config;use NextPDF\Core\Document;
$config = (new Config())->withRetainedPageBuffer();
$doc = Document::createStandalone($config);$doc->addPage(); // page 0 — leaves room for a grand total$doc->writeHtml('<h1>Invoice</h1>');
$doc->addPage(); // page 1 — line items$doc->writeHtml('<p>Line items…</p>');$total = 1234.56; // computed after laying out the items
$doc->setActiveBackfillPage(0); // draw back onto page 0$doc->writeHtml('<p>Grand total: ' . number_format($total, 2) . '</p>');$doc->endPageBackfill();
$doc->save(__DIR__ . '/invoice.pdf');程式碼範例——正式環境
標題為「程式碼範例——正式環境」的區段對任何已簽章、已標記、PDF/A、已線性化、已加密或物件串流的文件,保持緩衝區關閉——那些正是緩衝區所拒絕的組合。請明確地選擇一條路徑。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Config;use NextPDF\Core\Document;
function renderReport(bool $needsBackfill, bool $mustBeSigned): Document{ if ($needsBackfill && $mustBeSigned) { // The buffer refuses to combine with signing. Resolve the requirement // before building: pre-compute the value, or sign a separate pass. throw new \LogicException('Back-fill and signing are mutually exclusive.'); }
$config = new Config(); if ($needsBackfill) { $config = $config->withRetainedPageBuffer(); }
return Document::createStandalone($config);}邊界情況與陷阱
標題為「邊界情況與陷阱」的區段- 關閉時位元組完全相同。 在緩衝區關閉的情況下,寫入器會如以往一樣串流。
- 與簽章、標記、PDF/A、線性化、物件串流、加密與 Safe CSS 模式互斥。 此拒絕不受順序影響,且在序列化之前觸發。請為文件規劃其中一種模式。
- 位元組預算會硬性失敗。 保留緩衝區是有界的;一個超出未壓縮位元組預算的文件會失敗,而不是無上限地成長。
- 成對呼叫。 每一次
setActiveBackfillPage()都應由一次endPageBackfill()配對,使後續內容正常地附加。 - 串流預設拒絕隨機存取。 在沒有選擇性啟用的情況下,一次隨機存取切換會 fail closed。緩衝區是唯一受支援的路徑。
保留頁面緩衝區以記憶體換取回填能力:它會持有已沖出的頁面直到 save(),並受逐文件的未壓縮位元組預算限制。串流寫入器的平坦記憶體輪廓只在緩衝區關閉時適用。performance_budget(wall_ms: 1500、peak_mb: 128)反映了保留路徑較高的記憶體上限。
安全性注意事項
標題為「安全性注意事項」的區段保留頁面緩衝區不會擴大輸入面;它改變的是位元組何時被序列化,而非何物被擷入。它拒絕與加密及簽章結合,是一項安全屬性:一個回填絕不可能在事後改變已簽章或已加密的位元組,因為這兩者無法一同啟用。位元組預算為記憶體設限,以防範一份惡意文件。
一致性
標題為「一致性」的區段| 陳述 | 規範 | 條款 |
|---|---|---|
| 寫入器在儲存時序列化主體、交叉參照結構與尾端。 | ISO 32000-2 | §7.5 |
這是一項預覽能力。NextPDF 對已簽章、已標記、PDF/A、已線性化、已加密與物件串流的文件拒絕回填緩衝區,因此它透過此路徑不對那些設定檔作任何一致性主張。未重現任何規範文字。
Compat(TCPDF)轉接器
標題為「Compat(TCPDF)轉接器」的區段TCPDF 相容性轉接器將此能力以一個建構函式擴充的形式公開。以 retainedPageBuffer: true 建構轉接器,那麼一次以較早頁面為目標的 setPage() 或 lastPage() 呼叫,就會委派給核心回填,而不是引發串流的 UnsupportedFeatureException。這個建構函式引數是一個 NextPDF 擴充,而非舊版 TCPDF 對等功能——舊版 TCPDF 並沒有這樣的旗標。同樣的 fail-closed 拒絕適用。轉接器端的細節請參閱 compat 轉接器的 retained-page-buffer 頁面。