Pular para o conteúdo
getnextpdf.com

estabilidade: Experimental

Extensão de buffer de página retida

Extensão opcional, não paridade legada. Este argumento de construtor não existe no TCPDF 6.x legado. É uma extensão NextPDF. É desativada por padrão; com ela desativada, o adaptador se comporta exatamente como antes, e setPage() para uma página anterior levanta UnsupportedFeatureException como sempre.

O TCPDF legado permite que você chame setPage() para voltar a uma página anterior e continuar desenhando. O adaptador de streaming não consegue fazer isso por padrão — uma vez que uma página é descarregada, ela se foi — então setPage() ou lastPage() para uma página anterior levanta UnsupportedFeatureException. O buffer de página retida é o recurso opcional que restaura esse comportamento de preenchimento posterior sobre o buffer de página retida do core do NextPDF.

Passe retainedPageBuffer: true para o construtor do adaptador. Com o buffer ativado, uma chamada setPage() ou lastPage() que aponte para uma página anterior delega ao preenchimento posterior do core em vez de levantar a exceção:

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

Exemplo de produção: preencher posteriormente uma página de capa reservada

Seção intitulada “Exemplo de produção: preencher posteriormente uma página de capa reservada”

Um motivo comum para recorrer ao buffer é uma página de capa ou de resumo cujos números só são conhecidos depois que o corpo é disposto — uma contagem total de páginas, um total geral, uma contagem de registros. Reserve a página 1 de antemão, renderize o corpo e então preencha posteriormente a capa com setPage(1), e retome no final com lastPage(). Este exemplo também mostra os dois limites de falha fechada que você deve tratar: a UnsupportedFeatureException do adaptador para um número de página fora do intervalo e a RetainedPageBufferIncompatibleException do core se o documento também ativar um recurso incompatível.

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

Distinga as duas superfícies de falha deliberadamente:

  • UnsupportedFeatureException (adaptador) — um alvo de setPage() / lastPage() fora do intervalo, ou qualquer troca para página anterior quando o buffer está desativado.
  • RetainedPageBufferIncompatibleException (core, NextPDF\Exception\Strict) — o buffer está ativado, mas combinado com um recurso cujos metadados de nível de página não podem ser rederivados após um preenchimento posterior. Não existe um tipo RetainedPageBufferInconsistency; esta é a única exceção de incompatibilidade, e uma violação de orçamento aparece como \OverflowException.

O adaptador delega ao buffer de página retida do core, então as mesmas recusas se aplicam. O preenchimento posterior é recusado — de forma independente da ordem e antes da serialização — quando o documento também usa qualquer um de:

  • Uma assinatura digital.
  • PDF marcado (árvore de estrutura).
  • PDF/A.
  • Linearização.
  • Empacotamento de object stream.
  • Criptografia.
  • Modo de renderização Safe CSS.

Cada recusa é uma exceção tipada de falha fechada — nunca um descarte silencioso:

  • Combinar o buffer com qualquer recurso acima levanta a RetainedPageBufferIncompatibleException do core (namespace NextPDF\Exception\Strict, @since core 6.1.0). A verificação é independente da ordem: ela dispara independentemente de o recurso incompatível ter sido configurado antes ou depois de o buffer ser ativado.
  • Um orçamento de bytes não comprimidos de 16 MiB por documento limita o buffer; excedê-lo levanta \OverflowException no momento da build em vez de descartar uma página preenchida posteriormente.

O ponto da recusa é que um preenchimento posterior nunca pode alterar silenciosamente um documento assinado ou criptografado — os dois não podem ser ativados juntos.

  • Desativado por padrão. Construa sem a flag e o adaptador permanece inalterado; setPage() para uma página anterior ainda levanta UnsupportedFeatureException. Isso preserva o contrato de streaming para todo chamador existente.
  • Não é paridade legada. O TCPDF legado não tem a flag de construtor retainedPageBuffer. Documente isso como uma extensão NextPDF quando migrar, para que um leitor futuro não a confunda com um recurso do TCPDF.
  • lastPage() retorna ao final. Após um preenchimento posterior, chame lastPage() para retomar a anexação na página final.
  • Planeje um modo. Se o documento precisar ser assinado, marcado, PDF/A, linearizado, criptografado ou empacotado em object stream, não ative o buffer; em vez disso, pré-calcule o valor que você teria preenchido posteriormente.