在開發企業級文件解決方案時,您不可避免地會遇到由各種工具產生的 PDF:從高階的 Adobe 軟體到充滿錯誤的開源函式庫或虛擬印表機驅動程式。您將面臨的最臭名昭著的結構問題之一,就是「混合參照」(hybrid-reference) PDF。
在本文中,我們將解釋什麼是混合參照,為何特定的辦公室應用程式會產生它們,以及如何使用 Delphi 與穩健的 PDF 函式庫,以程式化方式解析與修復這些結構。
PDF 交叉參照表的演進
為了在不解析整個檔案的情況下快速定位物件 (字型、圖片、頁面),PDF 使用交叉參照表 (Cross-Reference Table, XRef)。在早期的 PDF 規範 (PDF 1.4 及更早版本) 中,這是在檔案結尾的一個文字 ASCII 表格。
從 PDF 1.5 開始,Adobe 引入了交叉參照串流 (Cross-Reference Streams, XRefStm),將交叉參照資料壓縮為二進位串流,大幅縮小了檔案大小。然而,為了與舊版 PDF 閱讀器保持向後相容性,某些產生器開始產生混合參照 PDF (Hybrid-Reference PDFs)。這些檔案同時包含舊式 ASCII XRef 表格與新式 XRef 串流。
混合參照的問題
混合檔案在理論上是有效的,但許多 PDF 產生器 (尤其是舊版 Office 套件中傳統的「儲存為 PDF」外掛程式) 的寫入方式並不正確。常見的錯誤包括將錯誤的位元組偏移量寫入 `startxref` 指標,或建立脫節的物件串流,導致 XRef 表格指向錯誤的生成編號 (generation number)。
如果您的 Delphi 應用程式嘗試使用嚴格的解析器來讀取格式不良的混合 PDF,解析器將會失敗並拋出「Corrupt XRef table」或「Invalid Object Number」例外。
使用 PDFium 處理混合備援
PDFium 引擎 (最初由 Foxit 開發並由 Google 開源) 對於格式不良的 PDF 具有極高的容錯能力。當它偵測到損壞的 XRef 表格時,會自動從檔案結尾 (EOF) 向後掃描,以尋找替代的 XRefStm。
在 Delphi 中使用 PDFium 時,您不需要手動解析結尾字典 (trailer dictionaries)。不過,您應該檢查結構性警告,以便通知使用者或記錄問題。
uses
System.SysUtils, pdfium_lib;
procedure LoadAndCheckHybridPDF(const FileName: string);
var
Doc: FPDF_DOCUMENT;
LastError: ULONG;
begin
FPDF_InitLibrary();
try
Doc := FPDF_LoadDocument(PAnsiChar(AnsiString(FileName)), nil);
if Doc = nil then
begin
LastError := FPDF_GetLastError();
case LastError of
FPDF_ERR_FILE: Writeln('File not found or could not be opened.');
FPDF_ERR_FORMAT: Writeln('File not in PDF format or corrupted.');
FPDF_ERR_PASSWORD: Writeln('Password required or incorrect password.');
FPDF_ERR_SECURITY: Writeln('Unsupported security scheme.');
FPDF_ERR_XFDF: Writeln('Invalid XRef or Hybrid Reference structure.');
else
Writeln('Unknown error occurred loading PDF.');
end;
Exit;
end;
Writeln('PDF loaded successfully despite hybrid or structural anomalies.');
// Proceed with processing...
FPDF_CloseDocument(Doc);
finally
FPDF_DestroyLibrary();
end;
end;
修復與重建 PDF
如果您的工作流程需要將 PDF 傳遞給更嚴格的下游系統 (例如較舊的硬體 RIP),您必須「平面化」(flatten) 混合結構。在 Delphi 中修復損壞的混合 PDF,最可靠的方法是將其載入具容錯能力的引擎並執行另存新檔作業。這會強制解析器從記憶體中的物件樹重建一個乾淨、統一的 XRef 表格。
// Conceptual example using a high-level wrapper
procedure RebuildPdfStructure(const InputFile, OutputFile: string);
var
Doc: TlxPDFDocument;
begin
Doc := TlxPDFDocument.Create;
try
// Tolerant engine ignores the broken hybrid XRef and walks the objects
Doc.LoadFromFile(InputFile);
// Saving rewrites the file with a clean PDF 1.7 XRef stream
Doc.SaveToFile(OutputFile);
Writeln('PDF structure successfully rebuilt.');
finally
Doc.Free;
end;
end;
了解並預期混合參照損壞,可確保您的文件處理管線保持彈性,即使面對數十年前的舊版檔案也是如此。
備註:PDFium Component VCL 元件 完全支援混合參照解析與自動結構修復。