技術記事

DelphiのHotPDFでPDF 2.0、PDF/A-4、PDF/UA-2を出力

HotPDFはDelphiとC++BuilderからネイティブPDF 2.0ドキュメントを書き出す。3つのPDF/A-4アーカイブプロファイルと、名前空間付き構造要素を持つPDF/UA-2アクセシブル出力も含まれる。これらを選ぶのは2つのプロパティの設定にすぎないが、その背後にある規格は、バージョン番号が示す以上に変わっていた。PDF/A-4は、PDF/A-2で誰もが覚えた適合度文字を廃止し、PDF/UA-2はパート1のドキュメントが持たなかった構造名前空間を導入した

本稿では、生成されるファイルの中で実際に何が変わるのか、そしてHotPDFがどの間違いを顧客サイトでの検証失敗ではなくEndDocでの例外に変えるのかを扱う

PDF/A-4の識別がパート2やパート3とどう違うのか

PDF/A-4はパート番号と改訂年で自己識別し、基本パートに適合度文字を持たない。PDFACompliance'4'に設定すると、HotPDFはpdfaid:part=4pdfaid:rev=2020を書き出し、pdfaid:conformanceエントリはまったく置かない。文字が行方不明になったのではない。パート4にはA/B/Uのレベルがなく、かつてそれらを分けていた要件が基本パートに折り込まれたからだ

2つの拡張だけが文字を保つ。'4E'はエンジニアリングドキュメント向けのPDF/A-4eを選び、適合度Eを書き出す。これは他のプロファイルが禁じる3DやRichMedia注釈のパスを許可する。'4F'はPDF/A-4fを選び、適合度Fを書き出す。これは任意フォーマットの埋め込みファイルを許可する。3つすべてがPDF 2.0ヘッダを強制し、通常のPDF/A出力インテントとメタデータ検査を要求し、暗号化を禁じる。暗号化されたアーカイブファイルは、規格が想定しない矛盾だ

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-archive.pdf';
    Pdf.PDFACompliance := '4F';   // PDF/A-4f: associated files of any format
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 760, 0, 'Invoice 2026-0731');
    Pdf.AddPDFAssociatedFile('invoice.xml', 'text/xml',
      'Structured invoice data', 'Data', LoadInvoiceBytes);
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

AddPDFAssociatedFileはファイルを埋め込み、/AFRelationshipを持つFileSpecを構築し、Catalogの/AF配列とEmbeddedFiles名前ツリーの両方に登録する。両方の登録は必須であり、片方にだけリストされたファイルは、ハイブリッド請求書が手軽な目視検査を通過し本物のバリデータで落ちる、もっともな単一の理由だ。関係性文字列はSourceDataAlternativeSupplementUnspecifiedのいずれかを受け入れる。そしてアクティブなプロファイルはPDF/A-3、PDF/A-4e、PDF/A-4fのいずれかでなければならない。基本パート4プロファイルは関連ファイルを許可しない。古いAddPDFA3AssociatedFileという名前も、既存コードのために引き続き機能する

PDF/UA-2がPDF/UA-1に求めた、PDF/UA-1が求めなかったもの

PDF/UA-2はPDF 2.0を強制し、pdfuaid:part=2pdfuaid:rev=2024を書き出し、構造ツリーに名前空間を導入する。パート1のドキュメントは標準ロールの平坦な1つの語彙しか持たなかった。パート2のドキュメントは、各ロールが宣言された名前空間に属す限り、カスタムロールを持てる。これこそ、ドメイン固有のタグ付けを、推測ではなく支援技術にとって読み解けるものにする仕組みだ

2つのメソッドがこれを実装する。RegisterStructureNamespaceは、間接/Type /Namespace辞書を作成または再利用し、StructTreeRoot /Namespacesにリストし、再利用できるようその辞書を返す。AddStructureElementNSは、その辞書を指す/NSエントリを持つ構造要素を作成する。これが、標準セットの外のロール名を許可する。同じURIでの繰り返し呼び出しは、重複を積み上げるのではなく、1つの辞書を再利用する

var
  Root: THPDFDictionaryObject;
