Műszaki cikk

AVIF-, HEIF- és JPEG XL-képek beágyazása PDF-be Delphiből

A PDF Library for Delphi az AddModernImageFromFile-on és annak stream- és string-változatain keresztül fogad be AVIF-, HEIF- és JPEG XL-képeket, megőrizve az alfacsatornát, a beágyazott ICC-profilt és a 16 bites csatornákat a PDF-képobjektumba vezető úton. A formátumfelismerés egy korlátozott, mágikus szám alapú olvasással történik, a dekódolás pedig egy cserélhető háttérrendszeren keresztül fut, így semmilyen külső komponens nem hívódik meg egy olyan fájlnál, amely valójában nem tartozik ezen formátumok egyikéhez sem

Ezek a formátumok telefonokon keresztül érkeztek a dokumentum-munkafolyamatokba. Az iOS évek óta alapértelmezetten HEIC-et állít elő, az Android-eszközök AVIF-et, egy sérült alkatrészt fényképező terepi technikus pedig olyan képet küld, amelyet egy 2015-ben épített PDF-jelentéskészítő egyáltalán nem tud megnyitni. Az általános visszaesési útvonal, a platform-bitkép szerinti dekódolás, megbízhatóan 8 bites színt ad, és útközben elveszíti az alfacsatornát és a színprofilt

Mit őriz meg a modern kép útvonal, amit egy bitkép-konverzió elveszít?

Három dolgot, és mindegyiktől függ egy munkafolyamat. Az alfacsatorna megmarad, ami a logóknál és az oldal tartalma fölé komponált termékkivágásoknál számít. Az ICC-profil megmarad, ami mindennél számít, amit nyomtatni vagy színegyeztetni fognak. A 16 bites csatornák pedig megmaradnak, ami az orvosi és tudományos képanyagoknál számít, ahol a 8 bites kvantálás pontosan azokat az árnyalatokat pusztítja el, amelyek miatt a képet felvették

Egy kép platform-bitképen átvezetése egy lépésben mindhármat elveszíti, és mindezt csendben teszi: az eredményül kapott PDF nagyjából jónak tűnik, és senki sem veszi észre, amíg egy nyomda meg nem kérdezi, miért rossz a vállalati vörös. A modern kép hívásokon a 8-as opcióérték az a jelző, amely együtt tartja az alfacsatornát, az ICC-t és a 16 bites csatornákat, és ez az alapértelmezett ezeknél a hívásoknál

Egy kép hozzáadása egy oldalhoz

A hívás egy képazonosítót ad vissza, amelyet ezután kiválasztanak és kirajzolnak, vagy egyetlen lépésben kirajzolnak és felszabadítanak:

uses
  PDFlibrary, PDFlibModernImage;

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

    // Options = 8 megőrzi az alfacsatornát, az ICC-t és a 16 bites csatornákat
    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;

A felismerés megelőzi a dekódolást, és szándékosan szűk. A könyvtár egy korlátozott fejlécet olvas, felismeri az AVIF-et és a HEIF-et azonosító ISO alap médiafájlformátum-márkákat, felismeri a JPEG XL nyers és konténer-aláírásait is, majd visszaállítja a hívó stream-pozícióját. Egy ismeretlen vagy álcázott bemenet soha nem éri el a külső kodeket, ami megakadályozza, hogy egy átnevezett futtatható fájlt egy dekódernek adjunk át, mintha az egy kép lenne

Hol történik valójában a dekódolás?

A modern képformátumok nagy, összetett kodekek, és egyet egy PDF-könyvtárba tenni furcsa tervezési döntés lenne. Az alapértelmezett háttérrendszer dinamikusan tölt be egy telepíthető MagickWand-modult a folyamaton belül, és egy dokumentált sorrendben keresi azt: egy általad beállított explicit fájl vagy könyvtár, környezeti változók, a futtatható fájl könyvtára, majd a rendszer keresési útvonala

Azok az alkalmazások, amelyek már szállítanak egy dekódert, vagy amelyeknek egyáltalán nem szabad külső modult betölteniük, ehelyett saját visszahívást regisztrálnak. A szerződés kicsi: olvasd be a bemeneti streamet, írj egy PNG-t a kimeneti streambe, tartsd tiszteletben a kért tájolást:

function MyDecoder(InStream, OutPNG: TStream;
  ImageFormat: TPDFlibModernImageFormat;
  ApplyOrientation: Boolean): Boolean;
begin
  // Dekódold az InStreamet a saját kodekeddel, és írd a PNG-bájtokat az OutPNG-be
  Result := DecodeWithBundledCodec(InStream, OutPNG,
    ImageFormat, ApplyOrientation);
end;

begin
  RegisterModernImageDecoderBackend(MyDecoder);
  // ... add images ...
  ClearModernImageDecoderBackend;    // vissza az alapértelmezett háttérrendszerhez
end;

A telepítés egy kényelmi funkciót és egy szándékos önmegtartóztatást kap. Ha a kodekkönyvtár tartalmaz egy modules\coders alkönyvtárat, a könyvtár kitölti azokat a kodek környezeti változókat, amelyeket egy ilyen elrendezés igényel, de csak akkor, ha a gazdaalkalmazás még nem állította be azokat. Egy saját futásidejű telepítési stratégiával rendelkező alkalmazás megtartja a sajátját

Miért PNG a közepén?

A hidalás egy memóriabeli PNG-n keresztül, nem egy nyers pixelpufferen, extra lépésnek tűnik, valójában viszont a legolcsóbb helyes megoldás. A PNG mindent kifejez, aminek túl kell élnie: az alfacsatornát, a színtípust, a bitmélységet és egy beágyazott ICC-profilt, a könyvtár pedig már rendelkezik egy érett, jól tesztelt útvonallal a PNG-ből a PDF-képobjektumba, a megfelelő szűrőkkel és színtérrel. Ennek újrafelhasználása azt jelenti, hogy a modern formátumok évekig tartó helyességi munkát örökölnek, ahelyett hogy egy párhuzamos implementációt kapnának

A híd teljes egészében memóriában van, így nem jönnek létre ideiglenes fájlok, és összeomlás esetén nincs szükség takarításra. Egy buktató explicit kezelést igényelt: néhány konverzió elejti az ICC-profilt formátumváltáskor. A háttérrendszer ezért a formátumváltás előtt rögzíti a forrásprofilt, Flate-tel tömöríti, egy érvényes iCCP chunköt épít újraszámolt CRC-vel, és eltávolít minden olyan sRGB chunköt, amely ütközne vele. Tesztelés közben egy dekódolt AVIF megtartotta a 16 bites RGBA-t 16 bites alfával, és az eredményül kapott PDF-ből kinyert profil bájtra pontosan megegyezett a forrásprofillal, 60 960 bájton

Gyakorlati megjegyzések, mielőtt éles környezetben bekapcsolod

Az elérhetőséget induláskor ellenőrizd, ne az első fényképnél. A ModernImageCodecAvailable jelenti, hogy használható-e háttérrendszer, a SetModernImageCodecLibrary pedig egy explicit fájlra vagy könyvtárra mutat, amikor a telepítésed nem szabványos helyre teszi a kodeket:

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

Figyeld az eredmény fájlméretét. Egy 16 bites RGBA kép beágyazott profillal egy nagy PDF-képobjektum, egy negyvenet tartalmazó jelentés pedig nagy lesz. Amikor a dokumentum képernyős megtekintésre szánt, nem nyomtatásra, a beágyazás előtti lemintavételezés a helyes megoldás, az általános méretcsökkentő eszközöket pedig a PDF-fájlméret optimalizálása ismerteti

Végül dönts tudatosan a színszabályzatról. A forrásprofil megtartása helyes archiválási és nyomdai munkánál; egy dokumentumszintű térre konvertálás akkor helyes, ha egy vegyes fényképkészletnek konzisztensnek kell látszania, a konverziós útvonalat pedig a egy dokumentum átszínezése másik színtérre ismerteti. Ha meg akarod erősíteni, mi került ténylegesen a fájlba, a szöveg-, kép- és betűtípus-kinyerés cikkben szereplő vizsgálati útvonal jelenti a dokumentum által hordozott képobjektumokat

A modern kép-bemenet, a színkezelés és a képoptimalizálás ugyanannak a könyvtárnak a részei Delphihez, C++Builderhez és Free Pascalhoz; a teljes funkciólista a PDF Library for Delphi oldalán található