Technical Article

Обробка гібридних PDF-файлів з додатків Office у Delphi

Під час розробки корпоративних рішень для роботи з документами ви неминуче зіткнетеся з PDF-файлами, створеними за допомогою безлічі різних інструментів: від висококласного програмного забезпечення Adobe до помилкових бібліотек з відкритим вихідним кодом або драйверів віртуальних принтерів. Однією з найвідоміших структурних проблем, з якими ви зіткнетеся, є гібридний PDF-файл (hybrid-reference).

У цій статті ми пояснимо, що таке гібридні посилання, чому певні офісні програми створюють їх, а також як програмно аналізувати та виправляти ці структури за допомогою Delphi та надійних бібліотек PDF.

Еволюція таблиці перехресних посилань PDF

Щоб швидко знаходити об'єкти (шрифти, зображення, сторінки) без синтаксичного аналізу всього файлу, PDF використовує таблицю перехресних посилань (XRef). У попередніх специфікаціях PDF (PDF 1.4 і старіших) це була буквальна текстова таблиця ASCII в кінці файлу.

Починаючи з PDF 1.5, компанія Adobe представила потоки перехресних посилань (XRefStm), які стискали дані перехресних посилань у двійковий потік, значно зменшуючи розміри файлів. Однак для зворотної сумісності зі старішими програмами для читання PDF деякі генератори почали створювати гібридні PDF-файли (Hybrid-Reference PDFs). Ці файли містять як таблицю XRef у старому стилі ASCII, так і потік XRef у новому стилі.

Проблема з гібридними посиланнями

Гібридні файли теоретично є дійсними, але багато генераторів PDF (особливо застарілі плагіни "Зберегти як PDF" у старих офісних пакетах) записують їх неправильно. Поширеною помилкою є запис неправильного зміщення байтів для покажчика `startxref` або створення розрізнених потоків об'єктів, де таблиця XRef вказує на неправильний номер покоління.

Якщо ваш додаток Delphi намагається прочитати погано сформований гібридний PDF-файл за допомогою суворого аналізатора, аналізатор видасть виняток "Corrupt XRef table" або "Invalid Object Number".

Обробка гібридних резервних варіантів за допомогою PDFium

Рушій PDFium (спочатку розроблений компанією Foxit і відкритий Google) є дуже толерантним до неправильно сформованих PDF-файлів. Коли він виявляє пошкоджену таблицю XRef, він автоматично сканує файл у зворотному напрямку від кінця файлу (EOF), щоб знайти альтернативний XRefStm.

У Delphi при роботі з PDFium вам не потрібно вручну аналізувати словники трейлера. Однак вам слід перевіряти наявність структурних попереджень, щоб мати змогу попередити користувача або зареєструвати проблему.

uses
  System.SysUtils, pdfium_lib;

procedure LoadAndCheckHybridPDF(const FileName: string);
var
  Doc: FPDF_DOCUMENT;
  LastError: ULONG;
begin
  FPDF_InitLibrary();
  try
    Doc := FPDF_LoadDocument(PAnsiChar(AnsiString(FileName)), nil);
    if Doc = nil then
    begin
      LastError := FPDF_GetLastError();
      case LastError of
        FPDF_ERR_FILE: Writeln('File not found or could not be opened.');
        FPDF_ERR_FORMAT: Writeln('File not in PDF format or corrupted.');
        FPDF_ERR_PASSWORD: Writeln('Password required or incorrect password.');
        FPDF_ERR_SECURITY: Writeln('Unsupported security scheme.');
        FPDF_ERR_XFDF: Writeln('Invalid XRef or Hybrid Reference structure.');
      else
        Writeln('Unknown error occurred loading PDF.');
      end;
      Exit;
    end;
    
    Writeln('PDF loaded successfully despite hybrid or structural anomalies.');
    
    // Proceed with processing...
    
    FPDF_CloseDocument(Doc);
  finally
    FPDF_DestroyLibrary();
  end;
end;

Виправлення та відновлення PDF-файлу

Якщо ваш робочий процес вимагає передачі PDF-файлу до більш суворої наступної системи (наприклад, старішого апаратного RIP), вам потрібно "згладити" гібридну структуру. Найнадійніший спосіб виправити пошкоджений гібридний PDF-файл у Delphi: завантажити його в толерантний рушій і виконати операцію "Зберегти як". Це змушує аналізатор відновити чисту уніфіковану таблицю XRef з дерева об'єктів у пам'яті.

// Conceptual example using a high-level wrapper
procedure RebuildPdfStructure(const InputFile, OutputFile: string);
var
  Doc: TlxPDFDocument;
begin
  Doc := TlxPDFDocument.Create;
  try
    // Tolerant engine ignores the broken hybrid XRef and walks the objects
    Doc.LoadFromFile(InputFile);
    
    // Saving rewrites the file with a clean PDF 1.7 XRef stream
    Doc.SaveToFile(OutputFile);
    Writeln('PDF structure successfully rebuilt.');
  finally
    Doc.Free;
  end;
end;

Розуміння та передбачення пошкоджень гібридних посилань гарантує, що ваші конвеєри обробки документів залишатимуться стійкими навіть при зіткненні із застарілими файлами десятирічної давності.

Примітка: Вирішення гібридних посилань та автоматичне відновлення структури повністю підтримуються компонентом PDFium Component.