DelphiおよびLazarus用のPDFium Componentは、ISO 19444-1で定義されたXML交換フォーマットであるXFDFを介して、フォームデータと注釈をやり取りします。TPdf.ExportXFDFは、ロードされたドキュメント内のすべてのフォームフィールド値とサポートされているすべての注釈をXFDFファイルまたはTStreamにシリアル化します。TPdf.ImportXFDFはXFDFドキュメントを読み戻し、ページ上に注釈を再作成してフィールド値を適用します。この1回のラウンドトリップにより、すべてのドキュメントアプリケーションが最終的に必要とする2つのワークフローがカバーされます。それは、査読者がAcrobatでPDFにマークアップを行い、そのコメントをマージするためにあなたにメールで送信するワークフロー、またはコンプライアンス規則により、フォームデータをPDF自体に埋め込むのではなく、差分比較可能で監査可能な個別のファイルに保存する必要があるというワークフローです
PDFiumはXFDFのインポートとエクスポートをサポートしていますか?
PDFiumライブラリ自体はサポートしていません。ネイティブのC APIにはXFDFやFDFの関数がまったく存在せず、公開ヘッダーにはどちらのフォーマットを読み書きするものも含まれていないため、どれだけラッパーを作成しても実現できません。そのため、PDFium Componentは、既存のTPdfの注釈およびフォームフィールドのデータモデルの上に、XMLを直接構築して解析する専用ユニットを用意し、PascalでXFDFエンジン全体を実装しています。ライターはXMLを手動で出力し、リーダーは手書きのステートマシンパーサーであるため、この機能はDelphiのXMLDocやFPC of DOMユニットへの依存関係を追加せず、DelphiとLazarusでまったく同じように動作します
この設計上の選択は、代替案を評価する際に重要になります。Pascalから生のFPDF_*関数を呼び出している場合、XFDFは壁となります。この機能は単にDLLに含まれていないからです。このコンポーネントは、XFDFを純粋なシリアル化の問題として扱うことでその壁を越えます。つまり、ラッパーがすでに読み取り方を知っているフィールド値と注釈レコードを収集し、それらを標準のXMLとして書き出し、インポート時にそのプロセスを逆にするという方法です
XFDFファイルには何が含まれていますか?
ISO 19444-1はXFDFをFDFのXML表現として定義しており、そのペイロードは2つのトップレベルブロックに分かれています。<fields>要素は、階層的なフォームフィールド名とその値(ユーザーがAcroFormに入力、選択、またはチェックしたすべてのもの)を保持します。<annots>要素は注釈を保持します。TPdf.ExportXFDFは、text、highlight、underline、strikeout、squiggly、line、circle、square、caret、polygon、polyline、stamp、ink、freetext、fileattachment、sound、link、redactの18のサブタイプを出力します。Widget(ウィジェット)注釈は、そのデータが<fields>で転送されるため、意図的に<annots>からは除外されており、Popupなどのビューア内部のサブタイプはXFDFの語彙に含まれていません
ラウンドトリップの信頼性を高めるため、TPdfAnnotationレコードはXFDFが想定するメタデータで拡張されました。これには、Name(NMの一意の識別子)、Subject、ModificationDate、CreationDate、Icon、Opacity、線の端点、ポリゴンおよびポリラインの頂点、インクジェスチャのパスが含まれます。新しい各フィールドはHas*ブール型センチネルとペアになっているため、以前のバージョンに対して構築されたアノテーションコードは引き続きコンパイルでき、同じ辞書を生成し続けます(未設定のフィールドは書き込まれません)。すでにプログラムでマークアップを作成している場合、クアッドポイント付きのテキストマークアップ注釈に使用しているのと同じレコードに、XFDFが必要とするすべてのデータが保持されるようになります
フォームデータと注釈をXFDFにエクスポートするにはどうすればよいですか?
1回の呼び出しでドキュメント全体を処理できます。フォーム入力(form-fill)を有効にしてPDFをロードし、ExportXFDFを呼び出すと、コンポーネントはすべてのページをスキャンし、フィールド値と注釈を収集してXMLを書き込みます。戻り値は書き込まれたUTF-8のバイト数であり、ログでの簡単な整合性チェックに使用できます
var
Pdf: TPdf;
Bytes: Integer;
begin
Pdf := TPdf.Create(Self);
Pdf.FormFill := True; // needed so field values are live
Pdf.FileName := 'expense-report.pdf';
Pdf.Active := True;
Bytes := Pdf.ExportXFDF('expense-report.xfdf');
ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;
双方向ともにTStreamのオーバーロードが用意されているため、ディスク上の一時ファイルを使用する必要はありません。XFDFがHTTPレスポンス、データベースのBLOB、または署名付きアーカイブエントリに送られる場合、TMemoryStreamへのエクスポートが自然な形になります
var
Buffer: TMemoryStream;
begin
Buffer := TMemoryStream.Create;
try
Pdf.ExportXFDF(Buffer); // same XML, no file involved
Buffer.Position := 0;
// hand the stream to a web response, blob column, or zip entry
finally
Buffer.Free;
end;
end;
ImportXFDFはどのようにしてコメントをドキュメントにマージしますか?
インポート側は、査読者のワークフローが完了する場所です。同僚がAcrobatで契約書に注釈を付け、コメントをXFDFとしてエクスポートし、PDFの2つ目のコピーの代わりに数キロバイトのXMLを送信します。TPdf.ImportXFDFはそのファイルを解析し、page属性が指定するページ上に各注釈を作成し、一致するウィジェット注釈にフィールド値を書き込みます。この関数は、適用されたフィールドと注釈の合計数を返すため、UIで実際にどれだけ届いたかを確認できます
var
Applied: Integer;
begin
Applied := Pdf.ImportXFDF('review-comments.xfdf');
StatusBar.SimpleText :=
Format('%d fields and annotations merged', [Applied]);
end;
パーサーは、厳格さよりも許容度を重視して構築された手書きのタグスキャナーです。未知の要素はスキップされ、処理命令、DOCTYPE宣言、およびコメントは無視され、認識されないフラグ名は破棄され、属性値はUTF-16サロゲートペアを含むXMLエンティティおよび数値文字参照の完全なセットを介してアンエスケープされます。Acrobat、Webビューア、または他のツールによって生成されたXFDFは、特に行事なしでマージされます。注釈がドキュメントに含まれると、査読サイクルはPDFium Componentを使用した注釈査読ワークフローの構築で説明されている返信およびステータス機能に続き、インポートされたフィールド値はフォームフィールドナビゲーションでカバーされる通常のタブ順序ロジックに参加します
なぜXFDFでは小数点区切り文字が重要なのでしょうか?
XFDFのすべての注釈は、座標文字列(rect="70.5,540,200,560"など)によって配置され、ISO 19444-1では小数点としてピリオドが要求されます。Delphiのデフォルトの浮動小数点フォーマットはWindowsのロケールに従うため、ドイツ語やフランス語のシステムでは、単純なFloatToStrが70.5を70,5に変換してしまい、カンマ区切りの座標リストを破損させ、他のプロセッサが拒否したり誤読したりする原因になります。PDFium Componentは、書き込むすべての数値を正規化します。ロケールの小数点区切り文字はピリオドに書き換えられ、末尾のゼロはトリミングされるため、エクスポートされたファイルはen-USマシンで生成されたかde-DEマシンで生成されたかにかかわらず、バイト単位で同一になります。社内では動作するものの、ヨーロッパの顧客環境で失敗するPDFツールをデバッグしたことがあるなら、まさにこのバグに遭遇したことになります。コンポーネントがこれを自動的に処理してくれることを知っておく価値はあります
ラウンドトリップの制限事項は何ですか?
明確にしておくべき2つの境界線があります。第一に、XFDFは注釈のデータを転送するものであり、レンダリングされた外観(アピアランス)を転送するものではありません。アピアランスストリームはフォーマットの一部ではないため、受信側のビューアはプロパティから各注釈の外観を再生成します。ハイライトや四角形はどこでも正しくレンダリングされます。カスタムの外観を持つスタンプは、ターゲットビューアがそのスタンプ名に対して描画する内容にフォールバックします。第二に、基礎となるPDFium APIには文字列のセッターはありますが、注釈番号やパスのジオメトリのセッターはありません。そのため、Opacity、線の端点、頂点、およびインクジェスチャはエクスポート専用です。コンポーネントはXFDFの書き込み時にこれらを忠実に保持しますが、インポート時にこれらをPDFに書き戻すことはできません。フィールド値、コンテンツ、色、矩形、クアッドポイント、日付、および一意のメタデータはすべて、双方向でラウンドトリップします
実践的なスタートガイドとして、Delphi、C++Builder、LazarusのデモセットにXfdfLabサンプルが付属しており、開いたPDFに対して両方の呼び出しをボタンにバインドしています。XFDFのサポートは、ベースとなる注釈およびフォームAPIとともに、現在のリリースのPDFium Componentに含まれています