Artigo Técnico

Incorporar imagens AVIF, HEIF e JPEG XL em PDF no Delphi

A PDF Library for Delphi aceita imagens AVIF, HEIF e JPEG XL como entrada por meio de AddModernImageFromFile e de suas variantes de stream e string, preservando o alfa, o perfil ICC incorporado e os canais de 16 bits no caminho até o objeto de imagem do PDF. A detecção de formato acontece em uma leitura limitada de número mágico, e a decodificação passa por um backend substituível, então nada externo é invocado para um arquivo que na verdade não é um desses formatos

Esses formatos chegaram aos fluxos de trabalho de documentos por meio de celulares. O iOS produz HEIC por padrão há anos, dispositivos Android produzem AVIF, e um técnico de campo fotografando uma peça danificada envia uma imagem que um gerador de relatórios em PDF construído em 2015 simplesmente não consegue abrir. O caminho de fallback genérico, decodificando por meio de um bitmap de plataforma, produz de forma confiável cor de 8 bits e perde o alfa e o perfil de cor no caminho

O que o caminho de imagem moderna preserva que uma conversão para bitmap perde?

Três coisas, e cada uma tem um fluxo de trabalho que depende dela. O alfa sobrevive, o que importa para logotipos e recortes de produto compostos sobre o conteúdo da página. O perfil ICC sobrevive, o que importa para qualquer coisa que será impressa ou combinada por cor. E os canais de 16 bits sobrevivem, o que importa para imagens médicas e científicas onde a quantização de 8 bits destrói exatamente as gradações para as quais a imagem foi capturada

Passar uma imagem por um bitmap de plataforma perde as três coisas em uma única etapa, e faz isso silenciosamente: o PDF resultante parece aproximadamente correto, e ninguém percebe até que uma gráfica pergunte por que o vermelho corporativo está errado. O valor de opção 8 nas chamadas de imagem moderna é a flag que mantém alfa, ICC e canais de 16 bits juntos, e é o padrão para essas chamadas

Diagrama do PDF Library for Delphi comparando uma conversão de bitmap de plataforma que descarta alfa, o perfil ICC e os canais de 16 bits com o caminho de imagem moderno que carrega os três no objeto de imagem do PDF
Uma decodificação genérica de bitmap achata silenciosamente a transparência e quantiza os canais antes mesmo de a página ser renderizada

Adicionando uma imagem a uma página

A chamada retorna um identificador de imagem, que é então selecionado e desenhado, ou desenhado e liberado em uma única etapa:

uses
  PDFlibrary, PDFlibModernImage;

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

    // Options = 8 mantém alfa, ICC e canais de 16 bits
    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 detecção precede a decodificação e é deliberadamente restrita. A biblioteca lê um cabeçalho limitado, reconhece as marcas do formato de arquivo de mídia base ISO que identificam AVIF e HEIF, e reconhece tanto as assinaturas brutas quanto as de contêiner do JPEG XL, e então restaura a posição do stream do chamador. Uma entrada desconhecida ou disfarçada nunca chega ao codec externo, o que impede que um executável renomeado seja entregue a um decodificador como se fosse uma imagem

Onde a decodificação realmente acontece?

Formatos de imagem modernos são codecs grandes e complexos, e colocar um deles dentro de uma biblioteca de PDF seria uma escolha de design estranha. O backend padrão carrega dinamicamente, em processo, um módulo MagickWand implantável e o procura em uma ordem documentada: um arquivo ou diretório explícito que você define, variáveis de ambiente, o diretório do executável e o caminho de busca do sistema

Diagrama do portão de detecção limitada por magic number que confirma AVIF, HEIF e JPEG XL antes de escolher um backend de decodificação a partir de um caminho explícito, variáveis de ambiente, diretório do executável ou um callback Delphi registrado
A detecção roda sobre uma leitura limitada do cabeçalho, de modo que um executável renomeado nunca chega ao codec

Aplicações que já entregam um decodificador, ou que não podem carregar nenhum módulo externo, registram seu próprio callback em vez disso. O contrato é pequeno: leia o stream de entrada, grave um PNG no stream de saída, respeite a orientação solicitada:

