為長篇 HTML 自動分頁
使用自動分頁,讓長篇內容延展至多頁。加上大綱後,讀者就能在各章節之間跳轉。這則 recipe(範例)依循 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)。分頁是在串流(stream)輸出的同時決定的,而非仰賴保留下來的版面樹。
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 的列)」的區段此處只重現評為 Verified 的列,來源為已通過真實性稽核的 CSS 支援矩陣。
| W3C 模組 | 層級 | 狀態 | 佐證 |
|---|---|---|---|
CSS 分段(css_break_3) | 3 | 已驗證 | src/Html/Fragmentation/、tests/Unit/Html/PagedMedia/ |
CSS 表格(css_tables_3) | 3 | 已驗證 | src/Html/Table/ 加上 golden PDF |
CSS Cascading and Inheritance(層疊與繼承)(css_cascade_3) | 3 | 已驗證 | src/Html/Cascade/ |
@page 具名頁面選擇器屬於 CSS Paged Media。在依賴它之前,請先查閱矩陣,確認該模組目前的評等。
單一遍歷串流的限制(ADR-001)
標題為「單一遍歷串流的限制(ADR-001)」的區段引擎會隨串流輸出斷頁。沒有保留下來的樹可供重新排版,因此斷頁決定一旦做出就是最終結果。有些內容需要在排版後才能得到最終頁碼,例如交叉參照。這類內容受到限制,因此撰寫時要把這項限制納入考量。
層級合約(ADR-010)
標題為「層級合約(ADR-010)」的區段分頁由斷頁控制器(controller)負責,而非由 parser(剖析器)負責。parser 不會輸出原始的換頁運算子,而是透過控制器合約請求斷頁。
大型文件的記憶體預算
標題為「大型文件的記憶體預算」的區段串流模型只持有目前的頁面緩衝區加上扁平的大綱清單,而非一次持有所有頁面。很長的文件也會維持在 ADR-020 的上限內,因為引擎會把已完成的頁面寫出。表格與 flex 容器仍受 5,000 個節點/每個排版脈絡的上限約束。
安全性注意事項
標題為「安全性注意事項」的區段惡意文件無法強迫記憶體無界成長。元素與巢狀上限(ADR-001)以及每個脈絡的節點預算(ADR-020)會限制其工作量。請驗證使用者提供的長篇內容長度與結構。引擎會把攻擊者可控制的大綱標題當作文字繪製,絕不對其加以解讀。
符合性
標題為「符合性」的區段| 陳述 | 規範 | 條款 | 參考 ID |
|---|---|---|---|
| 每個大綱項目都可與一個目標位置關聯,讓使用者直接跳到該位置。 | ISO 32000-2 | iso32000_2_sec12#x1.x5.p4(第 12 節) | |
| 大綱項目的 Dest 項目會指定該項目被啟用時所顯示的目標位置。 | ISO 32000-2 | iso32000_2_sec12#x1.x11.p30(第 12 節) |
這則 recipe 示範 NextPDF 如何讓長篇內容流動並建立大綱。 CSS 分段在支援矩陣中評為 Verified。
商業情境
標題為「商業情境」的區段不適用。