コンテンツにスキップ

カスタムレイアウトエンジンとレイアウト時テキストのインターセプト

NextPDF は、差し替え可能なレイアウトエンジンインターフェイスを公開していません。公開されているレイアウト拡張コントラクトは TextPreprocessorInterface で、レイアウト時にテキストをフックします。さらに、コンテンツのライフサイクルイベントを利用して、レイアウトが生成する内容を監視できます。

Terminal window
composer require nextpdf/core:^3

レイアウトパイプラインは内部実装です。グリフレイアウト、フォントサブセット化、ToUnicode CMap 出力、構造ツリーを扱うものであり、NextPDF では差し替えられません。安定したバイト出力とタグ付き PDF への準拠は、単一の管理されたビルドに依存しています。

NextPDF が公開しているのは、レイアウトのに位置する TextPreprocessorInterface です。実装は生のテキストを受け取り、セグメント化された結果を返します。この処理は、テキストがグリフレイアウト、フォントサブセット化、ToUnicode CMap、構造ツリーに渡される前に行われます。これは、レイアウトエンジンに手を加えずにテキストコンテンツを変更するための、サポートされた方法です。

このコントラクトには、ソースの PHPDoc で厳格なルールが定められています。実装はレイアウトの動作を変更してはなりません。改行、復帰、タブなど、レイアウトに影響する文字を追加してはならず、論理的な読み上げ順序を維持する必要があります。プリプロセッサはコンテンツの置き換えを宣言するものであり、レイアウトの選択は行いません。このルールを守ってください。守らない場合、安定した出力とアクセシビリティが損なわれます。

レイアウト結果を変更せずに監視するには、アクショントリガーとイベントリスナーのコンテンツライフサイクルイベントを使用します。ContentRenderedEvent は、コンテンツがページに描画された後に発火します。FontLoadedEvent は、フォントファミリーとスタイルごとに 1 回発火します。

NextPDF\Contracts\TextPreprocessorInterface(stable、1.9.0 以降):

メソッド戻り値目的
process(string $text)TextPreprocessResultレンダリングパイプラインの前で生のテキストを変換し、墨消しメタデータを含むセグメント化された結果を返します。

返される NextPDF\Contracts\TextPreprocessResult は、凍結された値オブジェクトです。そのコンストラクターのシグネチャと public プロパティは安定しており、マイナーリリースやパッチリリースでは変更されません。新しいメソッドが追加される場合はあります。

この小さなプリプロセッサは、固定トークンをマスクします。レイアウトに影響する文字は追加せず、読み上げ順序を維持します。

<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Contracts\TextPreprocessResult;
use NextPDF\Contracts\TextSegment;
final class TokenMaskingPreprocessor implements TextPreprocessorInterface
{
public function process(string $text): TextPreprocessResult
{
$masked = \str_replace('SECRET-TOKEN', '••••••••••••', $text);
return new TextPreprocessResult([
new TextSegment($masked, redacted: $masked !== $text),
]);
}
}

本番用のプリプロセッサでは、マッチングルールを 1 か所にまとめます。不正なパターンに対してはフェイルクローズし、元のテキストを決してログに記録しません。

<?php
declare(strict_types=1);
use NextPDF\Contracts\TextPreprocessorInterface;
use NextPDF\Contracts\TextPreprocessResult;
use NextPDF\Contracts\TextSegment;
use Psr\Log\LoggerInterface;
final class PatternRedactionPreprocessor implements TextPreprocessorInterface
{
/**
* @param non-empty-string $pattern A valid PCRE pattern for sensitive spans
*/
public function __construct(
private readonly string $pattern,
private readonly LoggerInterface $logger,
) {}
public function process(string $text): TextPreprocessResult
{
$result = \preg_replace($this->pattern, '[REDACTED]', $text);
if ($result === null) {
// Fail closed: never emit unredacted text on a pattern error.
$this->logger->error('Redaction pattern failed; substituting empty text');
return new TextPreprocessResult([new TextSegment('', redacted: true)]);
}
return new TextPreprocessResult([
new TextSegment($result, redacted: $result !== $text),
]);
}
}
  • レイアウトの差し替えは不可。 ボックスレイアウト、改行処理、ページネーションを差し替えるコントラクトはありません。サードパーティ製のレイアウトエンジンを組み込む要望は、設計上スコープ外です。
  • ルールの遵守。 \n\r\tprocess() 内で追加すると、レイアウトが破損し、安定した出力が損なわれます。エンジンはこのルールを信頼します。出力にレイアウトに影響する文字が含まれていないかどうかの再チェックは行いません。
  • 読み上げ順序。 セグメントの順序を変更すると、タグ付き PDF の読み上げ順序と PDF/UA への準拠が損なわれます。
  • 単一の責務。 プリプロセッサはコンテンツの置き換えを宣言します。監視にはライフサイクルイベントを使用し、process() を通じて副作用を発生させないでください。

process() は、レイアウトのホットパス上でテキストランごとに 1 回実行されます。メモリ使用量は軽く保ってください。パターンは呼び出しごとではなく、コンストラクター内で一度だけコンパイルしてください。コンテンツのライフサイクルイベントは、リスナーがバインドされていない場合にはコストがかかりません。

TextPreprocessorInterface は、機密コンテンツがコンテンツストリーム、フォントサブセット、メタデータに到達するに除去するための、サポートされた場所です。サブセット化と ToUnicode CMap の前に実行されるため、墨消しされたグリフがファイルに入ることはありません。プリプロセッサの失敗はフェイルクローズとして扱い、元のテキストを出力するのではなく、空のテキストまたはマスクされたテキストを使用してください。

このページには、規範的な署名やアーカイブに関する主張は適用されません。読み上げ順序のルールにより、このコントラクトはタグ付き PDF のニーズに合致します。タグレベルの準拠については、アクセシビリティリファレンスで扱っています。

NextPDF Pro は、一般的なドキュメントタイプ向けに調整された PII の墨消しを含む、本番用のテキスト前処理戦略を提供します。Core では TextPreprocessorInterface を自分で記述するか、同じ公開コントラクトを通じて検証済みの有償エディションのビルドを使用します。

用語集ではテキストプリプロセッサ拡張ポイントを定義しています。それぞれの正式な定義については、公開されている用語集を参照してください。