PDF Library for Delphi принимает изображения AVIF, HEIF и JPEG XL на вход через AddModernImageFromFile и её варианты для потоков и строк, сохраняя альфа-канал, встроенный ICC-профиль и 16-битные каналы на пути в объект изображения PDF. Определение формата происходит по ограниченному чтению магического числа, а декодирование проходит через заменяемый бэкенд, поэтому для файла, который на самом деле не является одним из этих форматов, ничего внешнего не вызывается
Эти форматы попали в документные рабочие процессы через телефоны. iOS годами создаёт HEIC по умолчанию, устройства Android создают AVIF, а полевой техник, фотографирующий повреждённую деталь, присылает изображение, которое генератор PDF-отчётов, созданный в 2015 году, вообще не может открыть. Общий резервный путь — декодирование через растровое изображение платформы — стабильно даёт 8-битный цвет и теряет по пути альфа-канал и цветовой профиль
Что сохраняет современный путь изображений, теряемое при конвертации через растровое изображение?
Три вещи, и у каждой есть рабочий процесс, который от неё зависит. Альфа-канал сохраняется, что важно для логотипов и вырезок продуктов, компонуемых поверх содержимого страницы. ICC-профиль сохраняется, что важно для всего, что будет напечатано или согласовано по цвету. И 16-битные каналы сохраняются, что важно для медицинских и научных изображений, где 8-битное квантование разрушает как раз те градации, ради которых изображение было снято
Прогон изображения через растровое изображение платформы теряет все три вещи за один шаг, причём молча: получившийся PDF выглядит приблизительно правильным, и никто не замечает, пока принтер не спросит, почему корпоративный красный цвет неверен. Значение опции 8 в вызовах современных изображений — это флаг, сохраняющий вместе альфа-канал, ICC и 16-битные каналы, и он по умолчанию используется в этих вызовах
Добавление изображения на страницу
Вызов возвращает идентификатор изображения, который затем выбирается и рисуется, либо рисуется и освобождается за один шаг:
uses
PDFlibrary, PDFlibModernImage;
var
Lib: TPDFlib;
ImageID: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.SetPageSize('A4');
Lib.NewPage;
// Options = 8 сохраняет альфа-канал, ICC и 16-битные каналы
ImageID := Lib.AddModernImageFromFile('site-photo.heic', 8);
if ImageID > 0 then
Lib.DrawImageAndRelease(ImageID, 40, 40, 515, 340)
else
Lib.DrawText(40, 40, 'image could not be decoded');
Lib.SaveToFile('inspection-report.pdf');
finally
Lib.Free;
end;
end;
Определение предшествует декодированию и намеренно узкое. Библиотека читает ограниченный заголовок, распознаёт бренды формата ISO base media file format, идентифицирующие AVIF и HEIF, и распознаёт как сырую, так и контейнерную сигнатуры JPEG XL, а затем восстанавливает позицию потока вызывающего кода. Неизвестный или замаскированный вход никогда не доходит до внешнего кодека, что не даёт переименованному исполняемому файлу быть переданным декодеру так, будто это картинка
Где на самом деле происходит декодирование?
Современные форматы изображений — большие сложные кодеки, и поместить один такой внутрь PDF-библиотеки было бы странным дизайнерским решением. Бэкенд по умолчанию динамически загружает развёртываемый модуль MagickWand в процессе и ищет его в документированном порядке: явно указанный файл или каталог, переменные окружения, каталог исполняемого файла и системный путь поиска
Приложения, уже поставляющие собственный декодер или которым вообще нельзя загружать внешний модуль, регистрируют собственный обратный вызов. Контракт небольшой: прочитать входной поток, записать PNG в выходной поток, соблюсти запрошенную ориентацию:
function MyDecoder(InStream, OutPNG: TStream;
ImageFormat: TPDFlibModernImageFormat;
ApplyOrientation: Boolean): Boolean;
begin
// Декодируйте InStream своим кодеком и запишите байты PNG в OutPNG
Result := DecodeWithBundledCodec(InStream, OutPNG,
ImageFormat, ApplyOrientation);
end;
begin
RegisterModernImageDecoderBackend(MyDecoder);
// ... добавление изображений ...
ClearModernImageDecoderBackend; // назад к бэкенду по умолчанию
end;
Развёртывание получает одно удобство и одно намеренное ограничение. Если каталог кодека содержит подкаталог modules\coders, библиотека заполняет переменные окружения кодека, необходимые для такой раскладки, но только когда хост-приложение ещё не установило их само. Приложение с собственной стратегией развёртывания среды выполнения сохраняет свою
Почему PNG посередине?
Соединение через PNG в памяти, а не через сырой буфер пикселей, выглядит как лишний шаг, а на деле оказывается самым дешёвым правильным решением. PNG выражает всё, что должно сохраниться: альфа-канал, тип цвета, глубину бит и встроенный ICC-профиль, а у библиотеки уже есть зрелый, хорошо протестированный путь от PNG к объекту изображения PDF с правильными фильтрами и цветовым пространством. Повторное использование этого пути означает, что современные форматы наследуют годы работы над корректностью вместо получения параллельной реализации
Мост целиком находится в памяти, поэтому временные файлы не создаются, и при сбое не нужна очистка. Одна тонкость потребовала явной обработки: некоторые конвертации теряют ICC-профиль при смене формата. Поэтому бэкенд захватывает исходный профиль до переключения формата, сжимает его Flate, строит корректный чанк iCCP с пересчитанной CRC и удаляет любой чанк sRGB, который вступил бы с ним в конфликт. При тестировании декодированный AVIF сохранил 16-битный RGBA с 16-битным альфа-каналом, а профиль, извлечённый из получившегося PDF, совпал с исходным профилем побайтово на 60 960 байтах
Практические заметки перед включением в продакшене
Проверяйте доступность при запуске, а не при первой фотографии. ModernImageCodecAvailable сообщает, доступен ли бэкенд, а SetModernImageCodecLibrary указывает на явный файл или каталог, когда ваше развёртывание размещает кодек в нестандартном месте:
Lib.SetModernImageCodecLibrary('C:\MyApp\codecs');
if Lib.ModernImageCodecAvailable = 0 then
Log('modern image input unavailable - HEIC and AVIF will be refused');
Следите за размером файла результата. 16-битное изображение RGBA со встроенным профилем — крупный объект изображения PDF, и отчёт с сорока такими будет большим. Когда документ предназначен для экранного просмотра, а не для печати, понижение разрешения перед встраиванием — правильный компромисс, а общие рычаги размера описаны в статье оптимизация размера файла PDF
Наконец, решайте политику цвета осознанно. Сохранение исходного профиля правильно для архивной и печатной работы; конвертация в общее для документа пространство правильна, когда смешанный набор фотографий должен выглядеть согласованно, а путь конвертации описан в статье перекраска документа в другое цветовое пространство. Если нужно подтвердить, что именно попало в файл, путь инспекции в статье извлечение текста, изображений и шрифтов сообщает об объектах изображений, которые несёт документ
Современный ввод изображений, управление цветом и оптимизация изображений — часть одной библиотеки для Delphi, C++Builder и Free Pascal; полный список возможностей — на странице PDF Library for Delphi