стабильность: Экспериментальная
Буфер сохранённых страниц (расширение)
Подключаемое расширение, а не устаревший паритет. Этого аргумента конструктора нет в устаревшем 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(). Этот пример также показывает две границы с отказом
закрытием, которые вы должны обработать: 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.
Каждый отказ — это типизированное, отказывающее закрытием исключение, а не молчаливый сброс:
- Сочетание буфера с любой функцией выше вызывает у ядра
RetainedPageBufferIncompatibleException(пространство имёнNextPDF\Exception\Strict,@sinceядра 6.1.0). Проверка независима от порядка: она срабатывает независимо от того, была ли несовместимая функция настроена до или после подключения буфера. - Подокументный бюджет несжатых байтов в 16 MiB ограничивает буфер;
превышение его вызывает
\OverflowExceptionво время сборки, а не сбрасывает дозаполненную страницу.
Смысл отказа в том, что дозаполнение никогда не может молча изменить подписанный или зашифрованный документ — эти два режима нельзя включить вместе.
Заметки о поведении
Заголовок раздела «Заметки о поведении»- По умолчанию выключено. Сконструируйте без флага, и адаптер не изменён;
setPage()на более раннюю страницу по-прежнему вызываетUnsupportedFeatureException. Это сохраняет потоковый контракт для каждого существующего вызывающего. - Не устаревший паритет. В устаревшем TCPDF нет флага конструктора
retainedPageBuffer. Документируйте это как расширение NextPDF при миграции, чтобы будущий читатель не принял его за функцию TCPDF. lastPage()возвращает к концу. После дозаполнения вызовитеlastPage(), чтобы возобновить добавление на последней странице.- Планируйте один режим. Если документ должен быть подписан, тегирован, PDF/A, линеаризован, зашифрован или с потоками объектов, не включайте буфер; вместо этого предвычислите значение, которое вы бы дозаполнили.