Bounded Cross-Reference Recovery

O HotPDF consegue recuperar um grafo de objetos e uma árvore de páginas reais quando um PDF tem uma declaração startxref ausente, inválida ou desatualizada, ou linhas de xref inutilizáveis

Sequência de recuperação

  1. O loader normal compatível com a spec roda primeiro no modo padrão xrrmOnFailure
  2. A recuperação escaneia a origem em chunks de 1 MiB com um carry curto entre chunks e registra objetos indiretos e âncoras de xref validados
  3. Marcadores aninhados dentro de um objeto indireto validado são rejeitados, incluindo strings, comentários, dicionários e dados binários de stream contendo texto como 99 0 obj ou xref
  4. Tabelas ou streams de xref válidos são tentados do mais novo ao mais antigo pelo loader existente de incremental, híbrido e object streams
  5. No máximo 4.096 âncoras de seção de xref são retidas, impedindo que repetição adversarial de marcadores multiplique as tentativas de parse
  6. Se nenhuma seção de xref produzir uma árvore de páginas real, os offsets de objetos diretos são reconstruídos e números de objeto duplicados usam a definição física mais recente
  7. A recuperação só tem sucesso quando um Catalog real, uma árvore Pages e pelo menos uma folha de página resolvem

Declarações

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;

Configuration

MembroDefaultFinalidade
XRefRecoveryModexrrmOnFailureDesativa a recuperação, roda-a apenas depois que o parse normal falha, ou força-a antes do parse normal
XRefRecoveryMaxScanBytes8 GiBTamanho máximo rígido de origem aceito pelo scan de recuperação em chunks
XRefRecoveryMaxObjects2,000,000Máximo rígido de candidatos a objetos indiretos validados aceitos pela recuperação
ParserRecoveryEventLimit1,024Máximo de eventos estruturados retidos para um carregamento, com zero desativando a retenção de eventos

Informações de recuperação

GetLoadedXRefRecoveryInfo retorna True quando a recuperação foi tentada e copia o registro THPDFXRefRecoveryInfo mais recente

Eventos estruturados de recuperação

GetLoadedParserRecoveryEvents retorna um snapshot de posse do chamador do stream de eventos retido, enquanto GetLoadedParserRecoveryEvent lê uma entrada sem alocar uma cópia do array

Informações de precedência de merge

GetLoadedXRefMergeInfo retorna True quando o carregamento de xref stream ou referência híbrida encontrou estado de merge que vale reportar

Informações de integridade

GetLoadedXRefIntegrityInfo reporta a cadeia ativa de free lists, links inválidos, ciclos, entradas livres não ligadas, erros de transição de geração, definições vivas duplicadas e entradas mais antigas sombreadas pela revisão mais nova

O detalhe sombreado inclui tipos de entrada ativos e rejeitados, offsets ou campos de armazenamento, gerações e profundidade de revisão, com um teto fixo de 256 entradas de detalhe

Salvando documentos recuperados

Um documento recuperado sempre usa o caminho de reescrita completa em SaveLoadedDocument, mesmo quando nenhum objeto foi editado, para que o HotPDF nunca copie bytes danificados da origem como uma saída aparentemente reparada

Exemplo

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;

APIs relacionadas