適用於 Delphi 與 Lazarus 的 PDFium 元件透過 XFDF(ISO 19444-1 定義的 XML 交換格式)交換表單資料與註解;TPdf.ExportXFDF 將已載入檔案中的每個表單欄位值與每個受支援的註解序列化為 XFDF 檔案或 TStream;TPdf.ImportXFDF 則將 XFDF 檔案讀回,在對應的頁面上重新建立註解並套用欄位值;這一個雙向轉換涵蓋了每個文件應用程式最終都需要之兩個工作流程:審查人員在 Acrobat 中為 PDF 進行標記,並將註解郵寄給您進行合併;或是合規規則說明表單資料必須存放在獨立的、可比對的且可稽核的檔案中,而不是直接寫入 PDF 本身
PDFium 是否支援 XFDF 匯入與匯出?
PDFium 函式庫本身並不支援;原生 C API 完全沒有 XFDF 或 FDF 函式——其公開標頭中沒有任何內容能讀取或寫入這兩種格式,因此不論進行多少次包裝都無法實現;因此,PDFium 元件在 Pascal 中實作了整個 XFDF 引擎(在專用單元中直接基於現有的 TPdf 註解與表單欄位資料模型建置與解析 XML);寫入器手動發出 XML,而讀取器則是手動撰寫的狀態機解析器,因此該功能不依賴 Delphi XMLDoc 或 FPC DOM 單元,且在 Delphi 與 Lazarus 上表現完全一致
當您評估替代方案時,該設計選擇很重要;如果您一直從 Pascal 呼叫原始的 FPDF_* 函式,XFDF 就是一堵牆:DLL 中根本沒有此功能;該元件藉由將 XFDF 視為純粹的序列化問題來越過這堵牆——收集包裝器已經知道如何讀取的欄位值與註解紀錄,將它們寫出為標準 XML,並在匯入時反向執行該過程
XFDF 檔案包含什麼內容?
ISO 19444-1 將 XFDF 定義為 FDF 的 XML 表示形式,其承載內容分為兩個頂層區塊;<fields> 元素攜帶分層的表單欄位名稱及其值——即使用者在 AcroForm 中輸入、選取或勾選的每一項內容;<annots> 元素攜帶註解:TPdf.ExportXFDF 發出 18 種子類型——文字、醒目提示、底線、刪除線、波浪線、線條、圓形、正方形、插入符號、多邊形、折線、圖章、手寫、任意文字、檔案附件、聲音、連結和修訂;Widget 註解特意不在 <annots> 中出現,因為它們的資料在 <fields> 中傳輸,且諸如 Popup 的檢視器內部子類型並不是 XFDF 字彙的一部分
為了使雙向轉換更加忠實,TPdfAnnotation 紀錄擴充了 XFDF 所期望的中介資料:Name(NM 唯一識別碼)、Subject、ModificationDate、CreationDate、Icon、Opacity、線條端點、多邊形與折線頂點,以及手寫軌跡路徑;每個新欄位都與一個 Has* 布林值哨兵配對,因此針對較早版本建立註解的程式碼仍可繼續編譯並繼續產生相同的字典——未設定的欄位絕不會被寫入;如果您已經以程式設計方式建立標記,您用於 帶有四邊形頂點的文字標記註解 的同一個紀錄,現在攜帶了 XFDF 所需的每一項內容
如何將表單資料與註解匯出至 XFDF?
一次呼叫即可完成整個檔案的工作;載入啟用了表單填充的 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;
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,然後給您發送幾 KB 的 XML,而不是 PDF 的第二個副本;TPdf.ImportXFDF 解析該檔案,在其 page 屬性所指名的頁面上建立每個註解,並將欄位值寫入相符的 widget 註解中;該函式傳回套用的欄位加註解的組合數量,以便使用者介面能夠確認到底接收了多少內容
var
Applied: Integer;
begin
Applied := Pdf.ImportXFDF('review-comments.xfdf');
StatusBar.SimpleText :=
Format('%d fields and annotations merged', [Applied]);
end;
解析器是一個手動撰寫的標記掃描器,旨在實現容錯性而非嚴格性:跳過未知的元素,忽略處理指示、DOCTYPE 宣告以及註解,捨棄無法識別的旗標名稱,且屬性值會透過整套 XML 實體和數值字元參照(包括 UTF-16 代理對)進行反逸出;由 Acrobat、網頁檢視器或其他工具產生的 XFDF 皆可順利合併;一旦註解進入檔案,審查週期便會繼續進行,正如 使用 PDFium 元件建立註解審查工作流程 中描述的回覆與狀態機制,且匯入的欄位值會參與 表單欄位巡覽 中涵蓋的一般標籤順序邏輯
為什麼小數分隔符對 XFDF 很重要?
XFDF 中的每個註解編碼都是由座標字串來定位的——例如 rect="70.5,540,200,560" 等等——且 ISO 19444-1 要求以句點作為小數分隔符;Delphi 預設的浮點數格式設定遵循 Windows 地區設定,因此在德國或法國系統上,一般的 FloatToStr 會將 70.5 轉換為 70,5,這會將以逗號分隔的座標清單損毀為其他處理器拒絕或誤讀的垃圾資料;PDFium 元件會對其寫入的每個數字進行標準化:地區小數分隔符會被重寫為句點,且尾隨的零會被剪裁,因此不論是在 en-US 還是 de-DE 電腦上產生的匯出檔案,其位元組都是完全一致的;如果您曾經除錯過一個在辦公室運作正常但在歐洲客戶網站上失效的 PDF 工具,您就遇到了這類確切的錯誤——值得高興的是該元件會為您處理好此問題
雙向轉換的限制是什麼?
兩項界限值得明確指出;第一,XFDF 傳輸的是註解資料,而非轉譯的外觀——外觀資料流不屬於該格式的一部分,因此接收端檢視器會根據其屬性重新產生每個註解的外觀;醒目提示或正方形在任何地方都能正確轉譯;具有自訂外觀的圖章則會退回到目標檢視器針對該圖章名稱所繪製的內容;第二,底層的 PDFium API 具有針對字串的 setter,但沒有針對註解編號或路徑幾何結構的 setter,因此 Opacity、線條端點、頂點以及手寫軌跡僅供匯出:該元件在寫入 XFDF 時忠實地保留了它們,但在匯入時無法將它們寫回 PDF 中;欄位值、內容、色彩、矩形、四邊形頂點、日期和身分識別中介資料都在兩個方向上支援雙向轉換
為了親手體驗,XfdfLab 範例隨附在 Delphi、C++Builder 和 Lazarus 示範集中,並在您開啟的任何 PDF 上將這兩個呼叫與按鈕連結;XFDF 支援包含在當前發布的 PDFium 元件 中,與其建立在其上的註解和表單 API 並存