Artigo Técnico

Compressão de Imagens Bilevel JBIG2 Nativa em PDFs Delphi

Um contrato digitalizado é composto de algumas centenas de pontos por polegada de tinta preta sobre papel branco. Armazenado como um bitmap de um bit por pixel já é pequeno, porém uma centena dessas páginas ainda infla um PDF além do que você enviaria por e-mail. O filtro certo muda a aritmética. JBIG2 é a compressão de maior taxa que a ISO 32000-1 define para imagens bilevel, e em uma pilha de texto digitalizado rotineiramente reduz pela metade o que o CCITT Grupo 4 produz. Este é o filtro a ser utilizado quando a entrada é faxeada, digitalizada ou de outra forma reduzida a duas cores, e o HotPDF pode gravá-lo diretamente em um PDF

O formato obtém essa taxa com duas ideias que um codec de imagem genérico não possui. Ele modela como as sequências pretas se situam contra um fundo branco, e percebe que uma página digitalizada é composta principalmente das mesmas poucas centenas de formas de glifos repetidas milhares de vezes. Entender ambas é o que permite escolher as opções de codificação deliberadamente em vez de adivinhar

Onde o JBIG2 se posiciona na especificação PDF

A ISO 32000-1 lista o JBIG2Decode entre os filtros de stream no §7.4.7, disponível a partir do PDF 1.4. Ele se aplica a um único lugar: XObjects de imagem cujo /BitsPerComponent é 1 e cujo espaço de cores se resolve para um único canal. Esse é o ponto central. JBIG2 é um codec bilevel, portanto nunca compete com DCT ou JPXDecode em fotografias. Compete com CCITTFaxDecode, os filtros de fax Grupo 3 e Grupo 4, exatamente no tipo de página de dois tons que um scanner de documentos produz

O decodificador consome a organização JBIG2 incorporada que o padrão chama de perfil PDF, onde cada stream de imagem contém uma sequência de segmentos em vez de um fluxo de bits puro. Um stream /JBIG2Globals opcional carrega segmentos compartilhados entre várias imagens no mesmo documento, que é o mecanismo que permite que conteúdo repetido seja armazenado uma vez para um arquivo inteiro em vez de uma vez por página. O HotPDF emite o stream por imagem por padrão e mantém o canal de globals livre, a menos que um backend o solicite

A arquitetura de codificador com backend plugável

Um codificador JBIG2 completo é um software de grande porte, e as partes mais agressivas dele foram historicamente oneradas por patentes e distribuídas sob licenças que não se adequam a todos os produtos. O HotPDF resolve essa tensão separando a interface do motor. A unit HPDFJBIG2 define as chamadas que o restante da biblioteca faz, e fornece um codificador embutido modesto para que o JBIG2 funcione imediatamente. Quando você precisa de taxas de nível de produção, você registra um motor mais potente e a biblioteca delega a ele, sem nenhuma alteração no seu código de chamada

A troca é uma única chamada de registro. Sem nenhum backend registrado, o codificador recorre ao seu caminho embutido. Registre um e toda codificação subsequente roda por ele

Diagrama da costura do codificador JBIG2 do HotPDF no HPDFJBIG2, que entrega a codificação a um backend registrado licenciado que honra LossyLevel 0 a 9 quando um está instalado, e caso contrário usa a região genérica MMR lossless embutida antes de envolver a saída em um image XObject JBIG2Decode com JBIG2Globals opcional
De fábrica, o caminho MMR integrado atende a todas as chamadas, enquanto uma engine licenciada assume no momento em que se registra através da emenda
uses
  HPDFJBIG2;

// Consulte o que está ativo e, se quiser, instale um engine mais forte
if not IsJBIG2EncoderBackendAvailable then
  // Backend de produção ausente: o HotPDF usa o caminho MMR interno
  RegisterJBIG2EncoderBackend(MyVendorJBIG2Encode);

// Depois, para voltar ao comportamento interno:
// ClearJBIG2Backends;

O mesmo gancho existe para decodificação através de RegisterJBIG2DecoderBackend, com IsJBIG2DecoderBackendAvailable para sondá-lo. É por isso que uma biblioteca fornece um pequeno caminho embutido mais uma costura de backend em vez de um codificador monolítico. O caminho embutido mantém o binário enxuto e livre de complicações de licença, enquanto a costura permite que uma equipe que licenciou um codificador completo o conecte sem tocar na camada de escrita de PDF

O que as opções de codificação realmente trocam

A codificação é configurada através de TJBIG2EncodeOptions, um registro com os campos Lossless, UseGlobalSegments, UseSymbolDictionary e LossyLevel. O wrapper amigável para componentes THPDFJBIG2Options publica Lossless, UseSymbolDictionary e LossyLevel para que possam ser definidos no Object Inspector, e converte para o registro internamente. Três intenções guiam as configurações

A reconstrução sem perdas mantém todos os pixels. Defina Lossless como True e deixe LossyLevel em zero, e o bitmap decodificado é idêntico bit a bit à entrada. Esta é a única escolha segura para arte vetorial, desenhos técnicos e qualquer página onde um pixel eliminado pudesse mudar o significado, como uma assinatura ou um carimbo. A codificação de dicionário de símbolos ativa a deduplicação com reconhecimento de texto e é a opção que separa o JBIG2 dos filtros de fax. O nível de perda, um inteiro de 0 a 9, permite que um backend capaz troque fidelidade por tamanho, tratando marcas quase idênticas como o mesmo símbolo. Zero significa sem perdas. O codificador embutido respeita apenas o caminho sem perdas e ignora qualquer nível de perda diferente de zero, portanto os níveis mais altos só têm efeito quando um backend que os implementa é registrado

var
  Options: TJBIG2EncodeOptions;
