Техническая статья

Экспорт книг Excel в PDF/A из Delphi

HotXLS экспортирует книгу или отдельный лист напрямую в PDF/A из Delphi и C++Builder, без Excel и без драйвера PDF-принтера. Установите UsePDFA и выберите профиль в TXLSPDFExport, и экспортированный файл будет нести встроенный ICC-профиль sRGB в качестве output intent, блок идентификации pdfaid в своих метаданных XMP и заголовок версии PDF, соответствующий уровню соответствия

Это требование обычно приходит из политики хранения записей, а не от разработчика. Финансовая отчётность, результаты лабораторных исследований, регуляторная отчётность и всё, что государственный орган обязан хранить десятилетиями, должно храниться в формате, который отрисовывается одинаково через пятьдесят лет, — именно для этого и существует PDF/A, ISO 19005. Электронная таблица — это живое вычисление; архивная копия таблицы обязана быть её зафиксированной картинкой

Что делает PDF архивным PDF?

Основную работу выполняют три требования. Файл должен быть самодостаточным, поэтому каждый используемый им шрифт встроен и ни один внешний ресурс не упоминается по ссылке. Его цвет должен быть однозначным, а это значит, что output intent называет цветовое пространство, для которого файл подготовлен. И он должен идентифицировать себя через метаданные XMP, объявляющие номер части и уровень соответствия

HotXLS обеспечивает все три требования. Профиль sRGB встраивается как объект потока с тремя компонентами и DeviceRGB в качестве альтернативы, на который ссылается output intent GTS_PDFA1, чьи условие и идентификатор оба называют sRGB IEC61966-2.1. Пакет XMP несёт запись части pdfaid и запись соответствия. Байты профиля скомпилированы прямо в библиотеку, а не загружаются во время выполнения, поэтому нет никакого шага развёртывания и никакого риска, что отсутствующий файл превратит архивный экспорт в обычный

Диаграмма трёх архивных требований PDF/A и аппарата, который пишет HotXLS из Delphi: встроенные субсеты FontFile2, выходное намерение GTS_PDFA1 sRGB и XMP-идентификация pdfaid
Файл PDF/A обязан быть самодостаточным, цветооднозначным и самоидентифицирующимся, и HotXLS пишет соответствующий аппарат из пары свойств UsePDFA
uses
  lxHandleX, lxPDF;

var
  Book: TXLSXWorkbook;
  Exporter: TXLSPDFExport;
begin
  Book := TXLSXWorkbook.Create;
  Exporter := TXLSPDFExport.Create;
  try
    if Book.Open('quarterly-results.xlsx') <> 1 then
      Exit;

    Exporter.UsePDFA := True;
    Exporter.PDFAProfile := xlsPdfA2b;      // ISO 19005-2, уровень B
    Exporter.DocumentLanguage := 'en-GB';
    Exporter.TagMode := xlsPdfTagsAutomatic;

    if Exporter.SaveAsPDF(Book, 'quarterly-results-archive.pdf') <> 1 then
      raise Exception.Create('archival export failed');
  finally
    Exporter.Free;
    Book.Free;
  end;
end;

Какой профиль выбрать?

Перечисление профилей охватывает PDF/A-1, PDF/A-2 и PDF/A-3, каждый в доступном варианте и базовом варианте. Практические различия уже, чем предполагает этот список

Уровень B, базовый вариант, гарантирует воспроизводимость визуального облика. Уровень A добавляет структурные и семантические требования, прежде всего тегированное дерево структуры и определённый порядок чтения, благодаря чему документ доступен для вспомогательных технологий. Часть 2 ослабляет некоторые ограничения части 1 и разрешает потоки объектов и JPEG 2000; часть 3 дополнительно позволяет произвольные встроенные файлы — именно это используют форматы электронного выставления счетов, чтобы нести машиночитаемый XML рядом со страницей, читаемой человеком

Для архивируемой электронной таблицы PDF/A-2b — прагматичный выбор по умолчанию: его широко принимают архивы, он не требует дисциплины тегирования, которую уровень A налагает на сетку ячеек, и он допускает компактные структуры, не позволяющие экспорту книги на 200 страниц раздуться. Выбирайте часть 3, когда архив должен также нести исходные данные в виде вложения, что экспортёр поддерживает через AddAttachment

Шрифтовое правило, на котором спотыкается большинство

Каждый глиф, который рисует файл PDF/A, должен происходить из встроенного шрифта. Это самая частая единственная причина, по которой в остальном корректный экспорт не проходит проверку, и режим сбоя коварен: файл выглядит правильно на машине, которая его создала, потому что на этой машине шрифт установлен

