Ограниченное восстановление перекрёстных ссылок

HotPDF может восстановить реальный граф объектов и дерево страниц, если PDF имеет отсутствующее, недопустимое или устаревшее объявление startxref или непригодные строки xref

Последовательность восстановления

  1. Сначала выполняется обычный соответствующий спецификации загрузчик в режиме по умолчанию xrrmOnFailure
  2. Восстановление сканирует источник порциями по 1 МиБ с коротким переносом между порциями и фиксирует проверенные косвенные объекты и якоря xref
  3. Маркеры, вложенные в проверенный косвенный объект, отклоняются, включая строки, комментарии, словари и бинарные данные потоков, содержащие текст вида 99 0 obj или xref
  4. Корректные таблицы или потоки xref проверяются от новой к более старой через существующий инкрементный, гибридный загрузчик и загрузчик объектных потоков
  5. Сохраняется не более 4096 якорей разделов xref, что предотвращает умножение попыток разбора из-за враждебного повторения маркеров
  6. Если ни один раздел xref не даёт реального дерева страниц, смещения прямых объектов перестраиваются, а при дублировании номеров объектов используется последнее физическое определение
  7. Восстановление завершается успешно, только если разрешаются реальные 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;

Конфигурация

ЭлементПо умолчаниюНазначение
XRefRecoveryModexrrmOnFailureОтключает восстановление, запускает его только после сбоя обычного разбора или форсирует его до обычного разбора
XRefRecoveryMaxScanBytes8 GiBЖёсткий максимальный размер источника, принимаемый кусочным восстановительным сканом
XRefRecoveryMaxObjects2,000,000Жёсткий максимум валидированных кандидатов в непрямые объекты, принимаемый восстановлением
ParserRecoveryEventLimit1,024Максимум структурированных событий, удерживаемых для одной загрузки; ноль отключает удержание событий

Информация о восстановлении

GetLoadedXRefRecoveryInfo возвращает True, если восстановление было предпринято, и копирует последнюю запись THPDFXRefRecoveryInfo

Информация о приоритете слияния

GetLoadedXRefMergeInfo возвращает True, если при загрузке потоков xref или гибридных ссылок встретилось состояние слияния, о котором стоит сообщить

Информация о приоритете слияния

GetLoadedXRefMergeInfo возвращает True когда загрузка xref stream или гибридных ссылок встретила заслуживающее отчёта состояние слияния

Информация о целостности

GetLoadedXRefIntegrityInfo сообщает активную цепочку free list, неверные ссылки, циклы, несвязанные свободные записи, ошибки переходов поколений, дублирующиеся живые определения и старые записи, затенённые новейшей ревизией

Детали затенения включают активные и отклонённые типы записей, смещения или поля хранилища, поколения и глубину ревизии с фиксированным потолком деталей в 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