При разработке корпоративных решений для работы с документами вы неизбежно столкнетесь с PDF-файлами, созданными огромным множеством различных инструментов: от высококлассного программного обеспечения Adobe до нестабильных библиотек с открытым исходным кодом или драйверов виртуальных принтеров. Одной из самых известных структурных проблем, с которыми вы столкнетесь, является PDF с «гибридными ссылками» (hybrid-reference PDF).
В этой статье мы объясним, что такое гибридные ссылки, почему некоторые офисные приложения генерируют их, и как программно парсить и восстанавливать эти структуры с помощью Delphi и надежных библиотек PDF.
Эволюция таблицы перекрестных ссылок PDF
Чтобы быстро находить объекты (шрифты, изображения, страницы) без синтаксического анализа всего файла, PDF использует таблицу перекрестных ссылок (XRef). В ранних спецификациях PDF (PDF 1.4 и старше) это была обычная текстовая таблица ASCII в конце файла.
Начиная с PDF 1.5, компания Adobe представила потоки перекрестных ссылок (XRefStm), которые сжимали данные перекрестных ссылок в двоичный поток, что значительно уменьшало размер файлов. Однако для обратной совместимости с более старыми программами для чтения PDF некоторые генераторы начали создавать PDF с гибридными ссылками. Такие файлы содержат как таблицу XRef в старом формате ASCII, так и поток XRef в новом стиле.
Проблема с гибридными ссылками
Гибридные файлы теоретически допустимы, но многие генераторы PDF (особенно устаревшие плагины «Сохранить в PDF» в старых офисных пакетах) записывают их некорректно. Распространенной ошибкой является запись неверного смещения в байтах для указателя `startxref` или создание разрозненных потоков объектов, где таблица XRef указывает на неверный номер поколения.
Если ваше приложение Delphi попытается прочитать плохо сформированный гибридный PDF с помощью строгого парсера, этот парсер завершит работу с исключением «Corrupt XRef table» (Поврежденная таблица XRef) или «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), вам необходимо «свести» (flatten) гибридную структуру. Самый надежный способ исправить сломанный гибридный 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 VCL.