losLab PDF Libraryは、PDFフォームデータのエクスポートおよびインポートのために、3つの異なる方法を提供しています。AcroFormフィールド値用のFDFおよびXFDF、注釈コメント用の同様の2つのフォーマット、そしてフォームに直接書き込まれる完全なXFA XDPパッケージです。ExportFormDataToXFDF、ImportFormDataFromXFDF、ImportAnnotationsFromFDF、ExportAnnotationsToXFDF、およびSetXFAFromStringがエントリポイントであり、それぞれにファイルバリアントと文字列バリアントが用意されています
これらほど多くのメソッドが存在する理由は、PDFのフォームデータが一様ではないからです。入力済みのAcroFormにはフィールド値があり、レビューコメントが含まれている場合もあります。さらに、レガシーなXFAフォームには、これらとは無関係なXMLアプリケーション記述全体が埋め込まれています。losLab PDF Libraryは、これら3つの処理をあえて別々のAPIに分けて保持しています。統合してしまうと、少なくともそのうちの2つに不適切なデータモデルを強いることになるからです。どの機能グループが必要かを決定することが設計上の最初の選択肢となり、通常は相手方のシステムが実際に何を受け取るかが判明した時点で決定されます
FDF、XFDF、およびXFAフォームデータの違いは何ですか?
FDFとXFDFは同じ情報を2つの異なる構文で保持しますが、XFAはまったく別の概念です。FDFは、ミニチュアのPDF構文ドキュメント(ISO 32000-2 §12.7.8)であり、文字列がPDF内部のリテラル文字列とまったく同じ方法でエスケープされた、<< /T (name) /V (value) >>ディクショナリの/Fields配列です。XFDFは同じデータのXML形式(ISO 19444)であり、Acrobatやほとんどのフォームバックエンドがネイティブで読み書きする<fields>ツリーです。XFAはそのどちらでもありません。XML Forms Architectureのテンプレートとそのデータであり、PDFが/AcroForm/XFAから参照するXML Data Package(XDP)として保存されます。フィールド値のやり取りを行う場合はFDFまたはXFDFを選択し、そもそもXFAフォームとして作成されたドキュメントを保守する場合にのみXFAを使用してください
FDFおよびXFDFにおいて、losLab PDF Libraryはもう1つの境界線、すなわちフィールド値と注釈コメントを区表しています。フォームデータ用のメソッド群(ExportFormDataToFDF、ImportFormDataFromFDF、ExportFormDataToXFDF、ImportFormDataFromXFDF)は/Fieldsサブツリーを読み書きし、それ以外には関与しません。一方、注釈用のメソッド群(ExportAnnotationsToFDF、ImportAnnotationsFromFDF、ExportAnnotationsToXFDF、ImportAnnotationsFromXFDF)は、Acrobatでいう「コメントのエクスポート」に相当する/Annotsサブツリーを読み書きします。これら2つが重複することはないため、フィールド値をエクスポートしても不要なレビューメモが混入することはなく、コメントをインポートしてもユーザーがすでに入力した値が乱されることはありません。コントロールがクリックされたときや再計算されたときの動作については、第3の課題として対話型フォームのアクションとJavaScriptに関する関連記事で解説しています
DelphiでFDFまたはXFDFからPDFフォームに値を入力するにはどうすればよいですか?
ドキュメントをロードし、インポートメソッドを1つ呼び出し、保存します。ImportFormDataFromXFDFとImportFormDataFromFDFは、それぞれ入力されたフォームデータを解析し、完全修飾名によって各エントリをAcroFormフィールドにマッピングして値を設定し、実際に更新されたフィールドの数を返します。どちらのメソッドも、ターゲットPDFにすでに存在するフィールドのみを更新します。フォームが定義していない名前のフィールドを自動的に生成することはないため、不要なデータファイルや悪意のあるデータファイルによってフォームが勝手に拡張されるのを防ぎます
var
Lib: TPDFlib;
FieldsSet: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('application-blank.pdf', '');
// XFDF produced by a case-management system, already UTF-8 on disk
FieldsSet := Lib.ImportFormDataFromXFDF('applicant-1042.xfdf');
if FieldsSet > 0 then
Lib.SaveToFile('application-filled.pdf');
finally
Lib.Free;
end;
end;
複雑なデータが正しく移行されるかどうかは、2つの詳細によって決まります。1つ目はエスケープです。FDFstores値はPDFリテラル文字列として保存されるため、括弧、バックスラッシュ、または印刷不可能なバイトを含む値は、ISO 32000-2 §7.3.4.2で定義されている8進エスケープに包まれて届きます。losLab PDF Libraryはインポート時にそのエスケープを解除するため、括弧やスラッシュはユーザーが入力した文字どおりに戻ります。2つ目はエンコーディングです。ファイルベースのメソッドはXFDFとFDFをUTF-8として読み書きします。これはXFDF宣言が保証するものであり、非ASCIIフィールド値(アクセント付きの名前、通貨記号、CJKアドレス)が破損することなくラウンドトリップするために必要なものです。ご自身でXFDFを構築する場合は、UTF-8を宣言してUTF-8で書き込めば、インポート処理と整合性が保たれます
階層型フィールド、複数選択肢、およびリッチテキスト
階層構造を持つフィールド名は、単純なエクスポート処理が破綻しやすい最初のポイントです。AcroFormは、Applicant.FullNameのようにドットで区切られた完全なタイトルでネストされたフィールドを特定しますが、XFDFはこのドット区切りの文字列を単一の名前属性には配置しません。ISO 19444は、これを<field name="Applicant"><field name="FullName">のようにネストします。losLab PDF Libraryは、エクスポート時にドット区切りのタイトルをネストされた<field>要素に分割し、インポート時にはネストされた要素を完全なタイトルに再構成するため、双方向の処理の対称性が保たれます。また、エクスポート時には末端ではない親フィールドはスキップされます。AcroFormの親ノードは名前階層のみを保持し、独自のデータ値を持たないため、出力すると実際のフォームと一致しない空の<value></value>が生成されてしまうからです。2つ目の罠は、複数選択のリストボックスです。選択フィールドは同時に複数の選択値を保持できます。XFDFはこれを繰り返される<value>要素として表現し、FDFは/V配列として表現します。losLab PDF Libraryは、フィールドが真に複数選択可能な場合にのみ複数の値に分割するため、通常の複数行テキストフィールドは不正な値に断片化されることなく改行を維持できます
3つ目はリッチテキストであり、最も誤解されやすい部分です。書式設定されたフィールドは、そのマークアップをXHTMLサブツリーとしてRVエントリに保存し、XFDFはそれを<value-richtext>で保持します。losLab PDF Libraryは、このサブツリーをエスケープせずに生のXMLフラグメントとして書き込むため、後続のツールは表示可能なタグ文字列ではなく、本物のリッチテキストとして読み取ることができます。インポート時には、生のRVサブツリーを保存しつつ、プレーンな<value>をVに適用します。フィールドが両方を提供している場合、プレーンな値がVに優先適用され、リッチテキストはRVに併記されます。これは、リッチテキストのインポートによって他のツールが設定した値が意図せず書き換えられるのを防ぐための相互運用性ルールです。支援技術にドキュメント構造を伝えるためにリッチテキストを使用する場合は、タグ付きPDFとアクセシビリティ構造に関する記事で説明されている読み順と同様に取り扱ってください
var
Lib: TPDFlib;
const
XFDF =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">' +
'<fields>' +
' <field name="Applicant">' +
' <field name="FullName"><value>Alice Example</value></field>' +
' </field>' +
' <field name="Skills">' +
' <value>Delphi</value><value>PDF</value>' +
' </field>' +
' <field name="Notes">' +
' <value>See attachment</value>' +
' <value-richtext><body><p>See <b>attachment</b></p></body></value-richtext>' +
' </field>' +
'</fields></xfdf>';
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('intake.pdf', '');
// Applicant.FullName reassembles; Skills fills the /V + /I arrays;
// Notes gets V from <value> and RV from <value-richtext>
Lib.ImportFormDataFromXFDFString(XFDF);
Lib.SaveToFile('intake-filled.pdf');
finally
Lib.Free;
end;
end;
注釈コメントをFDFまたはXFDFとして双方向にやり取りするにはどうすればよいですか?
コメントは注釈用のメソッド群によってやり取りされますが、サポートされているサブセットは意図的に小さく制限されています。ExportAnnotationsToXFDFとImportAnnotationsFromXFDF(およびそれに対応するFDFメソッド)は、正常にラウンドトリップ可能なフィールド(注釈のサブタイプ、矩形領域、0から始まるページインデックス、T内の作成者、Subj内の件名、プレーンなContents、およびPDFの/C DeviceRGBの3つの値とXFDFの#RRGGBB属性を相互変換する色情報)を持つテキストスタイルのマークアップを処理します。インポート処理は既知の要素名のみを受け入れ、ページが範囲外であるか矩形情報が欠落しているエントリはスキップするため、手動で編集されたXFDFや外部のXFDFによって破損した注釈がドキュメントに追加されるのを防ぎます。現時点でこのサブセットでサポートされていない機能としては、リッチテキストコンテンツ(RC)、ポップアップ、クアッドポイント(四角形座標)、フリーハンド(インクリスト)、頂点、事前に構築された外観ストリームなどがあります。そのため、ハイライトの形状やカスタムスタンプの外観はこのパスでは保持されません。実際に反映された内容を確認するには、ページ注釈をスキャンするか、テキスト検索とページ要素の列挙で解説されている要素ツリーを参照してください
var
Src, Dest: TPDFlib;
Xfdf: WideString;
begin
Src := TPDFlib.Create;
Dest := TPDFlib.Create;
try
Src.LoadFromFile('reviewed.pdf', '');
Xfdf := Src.ExportAnnotationsToXFDFString; // <annots> subset only
Dest.LoadFromFile('clean-copy.pdf', ''); // pages must line up by index
Dest.ImportAnnotationsFromXFDFString(Xfdf);
Dest.SaveToFile('clean-copy-commented.pdf');
finally
Dest.Free;
Src.Free;
end;
end;
SetXFAFromStringを使用した完全なXFA XDPパッケージの書き込み
XFAは特殊なケースであり、SetXFAFromStringを使用してパッケージ全体を一度に書き込みます。このメソッドは、テンプレートおよびデータセットパッケージを含む完全なXML Data Package(<xdp:xdp>ドキュメント)を受け取り、それを/AcroForm/XFAから参照されるストリームとして保存します。losLab PDF Libraryは、ドキュメントにAcroFormコンテナが存在しない場合にオンデマンドで自动作成するため、XFAを関連付けるためだけにダミーのフィールドを追加する必要はありません。また、保持されていた解析済みのXFA状態は破棄されるため、その後の読み取りでは古いキャッシュではなく、書き込まれた直後のパッケージが反映されます。XFAパッケージはXMLであり、必ずしもUTF-8であるとは限らないため、ライブラリはUTF-16のバイトオーダーマーク(BOM)を検出し、解析前にパッケージを正しくデコードします。これは、デフォルトでUTF-16を出力するツールによって生成されたパッケージを扱う際に重要です
パッケージが配置されると、GetXFAFormFieldValueとSetXFAFormFieldValueはScripting Object Model(SOM)パスを介して個々のフィールドにアクセスします。losLab PDF Libraryは、標準的なSOMルートと単純な相対パスの両方を受け入れるため、form1.FullName、$data.form1.FullName、およびxfa.datasets.data.form1.FullNameはすべて同じデータノードに解決されます。また、テンプレート側も同様に$templateおよびxfa.templateを受け入れます。この許容度により、常に完全修飾形式を出力する外部システムから生成されたSOMパスを扱うのが容易になります
var
Lib: TPDFlib;
const
Xdp =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">' +
' <template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">' +
' <subform name="form1">' +
' <field name="FullName"><ui><textEdit/></ui></field>' +
' </subform>' +
' </template>' +
' <xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">' +
' <xfa:data><form1><FullName>Alice Example</FullName></form1></xfa:data>' +
' </xfa:datasets>' +
'</xdp:xdp>';
begin
Lib := TPDFlib.Create;
try
Lib.SetXFAFromString(Xdp, 0); // creates the AcroForm container if none exists
// read back through the data DOM with an SOM path
if Lib.GetXFAFormFieldValue('form1.FullName') = 'Alice Example' then
Lib.SetXFAFormFieldValue('form1.FullName', 'Bob Example');
Lib.SaveToFile('xfa-packet.pdf');
finally
Lib.Free;
end;
end;
まとめると、フィールド値は、階層構造、複数選択、およびリッチテキストを含め、FDFおよびXFDFを介して完全に往復可能です。注釈コメントは、制限されたテキストマークアップのサブセットとして双方向移行が可能ですが、いくつかの機能に制限があります。そしてXFAは、パッケージ全体として読み書きされ、個々のフィールドにはSOMパスを介してアクセスします。相手方のシステムに適した形式を選択し、手動で構築するデータにはUTF-8を宣言してください。また、インポートメソッドは、ドキュメントにすでに定義されているフィールドと注釈のみを処理することを忘れないでください。ここで説明したフォームデータ、注釈、およびXFAに関するメソッドは、DelphiおよびC++Builder向けのlosLab PDF Libraryの一部であり、そのリファレンスにはすべてのエクスポートおよびインポートエントリポイントの完全なパラメータリストが掲載されています