begin
  Pdf.PDFUACompliance := True;
  Pdf.PDFUAPart := 2;             // part 2 forces PDF 2.0
  Pdf.Lang := 'en-US';
  Pdf.BeginDoc;
  Root := Pdf.AddStructureElement('Document', nil);
  Pdf.AddStructureElementNS('WidgetGroup',
    'https://example.com/ns/widgets', Root);
  Pdf.EndDoc;
end;

ここでのLangは飾りではない。自然言語が宣言されていないタグ付きドキュメントは、スクリーンリーダーに発音の推測を強いる。そしてPDF/UAは、その省略を好みではなく欠陥として扱う

EndDocが捕捉するのはどの構造エラーか

4つであり、それぞれがそうでなければ壊れた状態でバリデータに届くドキュメントに対応する。構造ルートは、ただ1つのトップレベルDocument要素を含まなければならない。すべての名前空間辞書は間接でなければならず、Namespaceと型付けされ、空でない一意のURIを持たなければならない。すべての構造要素の/NS参照は、ルートの/Namespaces配列に実際にリストされた辞書を指さなければならない。そして、名前空間を持たないロールは、PDF 2.0の標準ロールかRoleMapを経由して解決されなければならない

これらがEndDocで発火するのは、それがツリー全体がメモリ上に存在する最後の瞬間であり、同時に完成する最初の瞬間だからだ。もっと早く捕捉すれば有効な中間状態を拒否することになり、もっと遅く捕捉すればまったく捕捉できない。あなたのコードにとっての実地の帰結は、構造のバグが生成の終わりで、問題を名指しするメッセージとともに表面化するということだ。何週間もあとに誰かが顧客から転送してくるveraPDFレポートとしてではなく

知っておく価値のあるPDF 2.0のロール

型付きロール列挙はDocumentFragmentAsideTitleFENoteSubEmStrongArtifactを得る。このうち3つが、日常のビジネスドキュメントのタグ付けを変える。Asideは、サイドバーやプルクオートに、誤用されたSectではない本来の居場所をようやく与える。FENoteは脚注や文末注をそのものとしてマークし、リーダーが本文と混ぜてインターリーブする代わりに、それらを提示できるようにする。EmStrongは、強調をスパンレベルの書式としてタグ付けすることから来るセマンティックな推測を置き換える

文字列のオーバーロードは加えて、開放的なHn形式を、H7やその先も含めて受け入れる。PDF 1.7はH6で止まり、深い技術ドキュメントはアウトラインを平坦にするかレベルを使い回すしかなかった。規格書、法典、部品カタログを生成するなら、これだけが出力をPDF 2.0に移す理由になり得る

本番出力を切り替える前に確認すべきこと

PDF 2.0は、長い尻尾を引き連れるヘッダの変更だ。古いアーカイブ取り込みツール、一部のプリントRIP、そして驚くほど多くの基幹業務ビューアはPDF 1.7までしか受け入れず、あなたが何か間違えたからではなくヘッダで落ちる。切り替える前に消費側システムを確認すること。そしてPDF/A-4プロファイルを選ぶと、頼んだかどうかにかかわらずPDF 2.0を選ぶことになる点を忘れないで

安全な順序はこうだ。未知の読者に outward に出すドキュメントにはPDF/A-3を保ち、インジェストを自分が制御する社内アーカイブにはPDF/A-4fを使い、アクセシビリティポリシーが名指しする場所でのみPDF/UA-2を採用する。まずアーカイブ側から取り組むなら、PDF/A、PDF/X、PDF/UAの検証PDF/A-3上のZUGFeRDとFactur-Xハイブリッド請求書のガイドが、バージョン番号の前に来るプロファイル選択を扱っている。自動プレフライトレポートのノートは、評定を手作業ではなくビルドの一部にする方法を示している

HotPDFはPDF 2.0オーサリング面全体を、DelphiとC++Builder向けのネイティブVCLコードとして出荷する。だからPDF/A-4とPDF/UA-2の出力に外部エンジンも再頒布可能ファイルも不要だ。HotPDFコンポーネントページにサポートされるプロファイルとRAD Studioバージョンの一覧がある