begin
  Options := DefaultJBIG2EncodeOptions;   // Lossless True, symbol dictionary ligado
  Options.Lossless := True;
  Options.LossyLevel := 0;                // 0 mantém todo pixel
  Options.UseSymbolDictionary := True;    // deduplica glyphs repetidos
  // Passe Options para um backend, ou deixe o THPDFJBIG2Options carregar
end;

Dicionários de símbolos e por que digitalizações de texto ganham

Uma página de texto digitalizado não é realmente uma imagem de palavras. É a mesma letra e impressa várias centenas de vezes, o mesmo t, a mesma vírgula, cada instância uma cópia levemente ruidosa de uma forma subjacente. Um dicionário de símbolos captura essa estrutura. O codificador coleta as marcas distintas na página em um dicionário, armazena cada forma uma vez e depois registra a página como uma lista de posições que referenciam entradas do dicionário. Mil ocorrências do mesmo glifo custam um bitmap armazenado mais mil posicionamentos baratos

É precisamente aqui que o JBIG2 supera o CCITT Grupo 4. O Grupo 4 codifica cada linha de varredura contra a linha acima sem noção de um glifo, portanto paga o custo total de cada letra cada vez que ela aparece. O JBIG2 paga uma vez. Quando o mesmo dicionário é promovido ao stream de globals de nível de documento, a economia se multiplica em uma digitalização de várias páginas, porque as formas compartilhadas por página após página são armazenadas uma única vez para o arquivo inteiro. Em texto denso, a diferença não é marginal. É a razão pela qual o JBIG2 existe

Diagrama HotPDF comparando como o CCITT Group 4 cobra cada ocorrência de glifo separadamente enquanto o JBIG2 armazena cada forma distinta uma vez em um dicionário de símbolos e a posiciona por referência barata, promovendo formas compartilhadas ao fluxo JBIG2Globals de nível de documento, de modo que digitalizações multipágina são armazenadas uma vez por forma
Glifos repetidos se assentam em um dicionário, de modo que a página cobra um bitmap por forma mais posicionamentos baratos, e globals estendem a economia através das páginas

Região genérica e MMR para tudo o mais

Nem toda imagem bilevel é texto. Mapas, esquemas, desenhos de engenharia e páginas mistas têm arte vetorial que nenhum dicionário pode resumir. Para esses casos, o JBIG2 codifica uma região genérica, um retângulo de pixels comprimidos diretamente sem nenhum treinamento de símbolo. O padrão permite que uma região genérica use MMR, a codificação modified modified READ que o fax Grupo 4 já usa, que modela cada linha de pixels contra a linha acima

Este é o caminho que o HotPDF fornece em seu codificador embutido. Quando nenhum backend é registrado e a solicitação é sem perdas, a biblioteca comprime o bitmap como uma única região genérica MMR e o envolve na estrutura de segmento JBIG2 que o perfil PDF exige. Não precisa de dicionário, nem de passagem de treinamento, nem de uma segunda imagem para referenciar, portanto é o padrão confiável para arte vetorial e conteúdo bilevel misto. Não igualará um codificador de dicionário de símbolos completo em texto puro, mas é sempre correto, sempre sem perdas e sempre presente. A superfície do codificador para ele é uma única chamada

var
  Encoder: THPDFJBIG2Encoder;
  ImageData: TJBIG2ByteArray;
  Scanlines: TJBIG2ScanlineArray;  // um array de bytes por linha, MSB primeiro
  W, H: Integer;
begin
  // Scanlines, W e H descrevem uma página de 1 bit; cada linha tem (W + 7) div 8 bytes
  Encoder := THPDFJBIG2Encoder.Create;
  try
    if Encoder.EncodeToByteArray(Scanlines, W, H, ImageData) then
      // ImageData agora tem um stream JBIG2 pronto para um XObject /JBIG2Decode
      ;
  finally
    Encoder.Free;
  end;
end;

Ativando ao construir um documento

Para uso cotidiano, você não toca diretamente na classe do codificador. O HotPDF expõe JBIG2 como uma opção de compressão de imagem no documento. A enumeração THPDFImageCompressionType inclui icJBIG2 ao lado das opções Flate, JPEG e CCITT, e o documento carrega uma propriedade JBIG2Options do tipo THPDFJBIG2Options que contém as configurações usadas quando essa compressão é selecionada. Configure ambas antes de adicionar as imagens bilevel que deseja comprimir desta forma

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.ImageCompressionType := icJBIG2;     // manda imagens de 1 bit pelo JBIG2
    Pdf.JBIG2Options.Lossless := True;        // mantém todo pixel
    Pdf.JBIG2Options.UseSymbolDictionary := True;
    Pdf.JBIG2Options.LossyLevel := 0;
    // Adicione páginas e coloque aqui suas imagens digitalizadas de 1 bit
  finally
    Pdf.Free;
  end;
end;

Uma conveniência a destacar é o add-on DBGridHotPDFExport, que renderiza um TDBGrid diretamente para um PDF. Sua saída é em grande parte regras bilevel e texto, portanto um documento configurado para JBIG2 mantém essas exportações compactas sem nenhum manuseio extra da sua parte. Dois tópicos relacionados neste blog aprofundam o fluxo de trabalho ao redor. Para como imagens e fontes são dispostas ao criar relatórios, veja saída de relatórios com fontes e imagens no Delphi. Quando um documento comprimido deve satisfazer um perfil de arquivamento, as regras em validação de PDF/A, PDF/X e PDF/UA no Delphi indicam quais filtros um determinado nível de conformidade aceita. O JBIG2 é fornecido como parte do HotPDF Delphi Component para Delphi e C++Builder, ao lado das APIs de carregamento, edição e criptografia cobertas em outros lugares aqui