跳到內容

建立具備自動分頁的多頁文件

建立一份內容橫跨多頁的文件。一邊建構文件,一邊加入內容。啟用 setAutoPageBreak() 後,當游標抵達底部邊界時,版面引擎會自動替你開新頁。呼叫 save() 之後,再用 getNumPages() 讀取最終頁數。本範例依循 examples/05-multi-page.php

save() 期間,引擎會將每一頁的繪製標記寫入一個內容串流。ISO 32000-2 §7.7.3.3 將一頁的 Contents 定義為單一串流,或依序串接的串流陣列。因此,多頁輸出是一系列頁物件,而不是單一緩衝區。

Terminal window
composer require nextpdf/core:^3

不需要任何選用擴充套件。本範例可在 PHP 8.1–8.4 的 backport 相容矩陣上執行。getNumPages()setAutoPageBreak() 自 1.0.0 起即為穩定 API。

一份 NextPDF 文件是一棵頁面樹(page tree)。隨著你加入內容,內部游標(getY())會往下推進。啟用自動分頁後,引擎會在每個內容區塊之前檢查剩餘的垂直空間。如果該區塊放不進底部邊界以上的空間,引擎就會清空目前頁面,並替你呼叫 addPage()。你傳給 setAutoPageBreak() 的底部邊界,就是觸發門檻。

頁面層級的屬性(例如 media box)是可繼承的。ISO 32000-2 §7.7.3.4 規定,頁物件中省略的屬性會從上層的頁面樹節點 resolve(解析)取得。NextPDF 在整份文件套用一致的頁面尺寸,因此每一頁產生時都共用相同的幾何設定,你不必逐頁重複設定。

API 介面由 PHPDoc 自動產生。本範例會使用下列方法:

  • Document::createStandalone(): self — 建立一份獨立文件。
  • setAutoPageBreak(bool $enabled, float $margin = 20): static — 啟用自動分頁。$margin 是以毫米為單位的底部邊界觸發值。
  • addPage(?PageSize $size = null, Orientation $orientation = Orientation::Portrait): static — 起始第一頁,以及任何明確新增的頁面。
  • multiCell(...): static / cell(...): static — 輸出可流動或固定的文字區塊。分頁檢查會量測這些區塊。
  • getNumPages(): int — 排版後的頁數。
<?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->setFont('helvetica', '', 11);
for ($i = 1; $i <= 60; $i++) {
$doc->multiCell(0, 7, "Line {$i}: content flows until the page is full, "
. 'then the engine starts a new page automatically.');
}
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf');
echo 'Pages: ' . $doc->getNumPages() . "\n";

這是完整、可直接搭配 harness(測試載具)執行的範例。它會遵循 harness 所設定的 NEXTPDF_COOKBOOK_OUTPUT,因此不要把 PDF 印到 STDOUT。它本身不固定任何亂源(entropy)。當 harness 執行它時,DeterministicMode 會固定時鐘、/ID 與品牌資訊。

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Multi-Page Document');
// Enable automatic page breaks. The 25 mm bottom margin is the trigger:
// when the cursor would cross it, the engine flushes the page and adds
// a new one before the next block is drawn.
$doc->setAutoPageBreak(true, margin: 25);
$doc->addPage();
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 12, 'Multi-Page Document Example', newLine: true);
$doc->ln(5);
for ($chapter = 1; $chapter <= 3; $chapter++) {
$doc->setFont('helvetica', 'B', 14);
$doc->cell(0, 10, "Chapter {$chapter}: Lorem Ipsum", newLine: true);
$doc->setFont('helvetica', '', 11);
for ($para = 1; $para <= 5; $para++) {
$text = "Paragraph {$para} of Chapter {$chapter}. "
. 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '
. 'Sed do eiusmod tempor incididunt ut labore et dolore magna '
. 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation '
. 'ullamco laboris nisi ut aliquip ex ea commodo consequat.';
$doc->multiCell(0, 7, $text);
$doc->ln(3);
}
$doc->ln(5);
}
// The harness sets NEXTPDF_COOKBOOK_OUTPUT; honour it. STDOUT stays free
// for progress text only.
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/multi-page.pdf';
$doc->save($out);
echo 'Created multi-page.pdf with ' . $doc->getNumPages() . " pages\n";
  • 停用自動分頁。 使用 setAutoPageBreak(false, …) 時,超過底部邊界的內容會被裁切在該頁上,不會繼續流動,文件也會維持單一頁。若內容需要流動,請啟用自動分頁。
  • 單一區塊高過整頁。 引擎會在內部把超過可列印高度的 multiCell 文字拆開。但若是單一區塊高過可用區域且不可分割(例如一張高大的影像),就只會放置一次並溢出。這時請自行手動分頁。
  • 第一次的 addPage() 仍是必要的。 當尚無任何頁面時,cell() 會視需要呼叫 addPage()。即便如此,仍請明確呼叫 addPage(),讓第一頁的尺寸與方向具有確定性。
  • 邊界單位。 在預設單位系統下,setAutoPageBreak() 的邊界是以毫米為單位,而不是以點(point)為單位。

getNumPages() 是 O(1) 操作。它只讀取一個計數器,不會重新進行版面排版。記憶體用量隨保留的內容量而成長,而非隨頁數成長。引擎會在每一頁完成時,就把已完成的頁面寫入輸出緩衝區並清空暫存狀態 — 這就是單趟(single-pass)串流模型(ADR-001)。2000 ms / 64 MB 的預算,足以在參考主機上處理數百頁文字文件。

本範例只會寫入你的程式碼所提供的文字。它不會進行任何輸入剖析、網路存取或反序列化。請把任何來自外部來源的文字視為不受信任,並在繪製之前先限制其長度。引擎不會替你強制套用應用層級的內容大小上限。

陳述規範條款參考 ID
一頁的 Contents 是單一串流,或一個依序串接的串流陣列。ISO 32000-2§7.7.3.3
頁物件中省略的可繼承頁面屬性,會從上層的頁面樹節點解析取得。ISO 32000-2§7.7.3.4
trailer 的 /ID 是由兩個位元組字串組成的檔案識別碼(在 PDF 2.0 中為必填)。ISO 32000-2§7.5.5

可重現性設定檔 — 結構層級(為何不是逐位元組)。 每一份儲存的文件都帶有一個 trailer /ID,其兩個位元組字串即為檔案識別碼(ISO 32000-2 §7.5.5,如上)。第二個元素並非執行間穩定,因此即使內容完全相同,每次執行的原始位元組仍會不同。harness 會比對經 qpdf 正規化後的結構,該流程會剝除 /ID/CreationDate/ModDate。本範例描述的是 NextPDF 如何產生這個結構。它並未概括性地宣稱符合 ISO 32000-2。

不適用。具備自動分頁的多頁排版屬於 Core 能力,沒有 Premium 的限制門檻。