コンテンツにスキップ

ContentStream: PDF コンテンツストリームエミッター

ContentStream モジュールは、マークコンテンツオペレーターを出力します。構造タグとアーティファクトを開閉し、ネストの深さを追跡して、オペレーターバッファーを返します。

Terminal window
composer require nextpdf/core:^3

ContentStreamBuilder は、このモジュールで唯一のクラスです。ページコンテンツストリームのマークコンテンツレイヤーを構築します。コンテンツストリームは、ページコンテンツをオペレーターのシーケンスとしてエンコードします(ISO 32000-2 §8)。ビルダーは、そのコンテンツを囲むマークコンテンツオペレーターを出力します。

append() は、生のオペレーターバイトをそのまま追加します。ビルダーはこの入力をエスケープしません。その妥当性は呼び出し側の責任です。ここが、HTML パイプラインと Graphics モジュールがそれぞれのオペレーターを差し込むポイントです。

beginTag() は、構造タグの付いたシーケンスを開きます。BDC オペレーターを、MCID プロパティリストとともに出力します(ISO 32000-2 §14.6)。endTag() は、対応する EMC オペレーターを出力します。ビルダーはネストの深さをカウントします。開いているシーケンスがない状態で endTag() を呼び出すと、PageLayoutException をスローし、不均衡な EMC は書き込みません。

beginArtifact() は、アーティファクトシーケンスを開きます。アーティファクトは、構造ツリーの外に置かれなければならないページネーション装飾(ヘッダー、フッター、ページ番号、罫線)を保持します(ISO 32000-2 §14.8.2.2)。サブタイプは、4 つの ISO 値(PaginationLayoutPageBackground)のいずれかです。型付きの ArtifactSubtype 列挙型を使用することを推奨します。文字列のオーバーロードは列挙型に対して検証されるため、非標準の値はただちに失敗します。

relabelTag() は、すでに出力されたタグをその場で書き換えます。finish() は、バッファー全体を返し、マークコンテンツが不均衡な場合はスローします。drain() は、インクリメンタルストリーミング向けに、均衡チェックなしでこれまでのバッファーを返します。peek() は、バッファーを消費せずに返します。reset() は、状態をクリアします。

メソッドシグネチャー役割
append()append(string $raw): void生のオペレーターバイトの追加(エスケープなし)
beginTag()beginTag(string $structType, int $mcid): voidBDC 構造シーケンスの開始
endTag()endTag(): void最も内側のシーケンスの EMC による終了
beginArtifact()beginArtifact(ArtifactSubtype|string $type): voidアーティファクトシーケンスの開始
endArtifact()endArtifact(): void最も内側のアーティファクトの終了
getMarkedContentDepth()getMarkedContentDepth(): int現在のネストの深さの取得
relabelTag()relabelTag(string $old, string $new, int $mcid): void出力済みタグのその場での書き換え
finish()finish(): stringバッファー全体の取得。不均衡な場合はスロー
drain()drain(): string均衡チェックなしのバッファー取得
peek()peek(): stringバッファーを消費しない取得
reset()reset(): voidすべての状態のクリア

完全な PHPDoc テーブルを取得するには、composer docs:generate-api-php -- --module=ContentStream を実行してください。

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('P', mcid: 0);
$builder->append("BT /F1 12 Tf 72 720 Td (Hello) Tj ET\n");
$builder->endTag();
$pageContent = $builder->finish();

この例では、段落を構造タグで、フッターをアーティファクトで囲みます。drain() を使ってバッファーをインクリメンタルにストリーミングします。

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Accessibility\ArtifactSubtype;
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('H1', mcid: 0);
$builder->append($titleOperators);
$builder->endTag();
$builder->beginArtifact(ArtifactSubtype::Pagination);
$builder->append($footerOperators);
$builder->endArtifact();
if ($builder->getMarkedContentDepth() !== 0) {
throw new RuntimeException('Unbalanced marked content before flush.');
}
$chunk = $builder->drain();
  • append() は入力をエスケープしません。有効なオペレーターバイトのみを渡してください。ビルダーは呼び出し側を信頼します。
  • endTag()endArtifact() は、アンダーフロー時にスローします。開いていないシーケンスは決して閉じないでください。
  • finish() は均衡をチェックし、深さがゼロでない場合はスローします。drain() はチェックしません。drain() はインクリメンタルストリーミングの場合にのみ使用してください。
  • 深さカウンターは、タグとアーティファクトを区別しません。EMC は、どちらの種類でも最も内側のシーケンスを閉じます。厳密な順序でネストしてください。
  • 文字列を渡す beginArtifact() のオーバーロードは、列挙型に対して検証されます。非標準のサブタイプは、出力時ではなく呼び出し時に失敗します。
  • relabelTag() は、出力済みのタグを書き換えます。出力時に使用したものと同じ mcid を使用してください。

すべての操作は O(1) の文字列追加であり、relabelTag() の場合は O(buffer) の書き換えです。モジュールは、1 つの文字列バッファーと 1 つの整数の深さカウンターを保持します。パース処理は行わず、バッファー以外のアロケーションもありません。リファレンスワークロードの予算は、実時間 1500 ms、ピーク 64 MB です。このモジュールはその予算を大きく下回ります。

append() は信頼境界です。ビルダーはバイトをそのまま書き込むため、上流のコードは、リテラル文字列オペレーターに到達するすべての文字列をエスケープする必要があります。正規のエスケーパーは PdfStringEscaper::escapeLiteral()(ADR-015)です。エスケープしていないユーザーテキストを append() に決して渡さないでください。endTag()endArtifact()finish() での均衡チェックにより、不正な形式のマークコンテンツツリーが Writer に到達するのを防ぎます。ドキュメントの脅威モデルについては、/modules/core/security/ を参照してください。

このモジュールは、ISO 32000-2 と整合するマークコンテンツオペレーター構造を 出力します。具体的には、BDC/EMC のペア(§14.6 に従い MCID プロパティリストを伴う)と、§14.8.2.2 に従うアーティファクトシーケンスです。これらは実装上の事実です。その根拠は、src/ContentStream/ContentStreamBuilder.phpsrc/Accessibility/ArtifactSubtype.php 列挙型、および tests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTestContentStreamBuilderRelabelTagInvariantTest です。これらは、エンドツーエンドの PDF/UA-2 または PDF 2.0 準拠を主張するものではありません。これらのオペレーターが構成するタグ付き PDF 構造は、外部のオラクルによって検証されます。tests/Integration/Accessibility/VeraPdfUa2GoldenTest が、生成されたフィクスチャを PDF/UA-2 プロファイルについて veraPDF に対してチェックします。このオラクルテストは veraPDF バイナリが存在しない場合はスキップされる ため、オプトインのゲートです。無条件の準拠を主張するのではなく、このモジュールは「マークコンテンツ構造を生成し、PDF/UA-2 準拠は veraPDF によって検証される」と記述してください。