有界的交叉引用恢复
当 PDF 的 startxref 声明缺失、无效或过时,或者 xref 行不可用时,HotPDF 可以恢复真实的对象图和页面树
恢复序列
- 在默认的
xrrmOnFailure模式下,首先运行正常的符合规范的加载器 - 恢复以 1 MiB 的分块扫描源,并在分块之间保留少量带数据,记录已验证的间接对象和 xref 锚点
- 嵌套在已验证间接对象内部的标记会被拒绝,包括字符串、注释、字典以及包含诸如
99 0 obj或xref - 从最新到最旧,通过现有的增量、混合和对象流加载器尝试有效的 xref 表或流
- 最多保留 4,096 个 xref 节锚点,防止对抗性的标记重复成倍增加解析尝试
- 如果没有 xref 节产生真实的页面树,则重建直接对象偏移,重复的对象编号使用最新的物理定义
- 仅当真实的 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;
配置
| 成员 | 默认值 | 用途 |
|---|---|---|
XRefRecoveryMode | xrrmOnFailure | 禁用恢复、仅在正常解析失败后运行恢复,或在正常解析之前强制运行恢复 |
XRefRecoveryMaxScanBytes | 8 GiB | 分块恢复扫描接受的硬性最大源大小 |
XRefRecoveryMaxObjects | 2,000,000 | 恢复接受的已验证间接对象候选的硬性上限 |
ParserRecoveryEventLimit | 1,024 | 单次加载保留的结构化事件上限,零表示禁用事件保留 |
恢复信息
GetLoadedXRefRecoveryInfo 返回 True 并复制最近的 THPDFXRefRecoveryInfo 记录
Status区分成功、已禁用的恢复、字节限制失败、对象限制失败、xref 锚点限制失败和结构性失败Method标识重新定位的 xref 节或重建的直接对象偏移BytesScanned、CandidateObjectCount、RecoveredObjectCount、DuplicateObjectCount、RejectedCandidateCount和XRefCandidateCount公开有界的工作量和恢复质量SelectedXRefOffset在该方法成功时标识重新定位的节Diagnostic包含简洁的英文结果描述
合并优先级信息
GetLoadedParserRecoveryEvents 返回调用方持有的事件流快照;GetLoadedParserRecoveryEvent 读取单条而不分配数组拷贝
Kind区分尝试、策略、限制、对象候选、xref 候选、选定区段、重建偏移、成功与失败事件FirstByte为闭区间端,EndByte为开区间端;不存在源区间时两者均为-1ObjectNumber与GenerationNumber标识间接对象候选;文档级或 xref 锚事件用-1Confidence从prcNone依次到 low、medium、high 与 verifiedLoadedParserRecoveryEventDroppedCount报告超出保留上限后省略的事件数,在恶意输入下保持有界内存
合并优先级信息
GetLoadedXRefMergeInfo 返回 True当 xref-stream 或混合引用加载遇到值得报告的合并状态时
HybridRevisionCount统计通过/XRefStmHybridOverlapCount统计已由同一修订版本的 xref 流提供的表条目数,该流具有优先权OlderRevisionShadowedEntryCount统计因为较新的修订版本已经定义了该对象编号而被忽略的条目数DuplicateEntryCount统计最新的非混合节内部重复的条目数- 这两个优先级标志说明相应的规则是否被实际执行,而不仅仅是可用
保存已恢复的文档
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;