Technical Article

Обработка на хибридни референтни PDF файлове от Office приложения в Delphi

Когато разработвате корпоративни решения за документи, неизбежно ще срещнете PDF файлове, генерирани от огромно разнообразие от инструменти – от софтуер от висок клас на Adobe до пълни с грешки библиотеки с отворен код или виртуални драйвери за принтер. Един от най-известните структурни проблеми, с които ще се сблъскате, е "хибридният референтен" (hybrid-reference) PDF.

В тази статия ще обясним какво представляват хибридните препратки, защо определени офис приложения ги генерират и как програмно да анализирате и поправяте тези структури, използвайки Delphi и надеждни PDF библиотеки.

Еволюцията на PDF таблицата за кръстосани препратки

За да намира обекти (шрифтове, изображения, страници) бързо, без да анализира целия файл, PDF използва таблица за кръстосани препратки (Cross-Reference Table или XRef). В по-ранните PDF спецификации (PDF 1.4 и по-стари), това беше буквална ASCII текстова таблица в края на файла.

Започвайки с PDF 1.5, Adobe въведе потоци за кръстосани препратки (XRefStm), които компресираха данните за кръстосаните препратки в двоичен поток, значително намалявайки размерите на файловете. Въпреки това, за обратна съвместимост с по-стари PDF четци, някои генератори започнаха да създават хибридни референтни PDF файлове. Тези файлове съдържат както ASCII XRef таблица в стар стил, така и XRef поток в нов стил.

Проблемът с хибридните препратки

Хибридните файлове са теоретично валидни, но много PDF генератори (особено наследените плъгини "Save to PDF" в по-старите офис пакети) ги записват неправилно. Често срещана грешка е записването на неправилно отместване в байтове за указателя `startxref` или създаването на несвързани обектни потоци, при които XRef таблицата сочи към грешен номер на генерация.

Ако вашето Delphi приложение се опита да прочете лошо форматиран хибриден PDF с помощта на стриктен парсер, парсерът ще се провали с изключение "Corrupt XRef table" или "Invalid Object Number".

Обработка на хибридни резервни варианти с PDFium

Двигателят PDFium (първоначално разработен от Foxit и направен с отворен код от Google) е силно толерантен към неправилно форматирани PDF файлове. Когато открие повредена XRef таблица, той автоматично сканира файла назад от края на файла, за да намери алтернативния 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 е да го заредите в толерантен двигател и да извършите операция "Save-As". Това принуждава парсера да възстанови чиста, унифицирана 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.