Przejdź do głównej zawartości
getnextpdf.com

stabilność: Eksperymentalna

Zachowany bufor stron (rozszerzenie)

Opcjonalne rozszerzenie, a nie odpowiednik starego. Ten argument konstruktora nie istnieje w starym TCPDF 6.x. To rozszerzenie NextPDF. Jest domyślnie wyłączone; przy nim wyłączonym adapter zachowuje się dokładnie jak wcześniej, a setPage() do wcześniejszej strony zgłasza UnsupportedFeatureException jak zawsze.

Stary TCPDF pozwala wywołać setPage(), aby cofnąć się do wcześniejszej strony i dalej rysować. Adapter strumieniowy nie potrafi tego domyślnie — po wypchnięciu strony znika ona — więc setPage() lub lastPage() do wcześniejszej strony zgłasza UnsupportedFeatureException. Zachowany bufor stron to opcja, która przywraca to zachowanie uzupełniania na bazie zachowanego bufora stron rdzenia NextPDF.

Przekaż retainedPageBuffer: true do konstruktora adaptera. Przy włączonym buforze wywołanie setPage() lub lastPage() celujące we wcześniejszą stronę deleguje do uzupełniania w rdzeniu, zamiast zgłaszać:

<?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');

Przykład produkcyjny: uzupełnienie zarezerwowanej strony tytułowej

Dział zatytułowany „Przykład produkcyjny: uzupełnienie zarezerwowanej strony tytułowej”

Częstym powodem sięgnięcia po bufor jest strona tytułowa lub podsumowująca, której liczby są znane dopiero po rozłożeniu treści — łączna liczba stron, suma ogólna, liczba rekordów. Zarezerwuj stronę 1 z góry, wyrenderuj treść, a następnie uzupełnij stronę tytułową przez setPage(1) i wznów na końcu przez lastPage(). Ten przykład pokazuje też dwie granice fail-closed, które musisz obsłużyć: adapterowy UnsupportedFeatureException dla numeru strony spoza zakresu oraz rdzeniowy RetainedPageBufferIncompatibleException, jeśli dokument włącza też niezgodną funkcję.

<?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,
);
}
}

Rozróżniaj te dwie powierzchnie awarii celowo:

  • UnsupportedFeatureException (adapter) — cel setPage() / lastPage() spoza zakresu lub jakiekolwiek przełączenie na wcześniejszą stronę, gdy bufor jest wyłączony.
  • RetainedPageBufferIncompatibleException (rdzeń, NextPDF\Exception\Strict) — bufor jest włączony, ale połączony z funkcją, której metadanych na poziomie strony nie da się ponownie wyprowadzić po uzupełnieniu. Nie ma typu RetainedPageBufferInconsistency; to jedyny wyjątek niezgodności, a przekroczenie budżetu wypływa jako \OverflowException.

Adapter deleguje do zachowanego bufora stron rdzenia, więc obowiązują te same odmowy. Uzupełnianie jest odmawiane — niezależnie od kolejności i przed serializacją — gdy dokument używa też którejkolwiek z:

  • Podpisu cyfrowego.
  • Tagowanego PDF (drzewa struktury).
  • PDF/A.
  • Linearyzacji.
  • Pakowania w strumienie obiektów.
  • Szyfrowania.
  • Trybu renderowania Safe CSS.

Każda odmowa to typowany wyjątek fail-closed — nigdy cichy zrzut:

  • Połączenie bufora z którąkolwiek z powyższych funkcji zgłasza rdzeniowy RetainedPageBufferIncompatibleException (przestrzeń nazw NextPDF\Exception\Strict, @since rdzeń 6.1.0). Sprawdzenie jest niezależne od kolejności: zadziała niezależnie od tego, czy niezgodna funkcja została skonfigurowana przed buforem czy po włączeniu opcji bufora.
  • Budżet 16 MiB nieskompresowanych bajtów na dokument ogranicza bufor; przekroczenie go zgłasza \OverflowException w czasie budowania, zamiast porzucać uzupełnioną stronę.

Sensem odmowy jest to, że uzupełnienie nigdy nie może po cichu zmienić podpisanego lub zaszyfrowanego dokumentu — tych dwóch nie da się włączyć razem.

  • Domyślnie wyłączone. Skonstruuj bez flagi, a adapter pozostaje bez zmian; setPage() do wcześniejszej strony nadal zgłasza UnsupportedFeatureException. To zachowuje kontrakt strumieniowy dla każdego istniejącego wywołującego.
  • Nie odpowiednik starego. Stary TCPDF nie ma flagi konstruktora retainedPageBuffer. Udokumentuj to jako rozszerzenie NextPDF, gdy migrujesz, aby przyszły czytelnik nie pomylił tego z funkcją TCPDF.
  • lastPage() wraca na koniec. Po uzupełnieniu wywołaj lastPage(), aby wznowić dołączanie na ostatniej stronie.
  • Zaplanuj jeden tryb. Jeśli dokument ma być podpisany, tagowany, PDF/A, zlinearyzowany, zaszyfrowany lub ze strumieniami obiektów, nie włączaj bufora; zamiast tego oblicz z góry wartość, którą byś uzupełnił.