有界的交叉引用恢复

当 PDF 的 startxref 声明缺失、无效或过时,或者 xref 行不可用时,HotPDF 可以恢复真实的对象图和页面树

恢复序列

  1. 在默认的 xrrmOnFailure 模式下,首先运行正常的符合规范的加载器
  2. 恢复以 1 MiB 的分块扫描源,并在分块之间保留少量带数据,记录已验证的间接对象和 xref 锚点
  3. 嵌套在已验证间接对象内部的标记会被拒绝,包括字符串、注释、字典以及包含诸如 99 0 obj 或 xref
  4. 从最新到最旧,通过现有的增量、混合和对象流加载器尝试有效的 xref 表或流
  5. 最多保留 4,096 个 xref 节锚点,防止对抗性的标记重复成倍增加解析尝试
  6. 如果没有 xref 节产生真实的页面树,则重建直接对象偏移,重复的对象编号使用最新的物理定义
  7. 仅当真实的 Catalog、Pages 树和至少一个页面叶子解析成功时,恢复才算成功

声明

type
  THPDFXRefRecoveryMode = (
    xrrmDisabled,
    xrrmOnFailure,
    xrrmForce
  );

  THPDFXRefRecoveryStatus = (
    xrrsNotNeeded,
    xrrsSucceeded,
    xrrsDisabled,
    xrrsScanLimitExceeded,
    xrrsObjectLimitExceeded,
    xrrsXRefLimitExceeded,
    xrrsFailed
  );

  THPDFXRefRecoveryMethod = (
    xrrmethodNone,
    xrrmethodRelocatedSection,
    xrrmethodRebuiltObjectOffsets
  );

  THPDFXRefMergeInfo = record
    HybridRevisionCount: Integer;
    HybridOverlapCount: Integer;
    OlderRevisionShadowedEntryCount: Integer;
    DuplicateEntryCount: Integer;
    NewestRevisionPrecedenceApplied: boolean;
    HybridStreamPrecedenceApplied: boolean;
  end;

property XRefRecoveryMode: THPDFXRefRecoveryMode;
property XRefRecoveryMaxScanBytes: Int64;
property XRefRecoveryMaxObjects: Integer;

function GetLoadedXRefRecoveryInfo(
  out Info: THPDFXRefRecoveryInfo
): boolean;

function GetLoadedXRefMergeInfo(
  out Info: THPDFXRefMergeInfo
): boolean;

配置

成员默认值用途
XRefRecoveryModexrrmOnFailure禁用恢复、仅在正常解析失败后运行恢复,或在正常解析之前强制运行恢复
XRefRecoveryMaxScanBytes8 GiB分块恢复扫描接受的硬性最大源大小
XRefRecoveryMaxObjects2,000,000恢复接受的已验证间接对象候选的硬性上限
ParserRecoveryEventLimit1,024单次加载保留的结构化事件上限,零表示禁用事件保留

恢复信息

GetLoadedXRefRecoveryInfo 返回 True 并复制最近的 THPDFXRefRecoveryInfo 记录

合并优先级信息

GetLoadedParserRecoveryEvents 返回调用方持有的事件流快照;GetLoadedParserRecoveryEvent 读取单条而不分配数组拷贝

合并优先级信息

GetLoadedXRefMergeInfo 返回 True当 xref-stream 或混合引用加载遇到值得报告的合并状态时

保存已恢复的文档

SaveLoadedDocument 报告活动空闲链、无效链接、环、未链接的空闲条目、generation 迁移错误、重复存活定义,以及被最新修订遮蔽的较旧条目

遮蔽细节包含活动与被拒条目类型、偏移或存储字段、generation 与修订深度,细节上限固定 256 条

保存已恢复的文档

恢复出的文档在 SaveLoadedDocument 中始终走完整重写路径,即使没有任何对象被编辑——HotPDF 绝不会把损坏的源字节复制成看似已修复的输出

示例

var
  Info: THPDFXRefRecoveryInfo;
  PDF: THotPDF;
begin
  PDF := THotPDF.Create(nil);
  try
    PDF.XRefRecoveryMode := xrrmOnFailure;
    PDF.XRefRecoveryMaxScanBytes := Int64(16) * 1024 * 1024 * 1024;
    PDF.XRefRecoveryMaxObjects := 3000000;
    if PDF.LoadFromFile('damaged.pdf') > 0 then
    begin
      if PDF.GetLoadedXRefRecoveryInfo(Info) then
        WriteLn(Info.Diagnostic);
      PDF.SaveLoadedDocument('recovered.pdf');
    end;
  finally
    PDF.Free;
  end;
end;

相关 API