Bounded Cross-Reference Recovery
PDF에 없거나 잘못되었거나 오래된 startxref 선언이나 쓸모없는 xref 행이 있어도 HotPDF는 실제 객체 그래프와 페이지 트리를 복구할 수 있습니다
Recovery sequence
- 기본
xrrmOnFailure모드에서는 평범한 사양 준수 로더가 먼저 실행됩니다 - 복구는 청크 사이 짧은 carry를 두고 1MiB 청크로 소스를 스캔해 검증된 indirect 객체와 xref anchor를 기록합니다
- 검증된 indirect 객체 안에 중첩된 마커는 거부됩니다.
99 0 obj나xref같은 텍스트를 담은 문자열, 주석, 딕셔너리, 바이너리 stream 데이터가 그 대상입니다 - 유효한 xref 테이블/stream은 기존 증분, hybrid, object-stream 로더로 최신부터 오래된 순으로 시도됩니다
- xref section anchor는 최대 4,096개만 유지되어 적대적 마커 반복이 파싱 시도를 늘리지 못합니다
- xref section이 실제 페이지 트리를 만들지 못하면 direct-object 오프셋을 다시 만들고, 중복 객체 번호는 최신 물리 정의를 씁니다
- 복구는 실제 Catalog, Pages 트리, 최소 한 개의 page leaf가 해석될 때만 성공합니다
선언
type
THPDFXRefRecoveryMode = (
xrrmDisabled,
xrrmOnFailure,
xrrmForce
);
THPDFXRefRecoveryStatus = (
xrrsNotNeeded,
xrrsSucceeded,
xrrsDisabled,
xrrsScanLimitExceeded,
xrrsObjectLimitExceeded,
xrrsXRefLimitExceeded,
xrrsFailed
);
THPDFXRefRecoveryMethod = (
xrrmethodNone,
xrrmethodRelocatedSection,
xrrmethodRebuiltObjectOffsets
);
THPDFParserRecoveryConfidence = (
prcNone,
prcLow,
prcMedium,
prcHigh,
prcVerified
);
THPDFParserRecoveryEvent = record
Kind: THPDFParserRecoveryEventKind;
Confidence: THPDFParserRecoveryConfidence;
FirstByte: Int64;
EndByte: Int64;
ObjectNumber: Integer;
GenerationNumber: Integer;
Message: AnsiString;
end;
THPDFXRefMergeInfo = record
HybridRevisionCount: Integer;
HybridOverlapCount: Integer;
OlderRevisionShadowedEntryCount: Integer;
DuplicateEntryCount: Integer;
NewestRevisionPrecedenceApplied: boolean;
HybridStreamPrecedenceApplied: boolean;
end;
property XRefRecoveryMode: THPDFXRefRecoveryMode;
property XRefRecoveryMaxScanBytes: Int64;
property XRefRecoveryMaxObjects: Integer;
property ParserRecoveryEventLimit: Integer;
property LoadedParserRecoveryEventCount: Integer;
property LoadedParserRecoveryEventDroppedCount: Integer;
function GetLoadedXRefRecoveryInfo(
out Info: THPDFXRefRecoveryInfo
): boolean;
function GetLoadedParserRecoveryEvents:
THPDFParserRecoveryEventArray;
function GetLoadedParserRecoveryEvent(
Index: Integer;
out EventInfo: THPDFParserRecoveryEvent
): boolean;
function GetLoadedXRefMergeInfo(
out Info: THPDFXRefMergeInfo
): boolean;
function GetLoadedXRefIntegrityInfo(
out Info: THPDFXRefIntegrityInfo
): boolean;
구성
| 멤버 | 기본값 | 목적 |
|---|---|---|
XRefRecoveryMode | xrrmOnFailure | 복구를 끄거나, 일반 파싱이 실패한 뒤에만 돌리거나, 일반 파싱 전에 강제합니다 |
XRefRecoveryMaxScanBytes | 8 GiB | 청크 복구 스캔이 받아들이는 하드 최대 소스 크기 |
XRefRecoveryMaxObjects | 2,000,000 | 복구가 받아들이는 하드 최대 검증 indirect-object 후보 수 |
ParserRecoveryEventLimit | 1,024 | 한 번 로드에 유지되는 최대 구조화 이벤트 수, 0이면 이벤트 보존을 끕니다 |
Recovery information
GetLoadedXRefRecoveryInfo는 복구를 시도했으면 True를 반환하며 가장 최근 THPDFXRefRecoveryInfo 레코드를 복사합니다
Status는 성공, 꺼진 복구, 바이트 한도 실패, 객체 한도 실패, xref anchor 한도 실패, 구조 실패를 구분합니다Method는 재배치된 xref section인지 재구축된 direct-object 오프셋인지 식별합니다BytesScanned,CandidateObjectCount,RecoveredObjectCount,DuplicateObjectCount,RejectedCandidateCount,XRefCandidateCount는 상한 작업량과 복구 품질을 노출합니다SelectedXRefOffset은 그 방식이 성공했을 때 재배치된 section을 식별합니다Diagnostic은 간결한 영어 결과 설명을 담습니다
구조화 복구 이벤트
GetLoadedParserRecoveryEvents는 보유 이벤트 stream의 호출자 소유 스냅샷을 반환하고, GetLoadedParserRecoveryEvent는 배열 사본 할당 없이 항목 하나를 읽습니다
Kind는 시도, policy, 한도, 객체 후보, xref 후보, 선택된 section, 재구축 오프셋, 성공, 실패 이벤트를 구분합니다FirstByte는 포함,EndByte는 배타이며, 소스 범위가 없으면 둘 다-1로 설정됩니다ObjectNumber와GenerationNumber는 indirect 객체 후보를 식별하고 문서 수준/xref anchor 이벤트에는-1을 씁니다Confidence는prcNone부터 low, medium, high, verified 결과로 진행됩니다LoadedParserRecoveryEventDroppedCount는 설정 보존 한도 뒤에 생략된 이벤트를 보고해 적대적 입력에서도 메모리를 상태 안에 유지합니다
병합 우선순위 정보
GetLoadedXRefMergeInfo는 xref-stream/hybrid-reference 로딩이 보고할 만한 병합 상태를 만나면 True를 반환합니다
HybridRevisionCount는/XRefStm으로 보조 xref stream을 기여한 전통 revision을 셉니다HybridOverlapCount는 우선권을 가진 같은 revision의 xref stream이 이미 공급한 테이블 항목을 셉니다OlderRevisionShadowedEntryCount는 더 새로운 revision이 이미 그 객체 번호를 정의해 무시된 항목을 셉니다DuplicateEntryCount는 hybrid가 아닌 가장 새로운 section 안의 반복 항목을 셉니다- 두 우선순위 플래그는 해당 규칙이 단순히 존재한 게 아니라 실제 적용됐는지를 나타냅니다
Integrity information
GetLoadedXRefIntegrityInfo는 활성 free-list 체인, 잘못된 링크, 순환, 연결 안 된 free 항목, generation 전이 오류, 중복 live 정의, 최신 revision에 가려진 오래된 항목을 보고합니다
shadow 상세에는 활성/거부 항목 타입, 오프셋 또는 저장 필드, 세대, revision 깊이가 담기며 상한은 고정 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;