跳到內容
getnextpdf.com

穩定性: 實驗性

保留頁面緩衝區擴充功能

可選擇啟用的擴充,而非舊版對等功能。 這個建構函式引數在舊版 TCPDF 6.x 中並不存在。它是一個 NextPDF 擴充。它預設關閉;在它關閉的情況下,轉接器的行為與以往完全相同,而對一個較早頁面的 setPage() 會一如以往地引發 UnsupportedFeatureException

舊版 TCPDF 讓你能呼叫 setPage() 回到一個較早的頁面並繼續繪製。串流轉接器預設無法那樣做——一個頁面一旦沖出就消失了——因此對一個較早頁面的 setPage()lastPage() 會引發 UnsupportedFeatureException。保留頁面緩衝區是用來在 NextPDF 核心保留頁面緩衝區之上恢復這項回填行為的選擇性啟用。

retainedPageBuffer: true 傳給轉接器建構函式。在緩衝區開啟的情況下,一次以較早頁面為目標的 setPage()lastPage() 呼叫會委派給核心回填,而不是引發例外:

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\TCPDF;
$pdf = new TCPDF(retainedPageBuffer: true);
$pdf->AddPage(); // page 1 — reserve room for a running total
$pdf->Cell(0, 10, 'Invoice', ln: 1);
$pdf->AddPage(); // page 2 — line items
$pdf->Cell(0, 10, 'Line items…', ln: 1);
$total = 1234.56; // known only after the items are laid out
$pdf->setPage(1); // delegates to the core back-fill
$pdf->Cell(0, 10, 'Grand total: ' . number_format($total, 2), ln: 1);
$pdf->lastPage(); // return to the final page
$pdf->Output(__DIR__ . '/invoice.pdf', 'F');

正式環境範例:回填一個被保留的封面頁

標題為「正式環境範例:回填一個被保留的封面頁」的區段

一個常見會用到緩衝區的理由是一個封面或摘要頁,其數字只有在主體配置完成後才會知道——一個總頁數、一個總計、一個記錄數。先在前面保留第 1 頁,算繪主體,然後以 setPage(1) 回填封面,並在最後以 lastPage() 恢復。這個範例也展示了你必須處理的兩個 fail-closed 邊界:轉接器針對超出範圍頁碼的 UnsupportedFeatureException,以及在文件同時啟用一個不相容功能時的核心 RetainedPageBufferIncompatibleException

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Compat\Tcpdf\Exception\UnsupportedFeatureException;
use NextPDF\Compat\Tcpdf\TCPDF;
use NextPDF\Exception\Strict\RetainedPageBufferIncompatibleException;
/**
* Render a multi-page report whose cover page summarises figures that are
* only known once every body page has been laid out.
*
* @param list<array{label: string, amount: float}> $lineItems
*/
function renderReport(array $lineItems, string $destination): void
{
// Opt in to the back-fill buffer. Default-off; this is a NextPDF
// extension, not legacy TCPDF parity. (Underlying core feature: 6.1.0.)
$pdf = new TCPDF(retainedPageBuffer: true);
// Page 1 — the cover. Reserve it now; the summary is filled in last.
$pdf->AddPage();
$pdf->Cell(0, 10, 'Quarterly report', ln: 1);
// Body pages — lay out the line items, accumulating the running total.
$pdf->AddPage();
$total = 0.0;
foreach ($lineItems as $item) {
$total += $item['amount'];
$pdf->Cell(0, 8, $item['label'] . ': ' . number_format($item['amount'], 2), ln: 1);
}
// Back-fill the cover with figures known only now. setPage() delegates to
// the core back-fill in retained mode; an out-of-range page number still
// fails closed with UnsupportedFeatureException in BOTH modes.
try {
$pdf->setPage(1);
} catch (UnsupportedFeatureException $e) {
throw new RuntimeException('Cover page was not reserved: ' . $e->getMessage(), previous: $e);
}
$pdf->Cell(0, 10, 'Total: ' . number_format($total, 2), ln: 1);
$pdf->Cell(0, 10, 'Line items: ' . count($lineItems), ln: 1);
// Resume appending at the final page before output.
$pdf->lastPage();
// Output() drives the core build. If the document had also enabled a
// back-fill-incompatible feature (signing, tagging, PDF/A, linearization,
// object streams, encryption, Safe CSS mode), the core refuses here,
// order-independently, with RetainedPageBufferIncompatibleException — the
// back-fill can never silently corrupt such a document.
try {
$pdf->Output($destination, 'F');
} catch (RetainedPageBufferIncompatibleException $e) {
// $e->feature names the incompatible feature, e.g. 'signature'.
throw new RuntimeException(
'Back-fill is incompatible with ' . $e->feature . '; render pages in order instead.',
previous: $e,
);
}
}

請刻意區分這兩個失敗面:

  • UnsupportedFeatureException(轉接器)——一個超出範圍setPage() / lastPage() 目標,或在緩衝區關閉任何往較早頁面的切換。
  • RetainedPageBufferIncompatibleException(核心,NextPDF\Exception\Strict)——緩衝區開啟但與一個其逐頁中繼資料在回填之後無法重新推導的功能結合。沒有 RetainedPageBufferInconsistency 型別;這是唯一的不相容例外,而一次預算超限則以 \OverflowException 浮現。

轉接器委派給核心保留頁面緩衝區,因此同樣的拒絕適用。當文件同時使用以下任一者時,回填會被拒絕——不受順序影響、且在序列化之前進行:

  • 數位簽章。
  • 標記式 PDF(結構樹)。
  • PDF/A。
  • 線性化。
  • 物件串流封裝。
  • 加密。
  • Safe CSS 算繪模式。

每一次拒絕都是一個具型別、fail-closed 的例外——絕不是一次默默的丟棄:

  • 將緩衝區與上述任一功能結合,會引發核心的 RetainedPageBufferIncompatibleException(命名空間 NextPDF\Exception\Strict@since core 6.1.0)。這項檢查不受順序影響:無論不相容功能是在緩衝區被選擇啟用之前或之後設定,它都會觸發。
  • 一個逐文件的 16 MiB 未壓縮位元組預算為緩衝區設限;超出它會在建置時引發 \OverflowException,而不是丟棄一個已回填的頁面。

此拒絕的重點在於,一個回填絕不可能默默地改變一份已簽章或已加密的文件——這兩者無法一同啟用。

  • 預設關閉。 不帶該旗標建構,轉接器即維持不變;對一個較早頁面的 setPage() 仍會引發 UnsupportedFeatureException。這為每一個既有的呼叫端保留了串流契約。
  • 非舊版對等功能。 舊版 TCPDF 沒有 retainedPageBuffer 建構函式旗標。在你遷移時請將此記載為一個 NextPDF 擴充,使未來的讀者不會將它誤認為一項 TCPDF 功能。
  • lastPage() 回到結尾。 在一次回填之後,呼叫 lastPage() 以恢復在最後一頁附加。
  • 規劃一種模式。 如果文件必須被簽章、標記、PDF/A、線性化、加密或物件串流,請勿啟用緩衝區;改為預先計算你原本會回填的那個值。