HotPDF 透過作用於目前載入文件的兩個函式將 PDF 註解匯入與匯出為 XFDF:ExportLoadedAnnotationsToXFDF 和 ImportLoadedAnnotationsFromXFDF。XFDF 是標準化為 ISO 19444-1 的 XML 註解交換格式,這對函式可讓 Delphi 或 C++Builder 程式將其註解傳遞給 Acrobat 或協力廠商審查工具,並收回標記後的結果,而無需重寫註解所在的頁面內容
想像一下這解決的兩個方向。審查人員在 Acrobat 中開啟您產生的報告,在未對齊的圖表上繪製紅色箭頭,圈出錯誤的總額並在頁邊輸入筆記,然後將這些註解匯出為小型的 XFDF 檔案。或者相反地:您的程式本身產生這些標記,且您需要將它們傳送給使用非 HotPDF 工具的使用者。不論哪種方式,註解都以雙方都能理解的 XML 格式進行傳輸,而 PDF 頁面本身在位元組層級保持完全不變
FDF 和 XFDF 有何不同?
FDF 和 XFDF 在兩種不同的語法中攜帶相同的承載資料,當您決定將哪種檔案傳遞給另一個工具時,這種區別就顯得十分重要。FDF 是 PDF 規格本身定義的舊版表單資料格式(Forms Data Format):它使用 PDF 物件語法,因此 FDF 檔案看起來像是一個精簡版的 PDF,需要支援 PDF 的解析器才能讀取。XFDF 是相同資料的 XML 表達形式,獨立標準化為 ISO 19444-1,這意味著任何平台上的任何 XML 函式庫都可以開啟、對比(diff)或產生它。這兩種格式都可以在 <fields> 樹狀結構(受 ISO 19444-1 第 6.3 條約束)中攜帶表單欄位值,並在 <annots> 樹狀結構中攜帶註解;HotPDF 拆分了這些職責,將表單資料導向至 ExportLoadedFormToXFDF,並將 ExportLoadedAnnotationsToXFDF 保留給 <annots> 端。當您與 Web 服務、Java 審查伺服器或指令碼交換註解時,XFDF 格式可以避免強迫另一端嵌入 PDF 解析器
如何在 Delphi 中將 PDF 註解匯出為 XFDF?
HotPDF 透過走訪載入文件的每一個頁面,為每個支援的註解輸出一個 XFDF 元素,並傳回寫入的註解數量。先載入 PDF,然後使用目標路徑呼叫 ExportLoadedAnnotationsToXFDF。整數傳回值是序列化的註解計數;小於或等於零的結果表示未匯出任何內容且未寫入任何檔案,這表明該文件沒有包含支援的子類型的註解
var
Pdf: THotPDF;
Written: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('report-reviewed.pdf', '') > 0 then
begin
// 在每個頁面上為每個支援的註解寫入一個 XFDF 元素
Written := Pdf.ExportLoadedAnnotationsToXFDF('comments.xfdf');
if Written <= 0 then
ShowMessage('未找到支援的註解');
end;
finally
Pdf.Free;
end;
end;
輸出的 XFDF 是純文字、易讀的 XML。HotPDF 會在 ISO 19444-1 命名空間中寫入 <xfdf> 根、一個 <annots> 容器,且每個註解對應一個子元素,並將其以 0 為起始的頁面索引、色彩和幾何形狀作為屬性或子元素。一條帶有黃色填滿和開放式箭頭的線條,與一個填滿的多邊形並存,會被序列化為如下形式:
<?xml version="1.0" encoding="UTF-8"?>
<xfdf xmlns="http://ns.adobe.com/xfdf/">
<annots>
<line page="0" start="72,700" end="220,700"
color="#FF0000" interior-color="#FFFF00"
head="OpenArrow" tail="None">
<contents-richtext>基準線看起來有偏差</contents-richtext>
</line>
<polygon page="0" color="#0000FF" interior-color="#CCE5FF">
<vertices>72,120;180,120;180,200;72,200</vertices>
</polygon>
</annots>
</xfdf>
註解子類型如何對應至 XFDF 元素
每個註解子類型都對應至特定的 ISO 19444-1 元素,並有其自身的幾何形狀約定,HotPDF 遵循這些結構而非自行設計。線條註解帶有 start 和 end 屬性,其中包含直接取自註解 L 陣列的兩個端點座標組,而 LE 線條終端樣式則變為 head 和 tail 屬性。多邊形和聚合線(polyline)註解將其點清單移入 <vertices> 子元素中,以分號分隔的 x,y 配對表示,而非作為屬性,因為期望讀取子元素的閱讀器會默默捨棄隱藏在其他任何地方的點。筆跡(Ink)註解可以包含多個獨立的筆劃,它巢狀了 <inklist> 元素,其中每個筆劃對應一個 <gesture> 子元素,因此多筆劃的簽名在傳輸過程中仍保持為不同的手勢,而非合併後的單一影像
Rich Text、色彩和邊框樣式與幾何形狀一起得以保留。筆記的 Rich Text 內容主體會被寫入為 <contents-richtext> 子元素;PDF 儲存在 IC 陣列中的內部填滿(例如圓形、矩形、多邊形或線條箭頭內部的繪製,以及徹底遮蔽盒子的填滿)會以 #RRGGBB 格式作為 interior-color 屬性傳遞;邊框寬度、虛線樣式和雲狀邊框效果會對應至 width、dashes、style 和 intensity 屬性,因此帶有雲狀外框的圖標在接收端仍會顯示為雲狀。HotPDF 還保留了附加在標記註解上的快顯視窗,將快顯視窗的子幾何形狀及其開啟或關閉狀態匯入到註解的 Popup 字典中,並且它還攜帶了文字註解的開啟和審查狀態,因此審查後的文件不僅保留了其形狀,還保留了審查人員所依賴的工作流程中介資料
將 XFDF 匯入回載入的文件中
HotPDF 透過解析 XML 匯入 XFDF,使用 NewLoadedAnnotation 為每個元素建立全新的註解,將其附加到該元素所指定的頁面,並傳回其新增的註解數量。此工作流程與匯出對稱:載入基礎 PDF、使用審查人員的檔案呼叫 ImportLoadedAnnotationsFromXFDF,然後儲存載入的文件以保留新的標記。如果檔案缺失或 XML 無法解析,該函式會傳回零,且載入的文件將保持不變
var
Pdf: THotPDF;
Added: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('report.pdf', '') > 0 then
begin
Added := Pdf.ImportLoadedAnnotationsFromXFDF('comments.xfdf');
if Added > 0 then
Pdf.SaveLoadedDocument('report-annotated.pdf');
end;
finally
Pdf.Free;
end;
end;
由於每個 XFDF 元素都指定了自己的頁面索引,因此即使您依次匯入多個檔案,註解也會精確落在撰寫它們的頁面上,這使得在進行單次儲存之前,可以安全地將多個審查人員的意見收集到同一個載入的文件中。下面的範例將兩個審查人員的意見合併為一個檔案。要在程式碼中建立和編輯註解物件本身,而不是將它們作為檔案進行交換,請參考 HotPDF 如何直接從 Delphi 建立和編輯 PDF 註解物件
var
Pdf: THotPDF;
Total, I: Integer;
Files: array[0..1] of string;
begin
Files[0] := 'alice-comments.xfdf';
Files[1] := 'bob-comments.xfdf';
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('master.pdf', '') > 0 then
begin
Total := 0;
for I := Low(Files) to High(Files) do
Inc(Total, Pdf.ImportLoadedAnnotationsFromXFDF(Files[I]));
if Total > 0 then
Pdf.SaveLoadedDocument('master-merged.pdf');
end;
finally
Pdf.Free;
end;
end;
哪些內容能乾淨地雙向往返,哪些不能
HotPDF 會雙向往返 ISO 19444-1 支援的註解子類型,並刻意跳過其餘類型,而不是輸出可能會被閱讀器誤解的內容。支援的集合涵蓋了在實際審查工作中佔主導地位的標記類型:文字筆記、自由文字、線條、矩形、圓形、多邊形、聚合線、四種文字標記類型(醒目提示、底線、刪除線和波浪線)、印章、筆跡和插入符號,加上檔案附件、聲音、徹底遮蔽和連結,共計 18 個子類型。子類型不在該清單中的註解將在匯出時被忽略,且因為它是被跳過而非寫入為空值,它不會增加該函式傳回的計數
Rich Text 是實際的局限。HotPDF 保留了 <contents-richtext> 主體,以便格式化文字和純文字內容一起完成傳輸,但 XFDF 攜帶的是註解的文字與樣式標記,而非轉譯後的外觀串流,因此接收端應用程式會使用其自身的字型和版面配置重新繪製快顯視窗,而無法複製 HotPDF 的精確像素。請將雙向往返視為對內容和意圖的忠實重現,而非螢幕上精確到像素的轉譯。如果您的樣式內容存在於 XFA 表單資料中而非註解資料流中,則規則會有所不同,HotPDF 如何處理 XFA exData、Rich Text 和超連結介紹了這條獨立的路徑
字元層級的處理比看起來更嚴格,這正是您所需要的。HotPDF 在寫入文字時會套用 ISO 19444-1 第 5.8.2 條的轉義規則,對 XML 有特殊意義的字元和控制位元組進行編碼,使包含 & 符號、角括弧或分行的註解產生格式正確的 XML,且能被任何符合規範的解析器接受,並在匯入時反轉相同的規則。這就是為什麼從試算表貼上的筆記(包含標點符號等)能夠完好無損地還原,而不會損毀檔案
註解交換是載入文件 API 功能的一小部分,它可以與其餘功能組合使用:匯入審查人員的 XFDF、調整頁面或編輯文件中介資料、扁平化或重新設定檔案權限,然後匯出新的 XFDF 以進行下一輪操作。所有這些功能均隨附於適用於 Delphi 和 C++Builder 的標準 HotPDF 元件 中,其參考文件說明了完整的註解子類型支援以及隨附的表單資料 XFDF 函式