콘텐츠로 이동
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 코어 6.1.0)을 발생시킵니다. 검사는 순서와 무관합니다. 비호환 기능이 버퍼 옵트인 전에 구성되었든 후에 구성되었든 발동합니다.
  • 문서별 16 MiB 비압축 바이트 예산이 버퍼를 제한합니다. 그것을 초과하면 백필된 페이지를 폐기하는 대신 빌드 시점에 \OverflowException을 발생시킵니다.

거부의 요점은 백필이 서명되거나 암호화된 문서를 결코 조용히 변경할 수 없다는 것입니다 — 두 가지를 함께 활성화할 수 없습니다.

  • 기본 꺼짐. 플래그 없이 생성하면 어댑터는 변경되지 않습니다. 이전 페이지로의 setPage()는 여전히 UnsupportedFeatureException을 발생시킵니다. 이는 모든 기존 호출자에 대해 스트리밍 계약을 보존합니다.
  • 레거시 패리티 아님. 레거시 TCPDF에는 retainedPageBuffer 생성자 플래그가 없습니다. 마이그레이션할 때 이것을 NextPDF 확장으로 문서화하여, 미래의 독자가 그것을 TCPDF 기능으로 오해하지 않게 하세요.
  • lastPage()는 끝으로 돌아갑니다. 백필 후, 마지막 페이지에서 추가를 재개하려면 lastPage()를 호출하세요.
  • 하나의 모드를 계획하세요. 문서가 서명되거나, 태그되거나, PDF/A이거나, 선형화되거나, 암호화되거나, 객체 스트림이어야 한다면 버퍼를 활성화하지 말고, 대신 백필했을 값을 미리 계산하세요.