自動改ページで複数ページのドキュメントを作成する
コンテンツが複数ページにまたがるドキュメントを作成します。処理の進行に合わせてコンテンツを追加していきます。setAutoPageBreak() を有効にすると、カーソルが下マージンに達した時点で、レイアウトエンジンが新しいページを自動的に開始します。save() の後に、getNumPages() で最終的なページ数を取得します。このレシピは examples/05-multi-page.php に基づいています。
ドキュメントの save() の実行中、エンジンは各ページの描画内容をコンテンツストリームに書き込みます。ISO 32000-2 §7.7.3.3 では、ページの Contents を 1 つのストリーム、または順番に連結されたストリームの配列として定義しています。そのため、複数ページの出力は単一のバッファではなく、ページオブジェクトのシーケンスになります。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3オプションの拡張機能は必要ありません。このレシピは PHP 8.1~8.4 のバックポートマトリックスで動作します。getNumPages() と setAutoPageBreak() はいずれも 1.0.0 以降、安定しています。
概念の概要
「概念の概要」という見出しのセクションNextPDF のドキュメントはページツリーです。コンテンツを追加していくと、内部カーソル(getY())が進みます。自動改ページが有効な場合、エンジンは各コンテンツブロックを配置する前に、残りの垂直方向の空き領域を確認します。ブロックが下マージンより上に収まらない場合、エンジンは現在のページをフラッシュし、addPage() を自動的に呼び出します。setAutoPageBreak() に渡す下マージンが、トリガーのしきい値となります。
メディアボックスなどのページレベル属性は継承可能です。ISO 32000-2 §7.7.3.4 では、ページオブジェクトから省略された属性が祖先のページツリーノードから解決(resolve)されることを規定しています。NextPDF はドキュメント全体で一貫した 1 つのページサイズを設定するため、生成されるすべてのページが同じジオメトリを共有し、ページごとに指定を繰り返す必要はありません。
API サーフェス
「API サーフェス」という見出しのセクション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";コードサンプル — 本番環境
「コードサンプル — 本番環境」という見出しのセクションこれは、ハーネスでそのまま実行できる完全な例です。ハーネスが設定する NEXTPDF_COOKBOOK_OUTPUT を尊重する例なので、PDF を STDOUT に出力しないでください。この例自体では、エントロピーを固定しません。ハーネスがこの例を実行すると、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, …)を指定すると、下マージンを超えたコンテンツは次ページへ流し込まれず、そのページ内でクリップされ、ドキュメントは 1 ページのままになります。流し込み型のコンテンツでは有効にしてください。 - ページより高い単一のブロック。 エンジンは、テキストが印刷可能な高さを超える
multiCellを内部で分割します。ただし、使用可能領域より高い分割不可能な単一ブロック(たとえば高さのある画像)は、配置されたうえではみ出します。手動で分割してください。 - 最初の
addPage()は依然として必要です。cell()は、ページが存在しない場合に必要に応じてaddPage()を呼び出します。それでも、最初のページのサイズと向きが確定的になるように、addPage()を明示的に呼び出してください。 - マージンの単位。
setAutoPageBreak()のマージンは、デフォルトの単位系ではポイントではなくミリメートル単位です。
パフォーマンス
「パフォーマンス」という見出しのセクションgetNumPages() は O(1) です。これはカウンターを読み取るだけで、再レイアウトは行いません。メモリ使用量はページ数ではなく、保持しているコンテンツに比例します。エンジンはページが完成した時点で、出力バッファにフラッシュします。これがシングルパスのストリーミングモデル(ADR-001)です。2000 ms / 64 MB のバジェットは、リファレンスホスト上で数百ページ程度のテキストドキュメントをカバーします。
セキュリティに関する注意
「セキュリティに関する注意」という見出しのセクションこのレシピは、アプリケーション側のコードが提供するテキストのみを書き込みます。入力の解析、ネットワークアクセス、デシリアライズはいずれも行いません。外部から取得したテキストはすべて信頼できないものとして扱い、レンダリングの前に長さを制限してください。アプリケーションレベルのコンテンツサイズ制限を、エンジンが代わりに課すことはありません。
| 記述 | 仕様 | 箇条 | リファレンス ID |
|---|---|---|---|
ページの Contents は、単一のストリーム、または順序付けられ連結されたストリームの配列 | ISO 32000-2 | §7.7.3.3 | |
| ページオブジェクトから省略された継承可能なページ属性は、祖先のページツリーノードから解決 | ISO 32000-2 | §7.7.3.4 | |
トレーラーの /ID は、2 つのバイト文字列から成るファイル識別子(PDF 2.0 で必須) | ISO 32000-2 | §7.5.5 |
再現性プロファイル — 構造的(ビット単位ではない理由)。 保存されるすべてのドキュメントには、2 つのバイト文字列がファイル識別子となるトレーラー /ID が付与されます(前述の ISO 32000-2 §7.5.5)。2 番目の要素は実行間で安定しないため、コンテンツが同一であっても実行ごとに生のバイト列は異なります。ハーネスでは、qpdf で正規化された構造を比較します。その正規化では、/ID、/CreationDate、および /ModDate を除去します。このレシピでは、NextPDF がこの構造をどのように作り出すかを説明します。これは ISO 32000-2 への準拠を一律に主張するものではありません。
商用コンテキスト
「商用コンテキスト」という見出しのセクション該当しません。自動改ページによる複数ページの構成は Core の機能であり、Premium ゲートの対象ではありません。