긴 HTML을 여러 페이지로 나누기
한눈에 보기
섹션 제목: “한눈에 보기”자동 페이지 나누기를 사용해 긴 콘텐츠가 여러 페이지에 걸쳐 이어지도록 합니다. 독자가 섹션 사이를 이동할 수 있게 개요를 추가합니다. 이 레시피는 examples/12-bookmarks-and-toc.php를 따릅니다.
composer require nextpdf/core:^3이 제약 조건은 nextpdf/core 패키지와 일치합니다. 이 예제는 PHP 8.4에서 실행됩니다.
개념 개요
섹션 제목: “개념 개요”setAutoPageBreak(true, $margin)은 콘텐츠가 아래쪽 여백 임계값을 넘을 때마다 엔진이 새 페이지를 시작하도록 지시합니다. 엔진은 multiCell() 또는 writeHtml()을 통해 작성된 긴 텍스트를 그 경계에서 분할합니다. CSS Fragmentation 모듈(css_break_3)은 지원 매트릭스에서 Verified로 등급이 매겨져 있으며, HTML 파이프라인의 나누기 동작을 뒷받침합니다.
bookmark($title, $level)은 현재 위치를 가리키는 개요 항목을 추가합니다. PDF 개요 항목은 대상과 연결되므로 사용자가 해당 페이지로 바로 이동할 수 있습니다(ISO 32000-2). 엔진은 해당 대상을 항목의 Dest 항목으로 기록합니다(ISO 32000-2). level 인수는 항목을 리더의 사이드바에 있는 계층적 목차 안에 중첩합니다.
파이프라인은 단일 패스를 유지합니다(ADR-001). 페이지 분할은 보존된 레이아웃 트리에서가 아니라 스트림이 출력되는 동안 결정됩니다.
API 표면
섹션 제목: “API 표면”setAutoPageBreak(bool $enabled, float $margin = 20): static—NextPDF\Core\Concerns\HasPages.bookmark(string $title, int $level = 0, float $y = -1): static—NextPDF\Core\Concerns\HasNavigation.multiCell(...)/writeHtml(string $html): static—NextPDF\Core\Concerns\HasTextOutput.
전체 PHPDoc 표는 소스에서 생성됩니다.
코드 샘플 — 빠른 시작
섹션 제목: “코드 샘플 — 빠른 시작”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setAutoPageBreak(true, margin: 25);$doc->addPage();$doc->bookmark('Section 1', level: 0);$doc->setFont('helvetica', '', 11);
for ($i = 1; $i <= 80; $i++) { $doc->multiCell(0, 7, "Paragraph {$i} of a long flowing document.");}
$doc->save(__DIR__ . '/out.pdf');코드 샘플 — 프로덕션
섹션 제목: “코드 샘플 — 프로덕션”이 예제는 자체적으로 완결되어 있으며 하니스에서 실행할 수 있습니다. 중첩된 개요와 자동 페이지 나누기를 사용해 여러 챕터로 구성된 문서를 만들며, examples/12-bookmarks-and-toc.php를 반영합니다.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Bookmarks and Navigation');$doc->setPrintHeader(false);$doc->setPrintFooter(false);$doc->setAutoPageBreak(true, margin: 25);
$chapters = [ 'Chapter 1: Introduction' => ['What is NextPDF?', 'Key Features'], 'Chapter 2: Getting Started' => ['Installation', 'Your First PDF'], 'Chapter 3: Advanced Topics' => ['Worker-safe Architecture', 'Streaming Output'],];
$body = 'NextPDF is a modern PDF 2.0 library for PHP. This paragraph is ' . 'repeated so the content overflows the page and the engine inserts ' . 'an automatic page break at the bottom-margin threshold.';
foreach ($chapters as $chapter => $sections) { $doc->addPage(); $doc->bookmark($chapter, level: 0); $doc->setFont('helvetica', 'B', 18); $doc->cell(0, 12, $chapter, newLine: true); $doc->ln(3);
foreach ($sections as $section) { $doc->bookmark($section, level: 1); $doc->setFont('helvetica', 'B', 14); $doc->cell(0, 10, $section, newLine: true); $doc->setFont('helvetica', '', 11); for ($i = 0; $i < 12; $i++) { $doc->multiCell(0, 7, $body); } $doc->ln(4); }}
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/paginate-long-html.pdf');
echo "Wrote paginate-long-html.pdf\n";예상 STDOUT:
Wrote paginate-long-html.pdf예외 사례 및 주의 사항
섹션 제목: “예외 사례 및 주의 사항”- 비활성화 후 잊어버리기. 자동 페이지 나누기가 비활성화되어 있으면 엔진은 콘텐츠를 흐르게 하는 대신 아래쪽 여백을 넘는 부분을 잘라냅니다. 긴 콘텐츠를 처리하기 전에 다시 활성화해야 합니다.
- 분할할 수 없는 콘텐츠. 사용 가능한 페이지 높이보다 큰 단일 블록은
UnsplittableContentException을 발생시킬 수 있습니다. 매우 높은 표 행이나 큰 이미지가 그 원인 중 하나입니다. 소스 콘텐츠를 분할해야 합니다. - 콘텐츠 앞에 북마크 지정. 대상이 가리켜야 하는 위치에서
bookmark()를 호출해야 합니다. 원하는 페이지에서 다음에 작성할 제목 바로 앞에 배치합니다. - 머리글과 바닥글은 공간을 차지합니다. 인쇄 머리글이나 바닥글은 사용 가능한 콘텐츠 높이를 줄이며, 나누기 임계값은 이를 고려합니다. 예제에서처럼 둘 다 비활성화하면 본문 높이 전체를 사용할 수 있습니다.
- 개요 중첩.
level은 중첩 깊이입니다.level: 1의 자식은level: 0부모를 뒤따라야 합니다. 그렇지 않으면 리더가 개요 트리를 평면화합니다.
엔진은 단일 출력 패스 동안 페이지 분할을 결정합니다. 처리 비용은 콘텐츠 길이에 대해 선형으로 증가하며 O(n)입니다. 예산은 wall_ms: 2000, peak_mb: 96입니다. 다중 페이지 xref와 개요 조립 때문에 실제 실행 시간은 단일 페이지 레시피보다 약간 더 길게 나타납니다. 스트리밍 모델은 메모리를 제한된 상태로 유지하며, 개요는 작은 평면 목록입니다.
CSS 지원 매트릭스 발췌(Verified 행만)
섹션 제목: “CSS 지원 매트릭스 발췌(Verified 행만)”여기서는 진실성 감사를 거친 CSS 지원 매트릭스에서 Verified 행만 재현합니다.
| W3C 모듈 | 레벨 | 상태 | 증거 |
|---|---|---|---|
CSS Fragmentation(css_break_3) | 3 | Verified | src/Html/Fragmentation/, tests/Unit/Html/PagedMedia/ |
CSS Table(css_tables_3) | 3 | Verified | src/Html/Table/ + 골든 PDF |
CSS Cascading and Inheritance(css_cascade_3) | 3 | Verified | src/Html/Cascade/ |
@page 명명된 페이지 선택자는 CSS Paged Media의 일부입니다. 이에 의존하기 전에 매트릭스에서 해당 모듈의 현재 등급을 확인해야 합니다.
단일 패스 스트리밍 제약 조건(ADR-001)
섹션 제목: “단일 패스 스트리밍 제약 조건(ADR-001)”엔진은 스트림이 흐르는 동안 페이지 나누기를 출력합니다. 다시 흐르게 할 수 있는 보존된 트리가 없으므로, 나누기 결정은 한 번 내려지면 최종적입니다. 일부 콘텐츠에는 레이아웃 이후 확정된 페이지 번호가 필요합니다. 예를 들어 상호 참조가 그렇습니다. 그러한 콘텐츠는 제약을 받으므로, 그 한계를 염두에 두고 작성해야 합니다.
레이어 계약(ADR-010)
섹션 제목: “레이어 계약(ADR-010)”페이지 분할은 파서가 아니라 페이지 나누기 컨트롤러가 책임집니다. 파서는 원시 페이지 전환 연산자를 출력하지 않으며, 컨트롤러 계약을 통해 나누기를 요청합니다.
대용량 문서를 위한 메모리 예산
섹션 제목: “대용량 문서를 위한 메모리 예산”스트리밍 모델은 모든 페이지를 한 번에 보유하는 대신 현재 페이지 버퍼와 평면 개요 목록을 보유합니다. 엔진이 완료된 페이지를 플러시하기 때문에 매우 긴 문서도 ADR-020 상한 내에 유지됩니다. 표와 플렉스 컨테이너는 여전히 컨텍스트당 5,000개 노드 제한을 따릅니다.
보안 참고 사항
섹션 제목: “보안 참고 사항”악의적인 문서가 무제한 메모리를 강제할 수는 없습니다. 요소 및 중첩 상한(ADR-001)과 컨텍스트당 노드 예산(ADR-020)이 작업을 제한합니다. 사용자가 제공한 긴 콘텐츠의 길이와 구조를 검증해야 합니다. 엔진은 공격자가 제어하는 개요 제목을 결코 해석하지 않고 텍스트로 렌더링합니다.
| 설명 | 사양 | 조항 | reference_id(참조 ID) |
|---|---|---|---|
| 각 개요 항목은 대상과 연결될 수 있으므로 사용자가 해당 항목으로 바로 이동합니다. | ISO 32000-2 | iso32000_2_sec12#x1.x5.p4 | |
| 개요 항목의 Dest 항목은 해당 항목이 활성화될 때 표시되는 대상을 지정합니다. | ISO 32000-2 | iso32000_2_sec12#x1.x11.p30 |
이 레시피는 NextPDF가 긴 콘텐츠를 흐르게 하고 개요를 구성하는 방법을 보여 줍니다. CSS Fragmentation은 지원 매트릭스에서 Verified로 등급이 매겨져 있습니다.
상업적 맥락
섹션 제목: “상업적 맥락”해당 없음.