Technical Article

Compresión de imágenes JBIG2 en PDFs de Delphi con HotPDF

Un contrato escaneado son unos cientos de puntos por pulgada de tinta negra sobre papel blanco. Almacenado como mapa de bits de un bit por píxel ya es pequeño, pero un centenar de páginas así todavía hincha un PDF más allá de lo que se puede enviar por correo. El filtro adecuado cambia la ecuación. JBIG2 es la compresión de mayor ratio que ISO 32000-1 define para imágenes bitonales, y en un conjunto de texto escaneado suele reducir a la mitad lo que produce CCITT Group 4. Es el filtro indicado cuando la entrada es un fax, un escaneo o cualquier imagen reducida a dos colores, y HotPDF puede escribirlo directamente en un PDF

El formato logra esa ratio con dos ideas que un códec de imagen genérico no tiene. Modela cómo se distribuyen las marcas negras sobre el fondo blanco y detecta que una página escaneada está formada por los mismos pocos cientos de formas de glifos repetidos miles de veces. Comprender ambas ideas es lo que permite elegir las opciones de codificación de forma deliberada en lugar de adivinarlas

El lugar de JBIG2 en la especificación PDF

ISO 32000-1 incluye JBIG2Decode entre los filtros de flujo en §7.4.7, disponible desde PDF 1.4 en adelante. Se aplica en un único contexto: XObjects de imagen cuyo /BitsPerComponent es 1 y cuyo espacio de color se resuelve en un único canal. Ese es precisamente su propósito. JBIG2 es un códec bitonal, por lo que nunca compite con DCT o JPXDecode en fotografías. Compite con CCITTFaxDecode, los filtros de fax Group 3 y Group 4, exactamente en el tipo de página bicolor que produce un escáner de documentos

El decodificador consume la organización JBIG2 embebida que el estándar denomina perfil PDF, donde cada flujo de imagen contiene una secuencia de segmentos en lugar de un flujo de bits sin formato. Un flujo opcional /JBIG2Globals transporta segmentos compartidos entre varias imágenes del mismo documento, lo que permite almacenar el contenido repetido una sola vez para todo el archivo en lugar de una vez por página. HotPDF emite el flujo por imagen de forma predeterminada y mantiene el canal de globales libre salvo que un backend lo solicite

La arquitectura de codificador basada en backends

Un codificador JBIG2 completo es una pieza de software de gran envergadura, y sus partes más agresivas han estado históricamente gravadas por patentes y distribuidas bajo licencias que no se adaptan a todos los productos. HotPDF resuelve esa tensión separando la interfaz del motor. La unidad HPDFJBIG2 define las llamadas que el resto de la biblioteca realiza, e incluye un codificador interno modesto para que JBIG2 funcione desde el primer momento. Cuando se necesitan ratios de producción, se registra un motor más potente y la biblioteca le delega el trabajo sin ningún cambio en el código de llamada

El cambio es una única llamada de registro. Sin ningún backend registrado, el codificador recurre a su ruta interna. Al registrar uno, todas las codificaciones posteriores pasan a través de él

uses
  HPDFJBIG2;

// Query what is active, then optionally install a stronger engine.
if not IsJBIG2EncoderBackendAvailable then
  // Production backend not present: HotPDF uses its built-in MMR path.
  RegisterJBIG2EncoderBackend(MyVendorJBIG2Encode);

// Later, to return to the built-in behaviour:
// ClearJBIG2Backends;

El mismo gancho existe para la decodificación mediante RegisterJBIG2DecoderBackend, con IsJBIG2DecoderBackendAvailable para comprobarlo. Por eso una biblioteca incluye una ruta interna pequeña más una junta de backend en lugar de un único codificador monolítico. La ruta interna mantiene el binario ligero y libre de compromisos de licencia, mientras que la junta permite que un equipo que ha adquirido licencia de un codificador completo lo integre sin tocar en absoluto la capa de escritura de PDF

Qué intercambian realmente las opciones de codificación

La codificación se configura mediante TJBIG2EncodeOptions, un registro con los campos Lossless, UseGlobalSegments, UseSymbolDictionary y LossyLevel. El contenedor amigable con componentes THPDFJBIG2Options publica Lossless, UseSymbolDictionary y LossyLevel para que puedan establecerse desde el Object Inspector, y realiza la conversión al registro de forma interna. Tres intenciones guían la configuración

La reconstrucción sin pérdida conserva cada píxel. Si se establece Lossless en True y se deja LossyLevel en cero, el mapa de bits decodificado es idéntico bit a bit a la entrada. Es la única opción segura para arte lineal, dibujos técnicos y cualquier página donde un píxel perdido podría cambiar el significado, como una firma o un sello. La codificación con diccionario de símbolos activa la deduplicación consciente del texto y es la opción que diferencia JBIG2 de los filtros de fax. El nivel con pérdida, un entero de 0 a 9, permite que un backend capaz intercambie fidelidad por tamaño tratando marcas casi idénticas como el mismo símbolo. Cero significa sin pérdida. El codificador interno solo respeta la ruta sin pérdida e ignora cualquier nivel con pérdida distinto de cero, por lo que los niveles superiores solo surten efecto una vez registrado un backend que los implemente

