コンテンツにスキップ

Writer: PDF 2.0 シリアライザー + xref

Writer モジュールは、ドキュメントを PDF バイト列へシリアライズします。バージョンストラテジーを選択し、オブジェクトグラフを書き込み、クロスリファレンス構造とトレーラーを出力します。

Terminal window
composer require nextpdf/core:^3

PdfWriter がエントリーポイントです。write() メソッドは、DocumentData 値オブジェクトを受け取り、完全な PDF をバイト文字列として返します。ライターはオブジェクトグラフを組み立て、オブジェクト番号を割り当て、バイトオフセットを記録し、最後にクロスリファレンス構造を書き込みます。

ライターは、呼び出しごとに 1 つのシリアライゼーションストラテジーを使用します。PdfSerializationStrategy インターフェイスは、4 つのメソッドを定義します。writeHeader()getCatalogVersion()writeXrefAndTrailer()、および usesXrefStream() です。3 つのストラテジーがこのインターフェイスを実装します。Pdf20StreamStrategy%PDF-2.0 ヘッダーを書き込み、カタログバージョンを /2.0 に設定し、クロスリファレンス stream を出力します。Pdf17TableStrategy%PDF-1.7 と従来のクロスリファレンス table を書き込みます。Pdf14TableStrategy%PDF-1.4 とクロスリファレンステーブルを書き込みます。PdfWriter は、DocumentData::$outputProfilematch を適用してストラテジーを選択します。デフォルトは Pdf20StreamStrategy です。

列挙型 PdfOutputProfile は、3 つのターゲットバージョンを保持します。Pdf20Pdf17、および Pdf14 です。この列挙型は、headerVersion()catalogVersion()allowsObjectStreams()、および usesXrefStream() を公開します。アーカイブ適合モードは、ストラテジー選択の前に、選択されたプロファイルを上書きします。Pdf14FeatureGuard は、プロファイルが Pdf14 の場合に PDF 2.0 の機能を拒否します。

クロスリファレンスストリームは、各オブジェクト番号をそのバイトオフセットにマッピングします(ISO 32000-2 §7)。増分更新は、新しいオブジェクトをファイルの末尾に追加します(ISO 32000-2 §7.5.6)。ライターは、すべてのリテラル文字列を正規の PdfStringEscaper::escapeLiteral() シームを通じてエスケープします。これは ISO 32000-2 §7.3.4.2 の規定エスケープテーブルに従います(ADR-015)。

ライターは、確定的な出力をサポートします。setDeterministicMode() は、オブジェクト識別子とディクショナリのキー順序を固定します。setReproducibleClock() は、ドキュメントのタイムスタンプを固定します。両方を固定すると、固定された入力からバイト単位で同一の出力が生成されます。writeChunked() メソッドは、PDF を固定サイズのチャンクで yield するジェネレーターを返します。Streaming/StreamingPdfWriter は、メモリバジェットを超えるドキュメントについて、呼び出し側が提供するストリームへ 1 ページずつ書き込みます。

Linearizer は、完成した PDF をリニアライズ済みレイアウトに書き換えます。最初のページを早い位置に配置するため、ビューアーは完全なダウンロードが完了する前にそのページを表示できます。shadowValidate() は、入力を変更せずに書き換えを検証します。

注意。 PdfWriter.phpLinearizer.php は、バイトオフセットとオブジェクトグラフに関わるクリティカルな箇所です(マニフェストの危険ゾーン)。オブジェクト番号付けや xref オフセット演算を、Writer ゴールデンスイートなしで変更しないでください。

クラス主なメソッド役割
PdfWriterwrite(DocumentData): string, writeChunked(DocumentData, int): Generator, setDeterministicMode(), setReproducibleClock(), setOutputColorProfile(), getLastXrefOffset(), getFileId()主要シリアライザー
PdfSerializationStrategy(インターフェイス)writeHeader(), getCatalogVersion(), writeXrefAndTrailer(), usesXrefStream()バージョンストラテジーのコントラクト
Pdf20StreamStrategywriteHeader()%PDF-2.0, getCatalogVersion()/2.0, usesXrefStream()truePDF 2.0 xref ストリームストラテジー
Pdf17TableStrategywriteHeader()%PDF-1.7、xref テーブルPDF 1.7 xref テーブルストラテジー
Pdf14TableStrategywriteHeader()%PDF-1.4、xref テーブルPDF 1.4 xref テーブルストラテジー
PdfOutputProfile(列挙型)Pdf20, Pdf17, Pdf14; headerVersion(), catalogVersion(), allowsObjectStreams()ターゲットバージョンセレクター
PdfXrefWritergenerateFileId(), finalizeTrailerAndXref()File ID + trailer/xref のファイナライズ
Linearizerlinearize(string): string, shadowValidate(string): arrayFast Web View 書き換え
Streaming\StreamingPdfWriteropen(), newPage(), close()シングルパスストリーミングライター

完全な PHPDoc テーブルを生成するには、composer docs:generate-api-php -- --module=Writer を実行します。

ソース:examples/02-pdf-factory.php

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Writer\PdfWriter;
$writer = new PdfWriter();
$pdfBytes = $writer->write($documentData);
file_put_contents('out.pdf', $pdfBytes);

