Artigo Técnico

Incorporar Imagens AVIF, HEIF e JPEG XL em PDF a partir do Delphi

A PDF Library for Delphi aceita imagens AVIF, HEIF e JPEG XL como entrada através de AddModernImageFromFile e das suas variantes de fluxo e de string, preservando o alfa, o perfil ICC incorporado e os canais de 16 bits no caminho para dentro do objeto de imagem PDF. A deteção de formato acontece sobre uma leitura limitada de número mágico, e a descodificação corre através de um backend substituível, pelo que nada externo é invocado para um ficheiro que não seja realmente um desses formatos

Estes formatos chegaram aos fluxos de trabalho de documentos através dos telemóveis. O iOS produz HEIC por defeito há anos, os dispositivos Android produzem AVIF, e um técnico de campo que fotografa uma peça danificada envia uma imagem que um gerador de relatórios PDF construído em 2015 simplesmente não consegue abrir. O caminho de recurso genérico, descodificar através de um bitmap da plataforma, produz de forma consistente cor de 8 bits e perde o alfa e o perfil de cor pelo caminho

O que é 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 logótipos e recortes de produto compostos sobre o conteúdo da página. O perfil ICC sobrevive, o que importa para tudo o que vá ser impresso ou correspondido em cor. E os canais de 16 bits sobrevivem, o que importa para imagens médicas e científicas onde a quantização a 8 bits destrói exatamente as gradações para as quais a imagem foi captada

Passar uma imagem por um bitmap da plataforma perde as três coisas num único passo, e fá-lo silenciosamente: o PDF resultante parece aproximadamente correto, e ninguém repara até uma gráfica perguntar por que razão o vermelho corporativo está errado. O valor de opção 8 nas chamadas de imagem moderna é o sinalizador que mantém o alfa, o ICC e os canais de 16 bits em conjunto, e é a predefinição para essas chamadas

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

Acrescentar uma imagem a uma página

A chamada devolve um identificador de imagem, que é depois selecionado e desenhado, ou desenhado e libertado num único passo:

uses
  PDFlibrary, PDFlibModernImage;

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

    // Options = 8 mantém o alfa, o ICC e os 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 deteção antecede a descodificação e é deliberadamente restrita. A biblioteca lê um cabeçalho limitado, reconhece as marcas de formato de ficheiro multimédia base ISO que identificam o AVIF e o HEIF, e reconhece tanto as assinaturas em bruto como as de contentor do JPEG XL, e depois restaura a posição de fluxo 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 descodificador como se fosse uma fotografia

Onde é que a descodificação realmente acontece?

Os formatos de imagem modernos são codecs grandes e complexos, e colocar um deles dentro de uma biblioteca PDF seria uma escolha de design estranha. O backend predefinido carrega dinamicamente um módulo MagickWand implantável no mesmo processo e procura-o por uma ordem documentada: um ficheiro ou diretório explícito definido pelo utilizador, variáveis de ambiente, o diretório do executável, e o caminho de pesquisa do sistema

Diagrama da barreira limitada de deteção por número mágico que confirma AVIF, HEIF e JPEG XL antes de um backend de descodificação ser escolhido a partir de um caminho explícito, variáveis de ambiente, diretório do executável ou um callback Delphi registado
A deteção é executada sobre uma leitura limitada do cabeçalho, pelo que um executável renomeado nunca chega ao codec

As aplicações que já incluem um descodificador, ou que não podem de todo carregar um módulo externo, registam em alternativa o seu próprio callback. O contrato é pequeno: ler o fluxo de entrada, escrever um PNG no fluxo de saída, respeitar a orientação pedida:

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

begin
  RegisterModernImageDecoderBackend(MyDecoder);
  // ... acrescentar imagens ...
  ClearModernImageDecoderBackend;    // voltar ao backend predefinido
end;

A implantação recebe uma conveniência e uma contençã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 esquema exige, mas apenas quando a aplicação anfitriã ainda não as definiu. Uma aplicação com a sua própria estratégia de implantação de runtime mantém-na

Por que razão PNG no meio?

Fazer a ponte através de um PNG em memória em vez de um buffer de pixels em bruto parece um passo extra e é, na verdade, o passo correto mais económico. O PNG exprime tudo o que tem de 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 para um objeto de imagem 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 receberem uma implementação paralela

A ponte fica inteiramente em memória, pelo que não são criados ficheiros temporários e não é preciso nenhuma limpeza numa falha. Uma particularidade exigiu tratamento explícito: algumas conversões descartam o perfil ICC ao mudar de formato. O backend captura, por isso, o perfil de origem antes da mudança de formato, comprime-o com Flate, constrói um bloco iCCP válido com um CRC recalculado, e remove qualquer bloco sRGB que entrasse em conflito com ele. Em testes, um AVIF descodificado 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, com 60 960 bytes

Diagrama da PDF Library for Delphi da ponte PNG em memória que captura o perfil ICC de origem antes da mudança de formato, reconstrói-o como um chunk iCCP comprimido, descarta o chunk sRGB conflituoso 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 idêntica byte a byte à origem

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

Verifique a disponibilidade no arranque, não na primeira fotografia. ModernImageCodecAvailable reporta se um backend pode ser usado, e SetModernImageCodecLibrary aponta para um ficheiro ou diretório explícito quando a implantação coloca o codec algures fora do padrão:

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

Fique atento ao tamanho do ficheiro do resultado. Uma imagem RGBA de 16 bits com um perfil incorporado é um objeto de imagem PDF grande, e um relatório com quarenta destas será grande. Quando o documento se destina a visualização em ecrã e não a impressão, reduzir a resolução antes de incorporar é a opção certa, e as alavancas gerais de tamanho estão descritas em otimização do tamanho de ficheiro PDF

Por fim, decida a política de cor deliberadamente. Manter o perfil de origem é correto para trabalho de arquivo e de impressão; converter para um espaço à escala do documento é correto quando um conjunto misto de fotografias tem de parecer consistente, e o percurso de conversão está descrito em recolorir um documento para outro espaço de cor. Se precisar de confirmar o que realmente ficou no ficheiro, o percurso de inspeção em extração de texto, imagem e fonte reporta os objetos de imagem que um documento transporta

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