var
  Options: TJBIG2EncodeOptions;
begin
  Options := DefaultJBIG2EncodeOptions;   // Lossless True, symbol dictionary on
  Options.Lossless := True;
  Options.LossyLevel := 0;                // 0 keeps every pixel
  Options.UseSymbolDictionary := True;    // dedupe repeated glyphs
  // Pass Options to a backend, or let THPDFJBIG2Options carry them.
end;

Diccionarios de símbolos y por qué los escaneos de texto ganan

Una página de texto escaneado no es realmente una imagen de palabras. Es la misma letra e impresa varios cientos de veces, la misma t, la misma coma, cada instancia una copia ligeramente ruidosa de una forma subyacente. Un diccionario de símbolos captura esa estructura. El codificador recopila las marcas distintas de la página en un diccionario, almacena cada forma una sola vez y luego registra la página como una lista de posiciones que hacen referencia a entradas del diccionario. Mil apariciones del mismo glifo cuestan un mapa de bits almacenado más mil ubicaciones de bajo coste

Precisamente aquí es donde JBIG2 supera a CCITT Group 4. Group 4 codifica cada línea de escaneo contra la línea anterior sin ninguna noción de glifo, por lo que paga el coste completo de cada letra cada vez que aparece. JBIG2 paga solo una vez. Cuando el mismo diccionario se promueve al flujo de globales a nivel de documento, el ahorro se multiplica en un escaneo de varias páginas, porque las formas compartidas página tras página se almacenan una única vez para todo el archivo. En texto denso la diferencia no es marginal: es la razón por la que existe JBIG2

Región genérica y MMR para todo lo demás

No toda imagen bitonal es texto. Mapas, esquemas, planos de ingeniería y páginas mixtas tienen arte lineal que ningún diccionario puede resumir. Para esos casos, JBIG2 codifica una región genérica, un rectángulo de píxeles comprimido directamente sin ningún entrenamiento de símbolos. El estándar permite que una región genérica use MMR, la codificación READ modificada que ya usa el fax Group 4, que modela cada fila de píxeles contra la fila superior

Esta es la ruta que HotPDF incluye en su codificador interno. Cuando no hay ningún backend registrado y la solicitud es sin pérdida, la biblioteca comprime el mapa de bits como una única región genérica MMR y lo envuelve en la estructura de segmentos JBIG2 que requiere el perfil PDF. No necesita diccionario, ni pasada de entrenamiento, ni una segunda imagen de referencia, por lo que es el modo predeterminado fiable para arte lineal y contenido bitonal mixto. No igualará a un codificador completo con diccionario de símbolos en texto puro, pero siempre es correcto, siempre sin pérdida y siempre está disponible. La superficie de codificación se reduce a una sola llamada

var
  Encoder: THPDFJBIG2Encoder;
  ImageData: TJBIG2ByteArray;
  Scanlines: TJBIG2ScanlineArray;  // one byte array per row, MSB-first
  W, H: Integer;
begin
  // Scanlines, W and H describe a 1-bit page; each row is (W + 7) div 8 bytes.
  Encoder := THPDFJBIG2Encoder.Create;
  try
    if Encoder.EncodeToByteArray(Scanlines, W, H, ImageData) then
      // ImageData now holds a JBIG2 stream ready for a /JBIG2Decode XObject.
      ;
  finally
    Encoder.Free;
  end;
end;

Activarlo al construir un documento

Para el uso cotidiano no es necesario trabajar directamente con la clase del codificador. HotPDF expone JBIG2 como una opción de compresión de imagen en el documento. La enumeración THPDFImageCompressionType incluye icJBIG2 junto a las opciones Flate, JPEG y CCITT, y el documento dispone de una propiedad JBIG2Options de tipo THPDFJBIG2Options que contiene la configuración utilizada cuando se selecciona esa compresión. Configure ambas antes de añadir las imágenes bitonales que desea comprimir de este modo

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.ImageCompressionType := icJBIG2;     // route 1-bit images through JBIG2
    Pdf.JBIG2Options.Lossless := True;        // keep every pixel
    Pdf.JBIG2Options.UseSymbolDictionary := True;
    Pdf.JBIG2Options.LossyLevel := 0;
    // Add pages and place your scanned 1-bit images here.
  finally
    Pdf.Free;
  end;
end;

Vale la pena destacar el complemento DBGridHotPDFExport, que renderiza un TDBGrid directamente a PDF. Su salida consiste principalmente en líneas bitonales y texto, por lo que un documento configurado para JBIG2 mantiene esas exportaciones compactas sin ningún tratamiento adicional por su parte. Dos temas relacionados en este blog profundizan en el flujo de trabajo circundante. Para ver cómo se disponen imágenes y fuentes al generar informes, consulte salida de informes con fuentes e imágenes en Delphi. Cuando un documento comprimido debe cumplir un perfil de archivo, las reglas de validación PDF/A, PDF/X y PDF/UA en Delphi indican qué filtros acepta cada nivel de conformidad. JBIG2 se distribuye como parte del HotPDF Component para Delphi y C++Builder, junto a las API de carga, edición y cifrado descritas en otros artículos