Bounded Cross-Reference Recovery

PDF에 없거나 잘못되었거나 오래된 startxref 선언이나 쓸모없는 xref 행이 있어도 HotPDF는 실제 객체 그래프와 페이지 트리를 복구할 수 있습니다

Recovery sequence

  1. 기본 xrrmOnFailure 모드에서는 평범한 사양 준수 로더가 먼저 실행됩니다
  2. 복구는 청크 사이 짧은 carry를 두고 1MiB 청크로 소스를 스캔해 검증된 indirect 객체와 xref anchor를 기록합니다
  3. 검증된 indirect 객체 안에 중첩된 마커는 거부됩니다. 99 0 obj나 xref 같은 텍스트를 담은 문자열, 주석, 딕셔너리, 바이너리 stream 데이터가 그 대상입니다
  4. 유효한 xref 테이블/stream은 기존 증분, hybrid, object-stream 로더로 최신부터 오래된 순으로 시도됩니다
  5. xref section anchor는 최대 4,096개만 유지되어 적대적 마커 반복이 파싱 시도를 늘리지 못합니다
  6. xref section이 실제 페이지 트리를 만들지 못하면 direct-object 오프셋을 다시 만들고, 중복 객체 번호는 최신 물리 정의를 씁니다
  7. 복구는 실제 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;

구성

멤버기본값목적
XRefRecoveryModexrrmOnFailure복구를 끄거나, 일반 파싱이 실패한 뒤에만 돌리거나, 일반 파싱 전에 강제합니다
XRefRecoveryMaxScanBytes8 GiB청크 복구 스캔이 받아들이는 하드 최대 소스 크기
XRefRecoveryMaxObjects2,000,000복구가 받아들이는 하드 최대 검증 indirect-object 후보 수
ParserRecoveryEventLimit1,024한 번 로드에 유지되는 최대 구조화 이벤트 수, 0이면 이벤트 보존을 끕니다

Recovery information

GetLoadedXRefRecoveryInfo는 복구를 시도했으면 True를 반환하며 가장 최근 THPDFXRefRecoveryInfo 레코드를 복사합니다

구조화 복구 이벤트

GetLoadedParserRecoveryEvents는 보유 이벤트 stream의 호출자 소유 스냅샷을 반환하고, GetLoadedParserRecoveryEvent는 배열 사본 할당 없이 항목 하나를 읽습니다

병합 우선순위 정보

GetLoadedXRefMergeInfo는 xref-stream/hybrid-reference 로딩이 보고할 만한 병합 상태를 만나면 True를 반환합니다

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;

관련 API