デフォルトのプロファイルは PDF 2.0 です。出力は %PDF-2.0 で始まり、クロスリファレンスストリームで終わります。

この例では、バイト単位で同一の出力を得るために確定モードとクロックを固定し、その後、固定チャンクでストリーミングします。

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use DateTimeImmutable;
use NextPDF\Writer\PdfWriter;
use NextPDF\Writer\ReproducibleClock;
$pinned = new DateTimeImmutable('2026-01-01T00:00:00Z');
$writer = new PdfWriter();
$writer->setDeterministicMode($pinned, 'nextpdf-fixed-file-id');
$writer->setReproducibleClock(new ReproducibleClock($pinned));
$out = fopen('php://output', 'wb');
foreach ($writer->writeChunked($documentData, chunkSize: 65536) as $chunk) {
fwrite($out, $chunk);
}
fclose($out);
  • 1 回の write() 呼び出しごとに、1 つのストラテジーが実行されます。ライターは、呼び出しごとにプロファイルに基づいてストラテジーをリセットします。先行する呼び出しのバージョンが漏れることはありません。
  • アーカイブ適合モードは、要求されたプロファイルを上書きします。PDF/A-3 ビルドは PDF 1.7 を強制します。PDF/A-4 ビルドは PDF 2.0 を強制します。
  • バイト単位で同一の出力には、両方の固定が必要です。確定モード 再現可能クロックの両方を設定してください。一方だけを固定しても不十分です。
  • writeChunked() はジェネレーターを yield します。必ず最後まで消費してください。部分的な読み取りでは、切り詰められた無効な PDF が生成されます。
  • Linearizer は、クロスリファレンスオフセットを書き換えます。書き換えの失敗を許容できないパイプラインでは、最初に shadowValidate() を実行してください。
  • Pdf14TableStrategyfinal readonly です。PDF 1.4 パスでは、PDF 2.0 の機能を劣化させるのではなく、Pdf14FeatureGuard を通じて拒否します。

シリアライゼーションは、オブジェクト数と総バイトサイズに対して線形です。クロスリファレンスストリームは、オブジェクトテーブルに対するパスを 1 回追加します。writeChunked() は組み立てられたドキュメントを保持しますが、その内容を制限されたスライスで yield するため、ピークメモリはドキュメントサイズに 1 チャンクを加えたものになります。Streaming\StreamingPdfWriter はドキュメント全体を保持しません。メモリバジェットを超える入力向けのパスです。リファレンスワークロードのバジェットは、ウォールタイム 1500 ms とピーク 64 MB です。リニアライゼーションは、2 回目のフルパスと測定パスを追加します。その分を明示的にバジェットに含めてください。

ライターは、信頼済みのインメモリオブジェクトグラフをシリアライズします。主な脅威は、その入力側にあります。すべてのリテラル文字列は正規の PdfStringEscaper::escapeLiteral() を通過するため(ADR-015)、埋め込まれた制御バイトが文字列トークンから抜け出すことはできません。暗号化は、PdfEncryptionWriter/Encrypt トレーラーエントリーを通じて接続されます。公開鍵暗号化は、暗黙的にダウングレードされるのではなく、明示的な例外で拒否されます。確定的モードと再現可能クロックモードは、タイムスタンプと順序付けのサイドチャネルを出力から取り除きます。ドキュメントの脅威モデルと暗号化の信頼境界については、/modules/core/security/ を参照してください。

Writer は PDF 2.0 のファイル構造を生成します。具体的には、%PDF-2.0 ヘッダー、/2.0 カタログバージョン、クロスリファレンスストリーム、および ISO 32000-2 §7.3.4.2 のエスケープテーブルに従ったリテラル文字列のエスケープです。これらは実装上の事実です。その根拠は src/Writer/Pdf20StreamStrategy.phpsrc/Writer/PdfSerializationStrategy.php、および src/Writer/PdfWriter.php におけるストラテジー選択であり、これらは tests/Unit/Writer/(192 件のテスト。Pdf20StreamStrategyPdfXrefWriter、および Linearizer* スイートを含みます)と tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest ベースラインによって検証されます。

これは、完全な PDF 2.0 適合性の主張ではありません。完全な ISO 32000-2 適合性は、外部オラクルによって検証された、完全なドキュメントとしての特性であり、シリアライザー単体の特性ではありません。エンドツーエンドの適合性は、オラクルで確認された場合にのみ主張されます。tests/Integration/Accessibility/VeraPdfUa2GoldenTest は、生成されたフィクスチャを PDF/UA-2 について veraPDF で検証し、tests/Standards/Profile/PdfRConformanceTest は PDF/R プロファイルをカバーします。veraPDF ゴールデンテストは、ランナーに veraPDF バイナリが存在しない場合はスキップされるため、無条件のゲートではなく、オプトインのオラクルゲートです。これを実行するには、VERAPDF_BINARY を設定します。アーカイブプロファイルの選択(PDF/A-3 → PDF 1.7、PDF/A-4 → PDF 2.0)は、ADR-011 と適合モードによって決定され、/modules/core/conformance/ の適合スイートによって検証されます。これらのオラクルに裏付けられたプロファイル以外では、無条件の適合性を主張するのではなく、Writer は「PDF 2.0 構造を生成します。適合性は PDF/UA-2 プロファイルについて veraPDF によって検証されます」と記述してください。