HotXLS встраивает подмножества TrueType для используемых шрифтов, но путь отката к base-14 существует на случай, когда шрифт не удаётся разрешить, а откат к base-14 по определению не встраивается. Для архивного вывода настройте явный файл Unicode-шрифта, чтобы путь отката никогда не задействовался. Механика создания подмножеств и цепочки отката описана в статье создание подмножеств шрифтов TrueType; практическое правило — тестировать экспорт на машине, где не установлены ваши корпоративные шрифты

Диаграмма шрифтового правила PDF/A в экспортах HotXLS из Delphi: разрешённые шрифты становятся встроенными TrueType-субсетами, проходящими проверку везде, тогда как откат к base-14 никогда не встраивается и падает на машинах без шрифта
Неразрешимый шрифт откатывается к шрифту base-14, который никогда не встраивается, поэтому настройте явный файл Unicode-шрифта и тестируйте на машине без ваших корпоративных шрифтов

Что гарантирует экспортёр, а что нет

Точность здесь важнее, чем уверенный тон. Экспорт создаёт идентификационный аппарат PDF/A — output intent, встроенный ICC-профиль, метаданные pdfaid, подходящий заголовок версии — и проводит текст через путь встраивания. Он не запускает полный валидатор над собственным выводом, поэтому он не обеспечивает соблюдение каждого запрета из спецификации, например правил о прозрачности, о некоторых типах аннотаций и об опциональном содержимом

В экспорте электронной таблицы такие конструкции появляются редко, поэтому практический процент успеха высок. Но «высокий» — не то же самое, что «гарантированный», и для настоящего архивного конвейера правильная архитектура — это экспортировать, а затем проверить независимым инструментом, трактуя сбой проверки как ошибку конвейера, а не как предупреждение. Сопоставимое разделение ответственности для документов PDF описано в статье предварительная проверка соответствия PDF/A

Вёрстка — часть архивной достоверности

Архивированная электронная таблица, технически соответствующая требованиям, но визуально нечитаемая, не справилась со своей настоящей задачей. Настройка страницы весит больше в архивном экспорте, чем в повседневной печати, потому что впоследствии никто не сможет переверстать её заново

Три настройки окупаются сами по себе. Вписывание по ширине предотвращает классический сбой, когда таблица шириной в 14 столбцов приходит в виде семи страниц по два столбца каждая. Повторяющиеся строки заголовка делают каждую страницу читаемой в отрыве от остальных. А явные области печати исключают рабочие заметки, лежащие справа от настоящих данных. Полный набор элементов управления печатью и настройкой страницы описан в статье защита, настройка страницы и печать

// Настройка страницы, готовая к архивированию, применяется перед экспортом
Sheet := Book.Sheets[0];
Sheet.PageLandscape := True;
Sheet.FitToWidth := 1;
Sheet.FitToHeight := 0;                  // столько страниц по высоте, сколько нужно
Sheet.SetPrintArea('A1:N480');
Sheet.PrintTitleRows := '$1:$3';

// Метаданные тоже относятся к архиву
Book.Title := 'Quarterly results 2026 Q2';
Book.Author := 'Finance';

Контрольный список перед запуском первой пакетной обработки

Убедитесь, что файл идентифицирует себя: ничего не распаковывайте, просто откройте PDF в текстовом редакторе и найдите запись части pdfaid, запись соответствия и output intent GTS_PDFA1. Убедитесь в шрифтах: проверьте, что каждый шрифт в документе отмечен как встроенный. Убедитесь во внешнем виде: сравните экспортированные страницы с распечатанной копией, потому что настройка вписывания в страницу, молча сжимающая текст до четырёх пунктов, формально соответствует требованиям и при этом бесполезна

Затем прогоните независимый валидатор по репрезентативной выборке и сохраните отчёт. Архивы всё чаще запрашивают именно доказательство проверки, а не только файл, а сгенерировать его в момент экспорта гораздо дешевле, чем восстанавливать спустя годы. О повседневном, неархивном пути экспорта и его параметрах рендеринга см. статью экспорт листа в PDF

Архивный экспорт, настройка страницы и движок рендеринга за ними поставляются в одной библиотеке для Delphi и C++Builder; полный список возможностей — на странице компонента HotXLS Delphi Spreadsheet Component

Диаграмма архитектуры «экспорт, затем валидация» для вывода PDF/A HotXLS в Delphi: экспортёр пишет аппарат идентификации, а независимый валидатор маршрутизирует файл в архив или поднимает ошибку конвейера
Экспортёр строит аппарат идентификации, а независимый валидатор доказывает соответствие, и проваленная валидация останавливает пакет вместо журналирования предупреждения