Articolo tecnico

Incorporare immagini AVIF, HEIF e JPEG XL nel PDF da Delphi

PDF Library for Delphi accetta immagini AVIF, HEIF e JPEG XL come input tramite AddModernImageFromFile e le sue varianti stream e stringa, preservando l'alfa, il profilo ICC incorporato e i canali a 16 bit lungo il percorso verso l'oggetto immagine PDF. Il rilevamento del formato avviene su una lettura limitata del magic number, e la decodifica passa attraverso un backend sostituibile, così nulla di esterno viene invocato per un file che non è effettivamente uno di quei formati

Questi formati sono arrivati nei workflow documentali tramite i telefoni. iOS produce HEIC per impostazione predefinita da anni, i dispositivi Android producono AVIF, e un tecnico sul campo che fotografa una parte danneggiata invia un'immagine che un generatore di report PDF costruito nel 2015 non riesce ad aprire affatto. Il percorso di fallback generico, decodificare tramite una bitmap di piattaforma, produce affidabilmente colore a 8 bit e perde alfa e profilo colore lungo il percorso

Cosa preserva il percorso immagine moderno che una conversione bitmap perde?

Tre cose, e ognuna ha un workflow che dipende da essa. L'alfa sopravvive, il che conta per loghi e ritagli di prodotto composti sopra il contenuto della pagina. Il profilo ICC sopravvive, il che conta per qualsiasi cosa verrà stampata o abbinata cromaticamente. E i canali a 16 bit sopravvivono, il che conta per immagini mediche e scientifiche dove la quantizzazione a 8 bit distrugge proprio le gradazioni per cui l'immagine è stata catturata

Far passare un'immagine attraverso una bitmap di piattaforma perde tutte e tre le cose in un solo passaggio, e lo fa silenziosamente: il PDF risultante appare approssimativamente corretto, e nessuno se ne accorge finché una tipografia non chiede perché il rosso aziendale è sbagliato. Il valore opzione 8 sulle chiamate immagine moderne è il flag che mantiene insieme alfa, ICC e canali a 16 bit, ed è il valore predefinito per quelle chiamate

Aggiungerne una a una pagina

La chiamata restituisce un identificatore di immagine, che viene poi selezionato e disegnato, oppure disegnato e rilasciato in un solo passaggio:

uses
  PDFlibrary, PDFlibModernImage;

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

    // Options = 8 mantiene alfa, ICC e canali a 16 bit
    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;

Il rilevamento precede la decodifica ed è deliberatamente ristretto. La libreria legge un'intestazione limitata, riconosce i brand del formato file ISO base media che identificano AVIF e HEIF, e riconosce sia le firme grezze sia quelle di contenitore di JPEG XL, poi ripristina la posizione dello stream del chiamante. Un input sconosciuto o mascherato non raggiunge mai il codec esterno, il che impedisce che un eseguibile rinominato venga consegnato a un decoder come se fosse un'immagine

Dove avviene realmente la decodifica?

I formati immagine moderni sono codec grandi e complessi, e mettere uno di essi dentro una libreria PDF sarebbe una scelta di design strana. Il backend predefinito carica dinamicamente in processo un modulo MagickWand distribuibile e lo cerca in un ordine documentato: un file o una directory esplicita che imposti, variabili d'ambiente, la directory dell'eseguibile, e il percorso di ricerca di sistema

Le applicazioni che già distribuiscono un decoder, o che non devono caricare affatto un modulo esterno, registrano invece il proprio callback. Il contratto è piccolo: leggi lo stream di input, scrivi un PNG nello stream di output, rispetta l'orientamento richiesto:

function MyDecoder(InStream, OutPNG: TStream;
  ImageFormat: TPDFlibModernImageFormat;
  ApplyOrientation: Boolean): Boolean;
begin
  // Decodifica InStream con il tuo codec e scrivi i byte PNG in OutPNG
  Result := DecodeWithBundledCodec(InStream, OutPNG,
    ImageFormat, ApplyOrientation);
end;

begin
  RegisterModernImageDecoderBackend(MyDecoder);
  // ... aggiungi immagini ...
  ClearModernImageDecoderBackend;    // torna al backend predefinito
end;

Il deployment ottiene una comodità e una limitazione deliberata. Se la directory del codec contiene una sottodirectory modules\coders, la libreria compila le variabili d'ambiente del codec che tale struttura richiede, ma solo quando l'applicazione ospitante non le ha già impostate. Un'applicazione con una propria strategia di deployment a runtime la mantiene

Perché PNG nel mezzo?

Fare da ponte tramite un PNG in memoria anziché un buffer di pixel grezzi sembra un passaggio in più ed è in realtà quello corretto più economico. Il PNG esprime tutto ciò che deve sopravvivere, alfa, tipo di colore, profondità di bit e un profilo ICC incorporato, e la libreria ha già un percorso maturo e ben collaudato dal PNG a un oggetto immagine PDF con i filtri e lo spazio colore giusti. Riutilizzarlo significa che i formati moderni ereditano anni di lavoro sulla correttezza invece di ottenere un'implementazione parallela

Il ponte è interamente in memoria, quindi non vengono creati file temporanei e non serve alcuna pulizia in caso di crash. Una complicazione ha richiesto una gestione esplicita: alcune conversioni scartano il profilo ICC nel cambiare formato. Il backend quindi cattura il profilo sorgente prima del cambio di formato, lo comprime con Flate, costruisce un chunk iCCP valido con un CRC ricalcolato, e rimuove qualsiasi chunk sRGB che confliggerebbe con esso. Nei test, un AVIF decodificato ha mantenuto RGBA a 16 bit con alfa a 16 bit, e il profilo estratto dal PDF risultante corrispondeva byte per byte al profilo sorgente per 60.960 byte

Note pratiche prima di attivarlo in produzione

Controlla la disponibilità all'avvio anziché alla prima fotografia. ModernImageCodecAvailable riporta se un backend può essere usato, e SetModernImageCodecLibrary punta a un file o una directory esplicita quando il tuo deployment colloca il codec in un punto non standard:

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

Tieni d'occhio la dimensione del file risultante. Un'immagine RGBA a 16 bit con un profilo incorporato è un oggetto immagine PDF grande, e un report che ne contiene quaranta sarà voluminoso. Quando il documento è destinato alla visualizzazione a schermo piuttosto che alla stampa, il downsampling prima dell'incorporamento è il compromesso giusto, e le leve generali sulla dimensione sono trattate in ottimizzazione della dimensione dei file PDF

Infine, decidi la policy del colore deliberatamente. Mantenere il profilo sorgente è corretto per lavori di archiviazione e stampa; convertire in uno spazio a livello di documento è corretto quando un insieme misto di fotografie deve apparire coerente, e il percorso di conversione è descritto in ricolorare un documento in un altro spazio colore. Se serve confermare cosa è effettivamente finito nel file, il percorso di ispezione in estrazione di testo, immagini e font riporta gli oggetti immagine che un documento porta

L'input di immagini moderne, la gestione del colore e l'ottimizzazione delle immagini fanno parte della stessa libreria per Delphi, C++Builder e Free Pascal; l'elenco completo delle funzionalità si trova nella pagina di PDF Library per Delphi