コンテンツにスキップ
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() で再開します。この例は、扱わなければならない 2 つのフェイルクローズ境界も示します。範囲外のページ番号に対するアダプターの 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,
);
}
}

2 つの失敗サーフェスを意図的に区別してください。

  • UnsupportedFeatureException(アダプター) — 範囲外setPage() / lastPage() の対象、またはバッファがオフのときのあらゆる先行ページへの切り替え。
  • RetainedPageBufferIncompatibleException(コア、NextPDF\Exception\Strict) — バッファはオンだが、 バックフィル後にページレベルのメタデータを再導出できない機能と組み合わされている。RetainedPageBufferInconsistency 型は存在しません。これが唯一の非互換例外であり、予算超過は \OverflowException として表面化します。

アダプターはコアの保持ページバッファへ委譲するため、同じ拒否が適用されます。ドキュメントが次のいずれかも使用しているとき、バックフィルは — 順序に依存せず、シリアライズの前に — 拒否されます。

  • デジタル署名。
  • タグ付き PDF(構造ツリー)。
  • PDF/A。
  • 線形化。
  • オブジェクトストリームのパッキング。
  • 暗号化。
  • Safe CSS レンダリングモード。

各拒否は、暗黙のドロップではなく、型付きでフェイルクローズの例外です。

  • バッファを上記のいずれかの機能と組み合わせると、コアの RetainedPageBufferIncompatibleException (名前空間 NextPDF\Exception\Strict、コア 6.1.0 の @since)を送出します。チェックは順序に依存しません。 非互換機能がバッファのオプトインの前に設定されたか後に設定されたかにかかわらず発火します。
  • ドキュメントごとの 16 MiB 非圧縮バイト予算がバッファを有界化します。それを超えると、バックフィルされたページをドロップするのではなく、ビルド時に \OverflowException を送出します。

拒否の要点は、バックフィルが署名済みまたは暗号化済みのドキュメントを暗黙のうちに書き換えることは決してないという点です — 両者を同時に有効化できないからです。

  • デフォルトオフ。 フラグなしで構築すると、アダプターは変更されません。先行ページへの setPage() は依然として UnsupportedFeatureException を送出します。これは既存のすべての呼び出し側に対してストリーミング契約を保ちます。
  • レガシーパリティではありません。 レガシー TCPDF に retainedPageBuffer コンストラクターフラグはありません。移行時には、将来の読者が TCPDF の機能と取り違えないよう、これを NextPDF の拡張として文書化してください。
  • lastPage() は末尾に戻ります。 バックフィルの後、最終ページで追記を再開するには lastPage() を呼んでください。
  • 1 つのモードで計画してください。 ドキュメントが署名、タグ付け、PDF/A、線形化、暗号化、またはオブジェクトストリームを必要とする場合、バッファを有効にせず、代わりにバックフィルしたはずの値を事前計算してください。