在开发企业级文档解决方案时,您不可避免地会遇到由各种工具生成的 PDF 文件,从高端的 Adobe 软件到存在缺陷的开源库,再到虚拟打印机驱动程序。您将面临的最臭名昭著的结构问题之一就是“混合引用(hybrid-reference)”PDF。
在本文中,我们将解释什么是混合引用,为什么某些办公应用程序会生成它们,以及如何使用 Delphi 和稳健的 PDF 库以编程方式解析和修复这些结构。
PDF 交叉引用表的演变
为了在不解析整个文件的情况下快速定位对象(字体、图像、页面),PDF 使用交叉引用表(XRef)。在早期的 PDF 规范(PDF 1.4 及更早版本)中,这是位于文件末尾的字面 ASCII 文本表。
从 PDF 1.5 开始,Adobe 引入了交叉引用流(XRefStm),它将交叉引用数据压缩为二进制流,从而显著减小了文件大小。然而,为了向后兼容较旧的 PDF 阅读器,一些生成器开始生成混合引用 PDF。这些文件同时包含旧式 ASCII XRef 表和新式 XRef 流。
混合引用的问题
混合文件在理论上是有效的,但许多 PDF 生成器(尤其是旧版 Office 套件中传统的“保存为 PDF”插件)会错误地写入它们。一个常见的错误是为 `startxref` 指针写入了错误的字节偏移量,或者创建了脱节的对象流,其中 XRef 表指向了错误的生成号。
如果您的 Delphi 应用程序尝试使用严格的解析器读取格式不良的混合 PDF,解析器将失败并抛出“损坏的 XRef 表”或“无效的对象编号”异常。
使用 PDFium 处理混合回退
PDFium 引擎(最初由 Foxit 开发并由 Google 开源)对格式不良的 PDF 具有极高的容错能力。当它检测到损坏的 XRef 表时,它会自动从文件末尾(EOF)向后扫描以定位备用的 XRefStm。
在 Delphi 中,当使用 PDFium 时,您无需手动解析 trailer 字典。但是,您应该检查结构性警告,以便您可以提醒用户或记录问题。
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),您需要“展平”混合结构。在 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 完全支持混合引用解析和自动结构修复。