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
- O loader normal compatível com a spec roda primeiro no modo padrão
xrrmOnFailure - 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
- 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 objouxref - 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
- 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
- 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
- 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
| Membro | Default | Finalidade |
|---|---|---|
XRefRecoveryMode | xrrmOnFailure | Desativa a recuperação, roda-a apenas depois que o parse normal falha, ou força-a antes do parse normal |
XRefRecoveryMaxScanBytes | 8 GiB | Tamanho máximo rígido de origem aceito pelo scan de recuperação em chunks |
XRefRecoveryMaxObjects | 2,000,000 | Máximo rígido de candidatos a objetos indiretos validados aceitos pela recuperação |
ParserRecoveryEventLimit | 1,024 | Má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
Statusdistingue sucesso, recuperação desativada, falha por limite de bytes, falha por limite de objetos, falha por limite de âncoras de xref e falha estruturalMethodidentifica uma seção de xref realocada ou offsets de objetos diretos reconstruídosBytesScanned,CandidateObjectCount,RecoveredObjectCount,DuplicateObjectCount,RejectedCandidateCounteXRefCandidateCountexpõem o trabalho limitado e a qualidade da recuperaçãoSelectedXRefOffsetidentifica a seção realocada quando esse método tem sucessoDiagnosticcontém uma descrição concisa do resultado em inglês
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
Kinddistingue eventos de tentativa, política, limite, candidato a objeto, candidato a xref, seção selecionada, offsets reconstruídos, sucesso e falhaFirstByteé inclusivo eEndByteé exclusivo, com ambos definidos como-1quando não há faixa de origemObjectNumbereGenerationNumberidentificam candidatos a objetos indiretos e usam-1para eventos no nível do documento ou de âncoras de xrefConfidenceprogride deprcNonepelos resultados low, medium, high e verifiedLoadedParserRecoveryEventDroppedCountreporta os eventos omitidos após o limite configurado de retenção, preservando memória limitada diante de entradas hostis
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
HybridRevisionCountconta revisões tradicionais que contribuíram com um xref stream lateral via/XRefStmHybridOverlapCountconta entradas de tabela já fornecidas pelo xref stream da mesma revisão, que tem precedênciaOlderRevisionShadowedEntryCountconta entradas ignoradas porque uma revisão mais recente já definiu aquele número de objetoDuplicateEntryCountconta entradas repetidas dentro da seção não híbrida mais recente- As duas flags de precedência indicam se a regra correspondente foi exercida, e não apenas disponível
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;