Technický článek

Vkládání obrázků AVIF, HEIF a JPEG XL do PDF z Delphi

PDF Library for Delphi přijímá jako vstup obrázky AVIF, HEIF a JPEG XL přes AddModernImageFromFile a jeho varianty pro proud a řetězec, přičemž na cestě do objektu obrázku PDF zachovává alfa kanál, vložený profil ICC a 16bitové kanály. Rozpoznání formátu probíhá omezeným čtením magického čísla a dekódování běží přes vyměnitelný backend, takže pro soubor, který ve skutečnosti není žádným z těchto formátů, se nevolá nic externího

Tyto formáty se do dokumentových pracovních postupů dostaly přes telefony. iOS produkuje HEIC ve výchozím nastavení už léta, zařízení Android produkují AVIF a technik v terénu, který fotí poškozený díl, pošle obrázek, který generátor sestav PDF postavený v roce 2015 vůbec neumí otevřít. Obecná záchranná cesta, dekódování přes systémovou bitmapu, spolehlivě vydá 8bitovou barvu a cestou ztratí alfa kanál i barevný profil

Co moderní cesta pro obrázky zachovává, co při převodu na bitmapu zaniká?

Tři věci, a každá má pracovní postup, který na ní závisí. Alfa kanál přežije, což má význam pro loga a produktové výřezy skládané nad obsah stránky. Profil ICC přežije, což má význam pro cokoli, co se bude tisknout nebo barevně srovnávat. A 16bitové kanály přežijí, což má význam pro lékařské a vědecké snímky, kde 8bitová kvantizace ničí právě ty odstíny, kvůli kterým byl obrázek pořízen

Průchod obrázku systémovou bitmapou ztratí všechny tři v jednom kroku, a dělá to potichu: výsledné PDF vypadá přibližně správně a nikdo si toho nevšimne, dokud se tiskárna nezeptá, proč je firemní červená špatně. Hodnota volby 8 u moderních volání pro obrázky je příznak, který drží alfa kanál, ICC a 16bitové kanály pohromadě, a je pro tato volání výchozí

Přidání obrázku na stránku

Volání vrátí identifikátor obrázku, který se pak vybere a nakreslí, nebo nakreslí a uvolní v jednom kroku:

uses
  PDFlibrary, PDFlibModernImage;

var
  Lib: TPDFlib;
  ImageID: Integer;
begin
  Lib := TPDFlib.Create;
  try
    Lib.NewDocument;
    Lib.SetPageSize('A4');
    Lib.NewPage;

    // Options = 8 zachová alfa kanál, ICC a 16bitové kanály
    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;

Rozpoznávání předchází dekódování a je záměrně úzké. Knihovna přečte omezenou hlavičku, rozpozná značky formátu ISO base media file, které identifikují AVIF a HEIF, a rozpozná jak syrové, tak kontejnerové signatury JPEG XL, poté obnoví pozici volajícího proudu. Neznámý nebo maskovaný vstup se k externímu kodeku nikdy nedostane, což brání tomu, aby byl přejmenovaný spustitelný soubor předán dekodéru, jako by šlo o obrázek

Kde se dekódování ve skutečnosti odehrává?

Moderní formáty obrázků jsou velké, složité kodeky a vložit jeden z nich přímo do knihovny PDF by byla podivná návrhová volba. Výchozí backend dynamicky načte nasaditelný modul MagickWand v rámci procesu a hledá jej v zdokumentovaném pořadí: explicitní soubor nebo adresář, který nastavíte, proměnné prostředí, adresář spustitelného souboru a systémová cesta pro vyhledávání

Aplikace, které už dodávají vlastní dekodér, nebo které nesmí vůbec načítat externí modul, místo toho registrují vlastní zpětné volání. Smlouva je malá: přečíst vstupní proud, zapsat PNG do výstupního proudu, respektovat požadovanou orientaci:

