版面配置:頁首、頁尾、欄、書冊與頁面管理員
Layout 模組收納各種頁面裝飾引擎,Document 外觀(facade)會將工作委派給這些引擎:頁首與頁尾繪製、多欄版面、騎馬釘書冊拼版,以及結構性頁面操作。這是一個小而穩定的模組,共有六個類別,全部都是 @since 1.0.0。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段Layout(src/Layout/,六個類別,@since 1.0.0)是 HasLayout 這個 concern 底下的引擎層。應用程式碼會呼叫 Document 上的外觀方法;trait 會將每次呼叫路由到其中一個引擎。在 manifest 中,這個模組標記為 standard 風險與 internal 穩定度,唯一的相依方是 Core。你透過外觀使用它,而不是直接建構這些類別。
HeaderFooter 負責繪製重複出現的頁首與頁尾。它會為每個區帶保留標題、描述、標誌、字型、邊界與色彩等狀態,並透過 renderHeader() 與 renderFooter() 視需要產生 PDF 內容串流運算子。預設頁尾會印出一個靠右對齊的 "page / total" 字串。setHeaderCallback() 與 setFooterCallback() 會以呼叫端提供的 closure 取代預設版面。getHeaderContentHeight() 會回報頁首佔用的垂直空間,讓頁面本文能從其下方開始排版。當文件處於 tagged-PDF 模式時,自動頁首會由上游的 HasPages 抑制,因為頁首內容位於結構樹之外。
ColumnLayout 負責管理多欄流式排版。setEqualColumns(int $count, float $totalWidth, float $gap = 5) 會把可用寬度均分成等寬的欄。setColumnsArray() 則接受明確的 ColumnDefinition 位置與寬度。游標可透過 selectColumn() 選取某一欄,或透過 nextColumn() 前進到下一欄。getCurrentColumnX() / getCurrentColumnWidth() 會回傳目前作用中欄的幾何資訊。欄數無效、間距為負,或計算出的欄寬非正值時,會引發 PageLayoutException。
BookletLayout 會為騎馬釘(中央對折)裝訂重新排序頁面。reorderPages() 會把頁面清單補齊到四的倍數(一張書冊紙容納四個頁面格位),再由外而內拼版,讓紙張對折並裝訂後能依序閱讀。getMarginAdjustments() 會回傳指定位置左右兩側的內側(書脊)與外側(邊緣)邊界。getSheetCount() 會回報某個頁數需要多少張雙面紙張。重新排序只會改變內容的擺放位置。底層的 PDF 頁面序列維持線性,與頁面樹一致;頁面樹定義了文件中頁面的排列順序(ISO 32000-2 §7.7)。
PageManager 提供獨立於內容繪製之外的結構性頁面操作。movePage()、copyPage() 與 deletePage() 會以參考方式對一個 PageData 陣列進行操作。頁面區域(addPageRegion()、isInRegion()、getRegionOffset())用來定義禁止寫入的區帶。頁面群組(startPageGroup()、getGroupPageNo())支援分節編頁。PageRegion 與 ColumnDefinition 是這些引擎使用的兩個數值載體。Writer 模組會把產生的頁面序列化成頁面樹;其 Kids 項目是一個陣列,內含指向該頁面樹節點直屬子節點的間接參考(ISO 32000-2 §7.7.3.2)。
API 介面
標題為「API 介面」的區段| 符號 | 種類 | 穩定度 | 自 |
|---|---|---|---|
HeaderFooter::setHeaderData(string, string, string, float): self | 方法 | 穩定 | 1.0.0 |
HeaderFooter::setHeaderFont(string, float): self / setHeaderMargin(float): self | 方法 | 穩定 | 1.0.0 |
HeaderFooter::setFooterFont(string, float): self / setFooterMargin(float): self | 方法 | 穩定 | 1.0.0 |
HeaderFooter::setHeaderCallback(Closure): self / setFooterCallback(Closure): self | 方法 | 穩定 | 1.0.0 |
HeaderFooter::getHeaderContentHeight(): float | 方法 | 穩定 | 1.0.0 |
HeaderFooter::renderHeader(float, float, float, float, int, int): string | 方法 | 穩定 | 1.0.0 |
HeaderFooter::renderFooter(float, float, float, float, int, int, string): string | 方法 | 穩定 | 1.0.0 |
ColumnLayout::setEqualColumns(int, float, float): self | 方法 | 穩定 | 1.0.0 |
ColumnLayout::setColumnsArray(array): self / resetColumns(): self | 方法 | 穩定 | 1.0.0 |
ColumnLayout::selectColumn(int): self / nextColumn(): bool | 方法 | 穩定 | 1.0.0 |
ColumnLayout::getCurrentColumnX(float): float / getCurrentColumnWidth(float): float | 方法 | 穩定 | 1.0.0 |
BookletLayout::setBooklet(bool, float, float): void | 方法 | 穩定 | 1.0.0 |
BookletLayout::reorderPages(array): array | 方法 | 穩定 | 1.0.0 |
BookletLayout::getMarginAdjustments(int): array{left: float, right: float} | 方法 | 穩定 | 1.0.0 |
BookletLayout::getSheetCount(int): int | 方法 | 穩定 | 1.0.0 |
PageManager::movePage(int, int, array): void / copyPage(int, array): void / deletePage(int, array): void | 方法 | 穩定 | 1.0.0 |
PageManager::addPageRegion(float, float, float, float): void / isInRegion(float, float): bool | 方法 | 穩定 | 1.0.0 |
PageManager::getRegionOffset(float, float, float, float): float | 方法 | 穩定 | 1.0.0 |
PageManager::startPageGroup(): void / getGroupPageNo(int): int | 方法 | 穩定 | 1.0.0 |
PageRegion / ColumnDefinition | 數值載體 | 穩定 | 1.0.0 |
程式碼範例——快速上手
標題為「程式碼範例——快速上手」的區段<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Header and Footer');
$doc->setHeaderData( title: 'NextPDF Example', description: 'Header and Footer Demonstration',);$doc->setHeaderFont('helvetica', 10);$doc->setHeaderMargin(5);$doc->setFooterFont('helvetica', 8);$doc->setFooterMargin(10);
$doc->addPage();$doc->setFont('helvetica', 'B', 16);$doc->cell(0, 12, 'Document with Header and Footer', newLine: true);
$doc->save(__DIR__ . '/output/13-header-footer.pdf');來源:examples/13-header-footer.php。頁首會在每次 addPage() 時繪製;頁尾則在頁面排清時繪製。
程式碼範例——正式環境
標題為「程式碼範例——正式環境」的區段頁碼文字由頁尾 callback 掌管,雙欄本文則由欄引擎驅動。這兩個引擎都透過外觀使用。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Two-Column Report');
$doc->setFooterCallback(static function (Document $d): void { $d->setFont('helvetica', '', 8); $d->text(180.0, 285.0, 'Page ' . ($d->getPage() + 1));});
$doc->addPage();$doc->setEqualColumns(2, gap: 8);$doc->selectColumn(0);$doc->setFont('helvetica', '', 10);$doc->multiCell(0, 6, 'Left column flows here.');$doc->selectColumn(1);$doc->multiCell(0, 6, 'Right column flows here.');
$doc->save(__DIR__ . '/output/two-column-report.pdf');來源:取自 examples/13-header-footer.php 的模式。
邊界情況與陷阱
標題為「邊界情況與陷阱」的區段setEqualColumns()會拒絕欄數小於 1、間距為負,或計算後欄寬非正的版面。上述每一種情況都會引發PageLayoutException,而不會回傳降級的版面。selectColumn()會忽略超出範圍的 Index(索引)並維持目前的欄;它絕不會因索引錯誤而擲出例外。當nextColumn()已位於最後一欄時,會回傳false。BookletLayout::reorderPages()會用空白頁補齊到四的倍數,這些空白頁的尺寸會從最後一頁複製而來。空的頁面清單會回傳空陣列。重新排序只影響擺放位置;movePage()的索引仍然指涉邏輯順序。PageManager::movePage()、copyPage()與deletePage()在索引超出範圍時會靜默地不做任何事;它們以isset()驗證,並在不修改陣列的情況下直接返回。當缺頁屬於呼叫端的錯誤時,請自行驗證索引。getHeaderContentHeight()在頁首被停用,或既無標題也無描述時,會回傳0.0。此時頁面本文會從上邊界開始。- 在 tagged-PDF 模式下,自動頁首會在上游被抑制。請為無障礙文件建構具結構感知的版面。
頁首與頁尾繪製會以 O(裝飾內容) 的成本,把運算子附加到作用中的頁面緩衝區;成本與寫入的標題、描述與分隔線成正比,而非與文件大小成正比。欄的計算每次呼叫都是 O(1)。BookletLayout::reorderPages() 在頁數上是 O(n),外加一次性的補齊處理;拼版迴圈會對每個補齊後的格位各處理一次。PageManager 對每個點的區域測試是 O(區域數);頁面操作則是 O(n) 的陣列切分與拼接。這些引擎都不會跨整份文件保留各頁狀態,因此在長文件上不會造成記憶體成長。主要的記憶體成本來自累積的內容串流,這正是 串流與記憶體概念 所涵蓋的內容。HTML 管線的延遲與記憶體預算 gate 記載於 PERFORMANCE-BUDGETS;它的範圍限於 HTML 繪製路徑,並不會直接對這些版面引擎設下 gate。這個 performance_budget 的 1500 ms / 64 MB 是快速上手範例的整體 canvas 預算上限,而非每次呼叫的契約。
安全性注意事項
標題為「安全性注意事項」的區段這些引擎會處理呼叫端提供的字串與一個標誌路徑。標誌路徑會流經影像引擎,影像引擎會在檔案嵌入前先驗證。renderHeader() 與 renderFooter() 會在標題、描述與頁碼文字進入內容串流之前,先透過集中式的 PDF 字串轉義器加以轉義,因此呼叫端文字無法跳脫字面字串的語法。頁首或頁尾 callback 會執行呼叫端程式碼,其信任程度與文件其餘部分相同;請據此看待它所讀取的任何外部資料。PageManager 的頁面操作只是移動指向既有 PageData 的參考;它們不會剖析不受信任的位元組。
符合性
標題為「符合性」的區段| 聲明事項 | 標準 | 條款 | 佐證 |
|---|---|---|---|
| 為書冊輸出而重新排序頁面只會改變擺放位置;頁面樹仍然定義文件中頁面的線性排列順序。 | ISO 32000-2 | §7.7 | |
序列化後的頁面樹節點,其 Kids 項目是一個陣列,內含指向該節點直屬子節點的間接參考。 | ISO 32000-2 | §7.7.3.2 |
以上條款皆為改寫,並已對應到詞彙表;未重現任何規範性內文。
另請參閱
標題為「另請參閱」的區段- Core/HasLayout trait——組合這些引擎的外觀 concern。
- Core/HasPages trait——欄計算所讀取的頁面尺寸與邊界。
- Writer——將排好版的頁面序列化為 PDF 物件與頁面樹的輸出器。
- 串流與記憶體——說明為何裝飾引擎不保留各頁狀態,以及受記憶體限制的繪製路徑。
- HTML / 串流限制(ADR-001)——單趟串流範圍的設計理據。