PDFium Componentは、ISO 19005-1 Annex Cの実装上限——127バイトの名前トークン、8191の配列要素、4095の辞書エントリ、28レベルのコンテナネスト——を検証し、/Encodingエントリを持つシンボリックTrueTypeフォントを報告する。両方の検査はバイトスキャンパスで走るので、DelphiやLazarusのアプリケーションは、PDFiumのDLLをまったく読み込まずに評定を得る
これらは、ドキュメントが一見問題なく見えるだけに、人を最も当惑させる失敗だ。レンダリングも印刷もでき、すべてのフォントは埋め込まれ、出力インテントも存在する。それでもバリデータは、4096のエントリを持つ辞書を理由にファイルを拒否し、目に見えるドキュメントの中には理由を説明するものが何もない
Annex Cの上限が実際に何を守っているのか
あなたのジェネレーターより前の実装との相互運用性だ。Annex CはPDFリファレンスの実装上限をPDF/Aのすべてのパートに持ち越し、その数値は恣意的ではない。適合リーダーが歴史的に扱うことを要求されたものを記述しているからだ。これらを超えるファイルは、現代のビューアでは完璧に開けるかもしれないが、記録システムが15年前に標準化したアーカイブリーダーでは失敗する。それこそ、PDF/Aが防ぐために存在するシナリオだ
4つの上限は包含的だ。ちょうど127バイトの名前トークンは検証を通る。128は通らない。ちょうど8191要素の配列は検証を通る。8192は通らない。PDFium Componentはその理由で、すべての境界の両側をテストスイートでピン留めする。上限検査のオフバイワンは、最悪の種類のバリデータを生むからだ。適合ファイルを拒否しながら、それでも信じられるバリデータを
uses FPdfPdfa;
var
Src: TFileStream;
Res: TPdfAValidationResult;
begin
Src := TFileStream.Create('archive.pdf', fmOpenRead or fmShareDenyWrite);
try
Res := ValidatePdfACompliance(Src);
if pvaiArrayOverLimit in Res.Issues then
Memo1.Lines.Add('An array carries more than 8191 elements');
if pvaiDictOverLimit in Res.Issues then
Memo1.Lines.Add('A dictionary carries more than 4095 entries');
if pvaiNestingOverLimit in Res.Issues then
Memo1.Lines.Add('Containers nest deeper than 28 levels');
if pvaiNameOverLimit in Res.Issues then
Memo1.Lines.Add('A name token is longer than 127 bytes');
finally
Src.Free;
end;
end;
どのジェネレーターが実際にこれらの上限に当たるのか
プログラムで構造を構築するものであり、それは基幹業務の出力のほとんどだ。数千のフィールドを持つフォームは、8191を超える/Annots配列やAcroFormの/Fields配列を生む。生成された画像やフォントのインスタンスごとに1エントリが蓄積するページのリソース辞書は、4095を超える。深く生成された構造ツリー——ネストしたデータモデルの再帰で構築されたタグ付きドキュメント——は、誰もネスト深さを見ないために、気づかれずに28レベルを超える
長い名前は異なる習慣から来る。データを名前トークンにエンコードする習慣だ。顧客識別子から作られた色材名、完全なファイルパスを名前に持つオプションコンテンツグループ、6レベルの階層を連結した完全修飾名を持つフォームフィールド。名前は生成が安く、長く作るのも簡単であり、UTF-8でエンコードされたラベルが絡めば、127バイトは思ったより早く消える
すべてのケースで修復は構造的だ。配列を分け、辞書を分け、ネストを平坦化し、名前を短くする。各問題に対するプレフライトの推奨は、ファイルが無効だと告げるのではなく、具体的な上限を名指す。マーカー注入はここでは助けにならない。これらはメタデータの主張ではなく、オブジェクトグラフの形だからだ
なぜシンボリックTrueTypeフォントが/Encodingを持ってはならないのか
ISO 19005-1 §6.3.7が、シンボリックTrueTypeフォントに対してはフォント組み込みのcmapだけを認め、/Encodingエントリがそれと矛盾するからだ。シンボリックフォントは、コードをグリフに独自の基準で写像する。それこそがシンボリックの意味だ。エンコーディングテーブルを追加すれば、「バイト0x41はどのグリフを選ぶか」という問いに2つの答えができ、どちらが勝つかを示すルールがファイルの中にない。リーダーによって解決が異なり、あるビューアではテキストとして描かれるドキュメントが、別のビューアではディングバットとして描かれる
PDFium Componentは/FontDescriptorからシンボリックフラグを読む。その記述子がフォント辞書にインラインで書かれていても、間接参照されていても関係ない。非シンボリックTrueTypeフォントは、必要な/WinAnsiEncodingや/MacRomanEncodingをフラグ付けされずに保つ。非シンボリックフォントにとって、エンコーディングはまさに標準が求めるものだからだ。検査はエンコーディングの存在ではなく、矛盾で発火する
if pvaiSymbolicTrueTypeEncoding in Res.Issues then
Memo1.Lines.Add(
'A symbolic TrueType font carries /Encoding; PDF/A admits only its ' +
'built-in cmap (ISO 19005-1 6.3.7)');
この欠陥の実地の源は、すべてのTrueTypeフォントを同じように扱うプロデューサーによるフォントサブセット化だ。シンボル、Wingdings、バーコードフォント、アイコンフォントがよくある運び手だ。まさにビジネスドキュメントがチェックボックス、ロゴ、バーコードに使うフォントであり、まさにドキュメントが「フォント」のせいで検証に落ちたとき誰も再検査しないフォントだ
問題がプレフライトレポートにどう届くか
4つのコンテナ上限は構造に分類され、シンボリックTrueTypeエンコーディングの問題はコンテンツに分類される。この分割は、レポートが2人の異なる人に行くときに意味を持つ。構造の発見は通常ジェネレーターを書いた人に属し、コンテンツの発見は通常アセットを供給した人に属する
各問題は、救済を具体的な言葉で名指す推奨を運ぶ。名前トークンを127バイト以下に短くし、配列をどれも8191要素を超えないように分け、シンボリックTrueTypeフォントから/Encodingを削除する。「PDF/Aに適合しない」と言うレポートは調査を始めさせる。「どの上限を、どれだけ超えたか」を言うレポートは調査を終わらせる
DLLなしで検証する、そしてそれがここで意味を持つ理由
上記の検査はすべてファイルバイトに対して走る。だから、PDFiumバイナリをデプロイしていないサービスでも、ビルドステップでも、ネイティブDLLの読み込みがポリシー上の問題であるマシンでも動く。これはPDFium Componentの意図的な設計線だ。構造から答えられる検査は構造から答え、DLLはレンダリングエンジンを本当に必要とする検査のために予約される
周辺のワークフロー、つまりフォルダにわたって検証を走らせ、レポートを生成し、発見をどうするかを決定することについては、DelphiでのPDF/Aプレフライト検証とバッチプレフライトレポートCLIの解説を参照のこと。これらすべての検査の上に乗るアーカイブプロファイルの選択については、PDF/Aアーカイブコンプライアンスのノートが、発見の修正を始める前にどのパートとレベルを狙うかを扱う
PDFium ComponentはPDFiumエンジンをDelphi、C++Builder、Lazarusのために包み、高レベルのVCL APIと、DLLの有無にかかわらず動く一連の適合バリデータを提供する。サポートされる標準とプラットフォームについてはPDFium Component製品ページを参照のこと