function MyDecoder(InStream, OutPNG: TStream;
  ImageFormat: TPDFlibModernImageFormat;
  ApplyOrientation: Boolean): Boolean;
begin
  // Dekódujte InStream vlastním kodekem a zapište bajty PNG do OutPNG
  Result := DecodeWithBundledCodec(InStream, OutPNG,
    ImageFormat, ApplyOrientation);
end;

begin
  RegisterModernImageDecoderBackend(MyDecoder);
  // ... přidání obrázků ...
  ClearModernImageDecoderBackend;    // zpět na výchozí backend
end;

Nasazení má jednu výhodu a jedno záměrné omezení. Pokud adresář kodeku obsahuje podadresář modules\coders, knihovna doplní proměnné prostředí kodeku, které takové rozvržení potřebuje, ale pouze pokud je hostitelská aplikace ještě sama nenastavila. Aplikace s vlastní strategií nasazení runtime si ji ponechává

Proč je uprostřed PNG?

Přemostění přes PNG v paměti místo přes syrový buffer pixelů vypadá jako krok navíc a ve skutečnosti je to nejlevnější správný krok. PNG vyjadřuje vše, co musí přežít, alfa kanál, typ barvy, bitovou hloubku a vložený profil ICC, a knihovna už má vyzrálou, dobře otestovanou cestu z PNG do objektu obrázku PDF se správnými filtry a barevným prostorem. Její opětovné použití znamená, že moderní formáty zdědí roky práce na správnosti místo toho, aby dostaly paralelní implementaci

Přemostění je zcela v paměti, takže se nevytvářejí žádné dočasné soubory a při pádu není potřeba žádný úklid. Jedna vráska vyžadovala výslovné ošetření: některé konverze při změně formátu zahodí profil ICC. Backend proto zachytí zdrojový profil ještě před přepnutím formátu, zkomprimuje jej pomocí Flate, sestaví platný blok iCCP s přepočítaným CRC a odstraní jakýkoli blok sRGB, který by s ním kolidoval. Při testování si dekódovaný AVIF ponechal 16bitové RGBA s 16bitovým alfa kanálem a profil extrahovaný z výsledného PDF odpovídal zdrojovému profilu bajt po bajtu na 60 960 bajtech

Praktické poznámky před nasazením do produkce

Dostupnost kontrolujte při startu, ne až u první fotografie. ModernImageCodecAvailable hlásí, zda lze backend použít, a SetModernImageCodecLibrary ukazuje na explicitní soubor nebo adresář, když vaše nasazení umístí kodek na nestandardní místo:

Lib.SetModernImageCodecLibrary('C:\MyApp\codecs');
if Lib.ModernImageCodecAvailable = 0 then
  Log('modern image input unavailable - HEIC and AVIF will be refused');

Sledujte velikost souboru výsledku. 16bitový obrázek RGBA s vloženým profilem je velký objekt obrázku PDF a sestava se čtyřiceti takovými bude velká. Když je dokument určen pro zobrazení na obrazovce, ne pro tisk, je podvzorkování před vložením správným kompromisem, a obecné páky pro velikost popisuje článek optimalizace velikosti souboru PDF

Nakonec rozhodněte o barevné politice záměrně. Ponechání zdrojového profilu je správné pro archivaci a tiskovou práci; převod na prostor jednotný pro celý dokument je správný, když musí smíšená sada fotografií vypadat konzistentně, a cestu konverze popisuje článek přebarvení dokumentu do jiného barevného prostoru. Pokud potřebujete potvrdit, co se v souboru skutečně ocitlo, cesta inspekce v článku extrakce textu, obrázků a fontů hlásí objekty obrázků, které dokument nese

Vstup moderních obrázků, správa barev a optimalizace obrázků jsou součástí stejné knihovny pro Delphi, C++Builder a Free Pascal; kompletní seznam funkcí je na stránce PDF Library for Delphi