契約 / ドキュメント
ドキュメントドメインには、PDF を構築するための契約が集約されています。コンテンツを扱う PdfDocumentInterface、ワーカーセーフな生成を担う DocumentFactoryInterface、フォントと画像のレジストリ契約、そして配信とレイアウトに関する 3 つの enum が含まれます。いずれも 1.0.0 または 1.7.0 以降 stable です。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3概念の概要
「概念の概要」という見出しのセクションPdfDocumentInterface は主要な API サーフェスです。ページ管理、フォント選択、セルおよびマルチセルのテキストレイアウト、HTML レンダリング、画像の埋め込み、そして最終出力を定義します。すべてのメソッドが static を返すため、呼び出しをチェーンできます。Document::createStandalone() は、このインターフェイスを満たす具象インスタンスを返します。独自のサービスでは、このインターフェイスを型ヒントとして指定してください。これにより、エンジン内部を差し替え可能な状態に保てます。
ドキュメントの生成には 2 つの経路があります。従来の PHP-FPM リクエストでは、createStandalone() がプライベートなレジストリを備えた自己完結型のドキュメントを構築します。長時間稼働するワーカーは、もう一方の経路を使用します。これには RoadRunner、Swoole、Laravel Octane が含まれます。そこでは、DocumentFactoryInterface::create() が使い捨ての新しい Document を返します。ドキュメントはプロセス存続期間のレジストリから読み取りますが、それらを変更することはありません。ファクトリは FontRegistryInterface と ImageRegistryInterface のシングルトンを保持します。各ドキュメントは、それぞれ専用のレンダリングコンテキストとライターを取得します。これは障害を封じ込めるための設計です。あるドキュメントが、別のドキュメントの依存する共有状態を破損させることはできません。
ワーカーが高速性を保てる理由は、レジストリ契約にあります。FontRegistryInterface はフォントファイルを一度だけ解析します。解析したメタデータは、プロセスの存続期間にわたってキャッシュされます。ウォームアップ後にロックできるため、本番トラフィック中に変更されることはありません。ImageRegistryInterface は、上限付きの最長未使用(LRU)ポリシーに従って、デコード済みの画像バイナリデータをキャッシュします。画像のメタデータは、バイナリが破棄された後もメモリに常駐し続けます。どちらもキャパシティプランニング向けに memoryUsage() を公開します。ImageRegistryInterface は ResettableService を継承します。この契約により、構造メタデータを破棄することなくキャッシュ済みデータを退避できます。ワーカーは、メモリ圧迫時に画像キャッシュを破棄しつつ処理を継続できます。
3 つの enum がこのドメインを補完します。OutputDestination は、インライン表示、強制ダウンロード、ファイルシステムへの書き込み、生の文字列としての返却のいずれかを選択します。Orientation は、縦向きまたは横向きを選択します。Alignment は、テキストの左揃え、中央揃え、右揃え、両端揃えのいずれかを選択します。各 enum は、enum 値としてレガシーの TCPDF コードを各ケースに割り当てています。そのため、compat-tcpdf ブリッジは自然にマッピングできます。これらの enum に対する後方互換性の保証は、追加のみを行うものです。どのケースも削除されることはありません。新しいケースはマイナーリリースで追加される場合があります。
API サーフェス
「API サーフェス」という見出しのセクション| 型 | 種別 | 主なメンバー | 安定性 | 対応バージョン |
|---|---|---|---|---|
PdfDocumentInterface | interface | addPage(), setMargins(), setFont(), cell(), multiCell(), writeHtml(), image(), output(), save() | stable | 1.0.0 |
DocumentFactoryInterface | interface | create(?Config): Document | stable | 1.7.0 |
ResettableService | interface | reset(): void | stable | 1.7.0 |
FontRegistryInterface | interface | register(), get(), warmup(), lock(), isLocked(), registerFromBinary(), memoryUsage() | stable | 1.7.0 |
ImageRegistryInterface | interface | load(), loadFromString(), getMetadata(), memoryUsage()(ResettableService を継承) | stable | 2.0.0 |
OutputDestination | enum (string) | Inline, Download, File, String | stable | 1.0.0 |
Orientation | enum (string) | Portrait, Landscape | stable | 1.0.0 |
Alignment | enum (string) | Left, Center, Right, Justify | stable | 1.0.0 |
FontRegistryInterface と ImageRegistryInterface については、タイポグラフィのページで詳しく説明しています。このドキュメントページでは、生成ライフサイクルにおける両者の役割を扱います。
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクション<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Hello World');$doc->addPage();$doc->setFont('helvetica', '', 24);$doc->cell(0, 15, 'Hello, NextPDF!', newLine: true);$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'This is a minimal PDF generated with NextPDF.', newLine: true);$doc->save(__DIR__ . '/output/01-hello-world.pdf');コードサンプル — 本番
「コードサンプル — 本番」という見出しのセクション<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Core\PdfFactory;use NextPDF\ValueObjects\{Margin, PageSize};
$factory = PdfFactory::new() ->withPageSize(PageSize::A4()) ->withMargins(new Margin(15.0, 15.0, 15.0, 15.0)) ->withCompress(true) ->withLang('en');
// The same configured factory creates independent documents.$doc = $factory->create();$doc->setTitle('PdfFactory Example');$doc->setAuthor('NextPDF');$doc->addPage();$doc->setFont('helvetica', '', 16);$doc->cell(0, 12, 'Created via PdfFactory', newLine: true);
$doc2 = $factory->create();$doc2->addPage();$doc2->setFont('helvetica', '', 12);$doc2->cell(0, 10, 'Second document from the same factory.');
$doc->save(__DIR__ . '/output/02-pdf-factory.pdf');PdfFactory はイミュータブルなビルダーであり、各 with*() は新しいインスタンスを返します。内部では DocumentFactoryInterface を組み合わせているため、概要で説明したワーカーのレジストリモデルが追加の配線なしで適用されます。
エッジケースと注意点
「エッジケースと注意点」という見出しのセクションcreateStandalone()はプライベートなレジストリを構築します。ワーカーループで使うと、リクエストごとにすべてのフォントが再解析されます。代わりに、共有レジストリを備えたDocumentFactoryInterfaceを使用してください。Documentは、設計上そもそも使い捨てです。1 つのインスタンスを論理的に異なるドキュメント間で再利用すると、状態が漏れます。ドキュメントごとにcreate()を呼び出し、ガベージコレクションに回収させてください。FontRegistryInterface::lock()を呼ぶと、register()、addFontDirectory()、warmup()はLogicExceptionをスローするようになります。ロックはウォームアップ後に行い、リクエスト処理中には決して行わないでください。OutputDestination::Fileはサーバーのファイルシステムに書き込み、生のバイトを返します。save()は明示的なファイルパスを指定します。同じドキュメントに対してこの 2 つを混在させないでください。cell()は、TCPDF 互換性のため border 引数にbool|stringを受け付けます。空文字列はfalseと同じではありません。意図した型の値を渡してください。
パフォーマンス
「パフォーマンス」という見出しのセクションフォントと画像のレジストリにより、ドキュメントドメインはリクエストごとのシステムではなく、メモリ制約型のシステムになります。支配的なのは初回リクエスト時のフォント解析です。performance_budget は、ワーカーの例で 3 つのドキュメントにわたり、実時間 1500 ms、ピーク 64 MB です。そのバジェットのほとんどは、初回のフォント解析です。ウォームアップ後、ドキュメントごとに契約に起因する処理は O(1)、つまりレジストリの参照とコンテキストの割り当てです。いずれのレジストリでも memoryUsage() は、リアルタイムのキャパシティプランニング向けに MemoryReport を返します。ResettableService::reset() は、持続的な負荷の下でピークメモリを抑制します。
セキュリティ上の注意
「セキュリティ上の注意」という見出しのセクションドキュメント契約には暗号関連のサーフェスはありませんが、運用上は 2 つのリスクが当てはまります。第 1 に、image() はパスまたは URL を受け付けます。信頼できない入力を扱うシナリオでは、ユーザー制御の URL を直接渡すのではなく、ExternalResourcePolicyInterface(セキュリティポリシーのページを参照)を通じてリモート取得を制限してください。第 2 に、writeHtml() は HTML パイプラインのエントリーポイントです。信頼できないマークアップは、レンダリング前に HtmlSecurityPolicyInterface を通過させる必要があります。ドキュメント層自体はサニタイズを行いません。それはセキュリティポリシードメインの役割であり、契約であるため、フォークすることなくより厳格なポリシーを提供できます。
ドキュメント契約は、ISO 32000-2 で定義されている PDF 2.0 のドキュメント構造を実装します。出力、ページ、フォントの処理は、ISO 32000-2 §7 に従って間接オブジェクトとクロスリファレンスストリームを生成します。コンテンツは、エンジン層の契約(ADR-010)に従ってライター層を通じて出力されます。このページでは、構造的な準拠を超える条項レベルの主張は行いません。PDF/A および PDF/UA への準拠は、規定の表を掲載している抽出ページとアクセシビリティページで説明しています。
- 契約: 41 個のパブリックインターフェイス(SPI) — SPI の概要と安定性ティア。
- 契約 / タイポグラフィ —
FontRegistryInterfaceの詳細。 - 契約 / セキュリティポリシー —
writeHtml()とimage()をゲートするポリシー。 - Core —
DocumentとPdfFactoryの具象クラス。 - Document — ドキュメント構築モジュール。
- Writer — これらの契約に基づいて PDF オブジェクトを出力する層。