function MyDecoder(InStream, OutPNG: TStream;
  ImageFormat: TPDFlibModernImageFormat;
  ApplyOrientation: Boolean): Boolean;
begin
  // Decodifique InStream com seu próprio codec e grave os bytes PNG em OutPNG
  Result := DecodeWithBundledCodec(InStream, OutPNG,
    ImageFormat, ApplyOrientation);
end;

begin
  RegisterModernImageDecoderBackend(MyDecoder);
  // ... adiciona imagens ...
  ClearModernImageDecoderBackend;    // volta ao backend padrão
end;

A implantação ganha uma conveniência e uma restrição deliberada. Se o diretório do codec contiver um subdiretório modules\coders, a biblioteca preenche as variáveis de ambiente de codec que esse layout exige, mas apenas quando a aplicação hospedeira ainda não as definiu. Uma aplicação com sua própria estratégia de implantação de runtime mantém a dela

Por que passar por PNG no meio do caminho?

Fazer a ponte por um PNG em memória, em vez de um buffer de pixels bruto, parece uma etapa extra e na verdade é a mais barata correta. O PNG expressa tudo o que precisa sobreviver, alfa, tipo de cor, profundidade de bits e um perfil ICC incorporado, e a biblioteca já tem um caminho maduro e bem testado de PNG até um objeto de imagem de PDF com os filtros e o espaço de cor corretos. Reutilizá-lo significa que os formatos modernos herdam anos de trabalho de correção, em vez de ganhar uma implementação paralela

A ponte é inteiramente em memória, então nenhum arquivo temporário é criado e nenhuma limpeza é necessária em caso de falha. Um detalhe exigiu tratamento explícito: algumas conversões descartam o perfil ICC ao mudar de formato. O backend, portanto, captura o perfil de origem antes da troca de formato, comprime-o com Flate, constrói um chunk iCCP válido com um CRC recalculado, e remove qualquer chunk sRGB que entraria em conflito com ele. Em testes, um AVIF decodificado manteve RGBA de 16 bits com alfa de 16 bits, e o perfil extraído do PDF resultante correspondeu ao perfil de origem byte a byte em 60.960 bytes

Diagrama do PDF Library for Delphi da ponte PNG em memória que captura o perfil ICC de origem antes da troca de formato, o reconstrói como um chunk iCCP comprimido, descarta o chunk sRGB conflitante e reutiliza o caminho comprovado de incorporação de PNG em PDF
Capturar o perfil ICC antes da mudança de formato mantém a cópia incorporada byte a byte idêntica à origem

Notas práticas antes de ativar isso em produção

Verifique a disponibilidade na inicialização, não na primeira fotografia. ModernImageCodecAvailable informa se um backend pode ser usado, e SetModernImageCodecLibrary aponta para um arquivo ou diretório explícito quando sua implantação coloca o codec em algum lugar não padrão:

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

Observe o tamanho de arquivo do resultado. Uma imagem RGBA de 16 bits com um perfil incorporado é um objeto de imagem de PDF grande, e um relatório com quarenta delas será grande. Quando o documento se destina a visualização em tela em vez de impressão, reduzir a resolução (downsampling) antes de incorporar é a escolha certa, e as alavancas gerais de tamanho são abordadas em otimização de tamanho de arquivo PDF

Por fim, decida a política de cor deliberadamente. Manter o perfil de origem é correto para arquivamento e trabalho de impressão; converter para um espaço de todo o documento é correto quando um conjunto misto de fotografias precisa parecer consistente, e a rota de conversão é descrita em recolorindo um documento para outro espaço de cor. Se você precisa confirmar o que realmente entrou no arquivo, o caminho de inspeção em extração de texto, imagem e fonte relata os objetos de imagem que um documento carrega

Entrada de imagem moderna, gerenciamento de cor e otimização de imagem fazem parte da mesma biblioteca para Delphi, C++Builder e Free Pascal; a lista completa de recursos está na página da PDF Library for Delphi