アクセシビリティ:タグ付けプリミティブと PDF/UA-2 構造モデル
NextPDF Core は、アクセシブルなオーサリングを支援するプリミティブとして、論理構造ツリー、標準ロールマッピング、マーク付きコンテンツのタグ付け、および ISO 14289-2 (PDF/UA-2)と ISO 32000-2 §14.7 で定義された構造ツリーモデルに準拠した BCP-47 言語属性を提供します。生成されたファイルの適合性は、最終的なドキュメント、作成者によるコンテンツの選択、および外部チェッカーによって決まります。これは、ライブラリがユーザーに代わって保証するものではありません。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core概念の概要
「概念の概要」という見出しのセクションタグ付き PDF には、ルートに単一の Document 構造要素を保持する論理構造ツリーがあります。支援技術はこのツリーを読み取り、視覚レイアウトとは独立した、意味のある読み上げ順序を導出します(ISO 32000-2 §14.7.2、ISO 14289-2 §8.2.5.2)。NextPDF では、NextPDF\Accessibility 名前空間内で連携する 3 つの型によってこのモデルを表します。
StructureTree は階層を所有し、ページごとにマーク付きコンテンツ識別子を割り当て、親子のネストを追跡します。ISO 32000-2 §14.7 に従って、構造ツリーのルート、構造要素、親ツリー、ロールマップ、および PDF 2.0 標準構造名前空間をシリアライズします。createRoot() は、必須の単一の Document 要素を言語属性付きで生成します。addElement() は型付き子要素をアタッチします。hasRoot() と rootHasChildren() は、ツリーが存在するかどうか、および子孫を持つかどうかを返します。
StructureElement は、1 つの構造要素辞書を表す値オブジェクトです。標準構造タイプ(H1 から H6、P、L、LI、Table、Figure、Link といった表 368 の名前)、マーク付きコンテンツ識別子エントリ、および代替テキスト、置換テキスト、タイトル、言語向けのオプションのアクセシビリティ属性を取り込みます。1 つの要素が複数のページにまたがる場合があり、ページごとに 1 つの識別子エントリを蓄積することで、kids 配列はページ境界をまたぐマーク付きコンテンツを参照します。
TaggedContentEmitter は、HTML パイプラインと構造ツリーを橋渡しします。Document::enableTaggedPdf() が有効な場合、HTML レンダラーはエミッターを接続し、ブロックレベル要素ごとに、対応する構造要素ノードと対になったマーク付きコンテンツ演算子を生成します。HtmlToStructureMap は、HTML タグから PDF 構造タイプへのテーブル駆動のマッピングを提供します(ISO 14289-2 §8)。エミッターは、HTML のヘッダーおよびフッター領域のような装飾的なランニングコンテンツをアーティファクトにルーティングし、読み上げ順序から除外します。
言語タグ付けは Bcp47Validator によって検証されます(RFC 5646)。整形式性の構文チェックと、レジストリに基づく妥当性チェックを提供します。厳格モード(ConformancePolicy::strictUa2())は、形式不正のタグを書き込み時に黙って破棄するのではなく、API 境界で拒否します。これは、カタログの言語エントリが特定の言語に解決されるという ISO 14289-2 §8.4.4 の要件と一致します。
API サーフェス
「API サーフェス」という見出しのセクション| シンボル | 種類 | 概要 |
|---|---|---|
Document::enableTaggedPdf(string $lang = 'en', ?ConformancePolicy $policy = null): static | メソッド | 構造ツリーと HTML ブリッジを有効にし、mark-info とカタログの言語エントリを設定します。 |
Document::setLanguage(string $lang): static | メソッド | ドキュメントレベルの自然言語を設定します (BCP-47)。 |
Document::isTaggedPdfEnabled(): bool | メソッド | アクティブな適合モードが構造タグ付けを必須とするかどうか。 |
StructureTree::createRoot(string $lang = 'en'): int | メソッド | 必須の単一の Document ルート要素を作成します。 |
StructureTree::addElement(int $parentIndex, string $type, int $pageIndex, ...): int | メソッド | 型付きの子構造要素をアタッチします。 |
StructureTree::hasRoot(): bool と rootHasChildren(): bool | メソッド | ツリーが存在するかどうか、および子孫を持つかどうかを確認します。 |
StructureElement | final クラス | 1 つの構造要素を表す値オブジェクト (代替テキスト、置換テキスト、タイトル、言語、識別子)。 |
RoleMap::standard(): array<string,string> | static | 標準構造タイプの語彙(ISO 32000-2 表 368 に PDF 2.0 のタイプを加えたもの)。 |
Bcp47Validator::isWellFormed/isValid/validate/normalise | メソッド | RFC 5646 の構文的およびレジストリに基づく言語タグ検証。 |
AccessibilityAutoFixerRegistry | final クラス | ヒューリスティックな構造修正のための、オプトイン式 PSR-11 スタイルレジストリ。 |
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクション<?php
declare(strict_types=1);
use NextPDF\Core\Document;
$doc = Document::createStandalone();
// The BCP-47 tag drives the catalog language entry and the// structure-tree root language attribute.$doc->enableTaggedPdf(lang: 'en');$doc->setTitle('Tagged accessibility demo');$doc->addPage();
// Semantic HTML maps to structure elements: h1 to /H1, p to /P,// ul and li to /L plus /LI. Text runs are wrapped in// marked-content operators with stable identifiers.$doc->writeHtml('<h1>Document title</h1><p>Body paragraph.</p>');
$doc->save(__DIR__ . '/output/tagged.pdf');コードサンプル — 本番環境
「コードサンプル — 本番環境」という見出しのセクション<?php
declare(strict_types=1);
use NextPDF\Conformance\ConformancePolicy;use NextPDF\Core\Document;use NextPDF\Exception\InvalidConfigException;use Psr\Log\LoggerInterface;
final class AccessibleReportWriter{ public function __construct(private readonly LoggerInterface $logger) { }
public function render(string $html, string $bcp47Lang, string $outPath): void { $doc = Document::createStandalone();
try { // strictUa2() rejects malformed BCP-47 tags at the API // boundary (ISO 14289-2 §8.4.4) instead of dropping silently. $doc->enableTaggedPdf($bcp47Lang, ConformancePolicy::strictUa2()); } catch (InvalidConfigException $e) { $this->logger->error('Rejected language tag for tagged PDF', [ 'lang' => $bcp47Lang, 'reason' => $e->getMessage(), ]);
throw $e; }
$doc->setTitle('Quarterly accessibility report') ->setLanguage($bcp47Lang) ->addPage();
$doc->writeHtml($html);
// The engine emits a Degraded / ComplianceRisk advisory directing // the caller to validate externally; surface it to operators // rather than treating tagged output as certified. foreach ($doc->getWarnings() as $warning) { $this->logger->warning('Tagged-PDF advisory', [ 'code' => $warning->code->value, 'message' => $warning->message, ]); }
$doc->save($outPath); }}エッジケースと注意点
「エッジケースと注意点」という見出しのセクション- 呼び出し順序。
enableTaggedPdf()はwriteHtml()より前に呼び出してください。HTML パイプラインはパーサー構築時に適合モードを確認するため、すでにレンダリング済みのコンテンツにエミッターが遡及的に接続されることはありません。 - 空の構造ツリー。
enableTaggedPdf()が呼び出されていても、アタッチされた構造に子孫がないドキュメントは、メタデータで PDF/UA-2 を表明しません。公開時のゲート条件はrootHasChildren()であり、hasRoot()ではありません。空の構造ツリーで PDF/UA-2 を表明するファイルは、バリデーターによって拒否されるためです(ISO 14289-2 §5、EmptyTaggedPdfDoesNotAdvertisePdfUa2Testによって検証済み)。 - 適合モードの折りたたみ。 同じドキュメントで
enablePdfA()とenableTaggedPdf()を呼び出すと、単一値の適合性ディスクリミネーターが last-wins (最後の呼び出しが優先)に折りたたまれます。副作用(構造ツリー、mark-info)は加算的なまま維持され、CONFORMANCE_MODE_CLOBBERED警告が発行されるため、この折りたたみを検出できます。 - 自動修正は自動ではありません。 組み込みの修正処理(
EmptyTagStripper、LegacyLangNormaliser、RootLangFallback)はNextPDF\Accessibility\AutoFixer\*の下で提供されますが、自動登録されることはありません。利用者が、これらをAccessibilityAutoFixerRegistryに明示的に登録する必要があります。
既知の制限事項
「既知の制限事項」という見出しのセクションNextPDF は PDF/UA-2 構造ツリーモデルに準拠した構造を出力しますが、推論できないセマンティクスを自動生成しません。以下は作成者が提供するマークアップまたは属性を必要とし、自動生成されません。
- 画像やその他の非テキストコンテンツの代替テキスト。
- HTML マークアップで表現された範囲を超える、テーブルヘッダーのスコープおよびヘッダーとセルの関連付け。
- 表示されるリンクテキストが自己説明的でない場合に、リンクの目的を示すテキスト。
- リストマークアップなしに視覚的にリストとしてレイアウトされたコンテンツのリストセマンティクス。
- ソースの順序が意図した読み上げ順序と異なる場合の、修正済みの読み上げ順序。
- 曖昧なコンテンツについて、装飾的か意味を持つかの分類。
このライブラリはエンドツーエンドの PDF/UA-2 検証を実行しません。ランタイム自体が Degraded / ComplianceRisk のアドバイザリ(PDFUA2_FOUNDATIONAL)を発行し、本番環境での承認に先立って外部チェッカーで出力を検証するよう、呼び出し元に指示します。PDF/UA チェッカー(例:veraPDF)で検証してください。NextPDF はユーザーに代わって適合性を表明することはありません。最終的なドキュメントの適合性は、API の呼び出しではなく、オーサリングの選択とバリデーターによって決まります。
パフォーマンス
「パフォーマンス」という見出しのセクション構造ツリーの構築は、構造要素の数に対して線形です。識別子の割り当ては、マーク付きコンテンツシーケンスごとに償却定数時間です。シリアライズは、要素セット全体に対する単一の線形パスです。HTML 駆動型タグ付けにおける支配的なコストは、タグの出力ではなく HTML パイプライン自体です。performance_budget で宣言されているレシピごとの上限(実時間 1500 ms、ピーク 64 MB)は、典型的な複数ページのセマンティックドキュメントに適用されます。大きなドキュメントは、ページ数ではなく要素数に対して線形にスケールします。
セキュリティに関する注意事項
「セキュリティに関する注意事項」という見出しのセクション言語タグとアクセシビリティ属性は、PDF の名前オブジェクトおよび文字列オブジェクトに書き込まれます。NextPDF はこれらを PdfStringEscaper を通じてエスケープするため、不正な形式または悪意のある言語、代替テキスト、置換テキスト、タイトルの値が PDF オブジェクトのコンテキストから抜け出すことはできません。厳格モードはさらに、未登録の BCP-47 タグを API 境界で拒否し、ライターに到達する前に入力サーフェスを狭めます。アクセシビリティ属性には、作成者が提供する自由テキストが含まれる場合があります。これらは信頼できない出力として扱い、他のドキュメントコンテンツと同じレビューを適用してください。プロファイルチェッカーの動作については、適合性モジュールを参照してください。
このページでは、ライブラリの動作を条項識別子に対応付けます。これは、ユーザーの出力が適合していることを表明するものではありません。引用された条項は要約・言い換えであり、原文のまま引用することはありません。条項レベルの表と明示的な非対応範囲については、PDF/UA-2 仕様マッピングを参照してください。引用チャンクのハッシュは docs/public/modules/core/_normative-evidence-a11y.md に記録されています。