Technical Article

Compressione nativa di immagini JBIG2 nei PDF Delphi

Un contratto scansionato consiste in poche centinaia di punti per pollice di inchiostro nero su carta bianca. Salvato come bitmap a un bit per pixel è già piccolo, ma cento di queste pagine ingrandiscono comunque un PDF oltre le dimensioni adatte per l'invio via email. Il filtro giusto cambia questa aritmetica. JBIG2 è la compressione con il rapporto più elevato che lo standard ISO 32000-1 definisce per le immagini a due livelli e, su una pila di testo scansionato, dimezza regolarmente ciò che produce CCITT Group 4. Questo è il filtro da utilizzare quando l'input è inviato via fax, scansionato o altrimenti ridotto a due colori, e HotPDF può scriverlo direttamente in un PDF

Il formato ottiene questo rapporto grazie a due idee che un codec immagine generico non possiede. Modella il modo in cui i tratti neri si posizionano su uno sfondo bianco e nota che una pagina scansionata è per lo più costituita dalle stesse poche centinaia di forme di glifi ripetute migliaia di volte. Comprendere entrambe le cose è ciò che consente di scegliere consapevolmente le opzioni di codifica invece di tirare a indovinare

Dove si colloca JBIG2 nelle specifiche PDF

Lo standard ISO 32000-1 elenca JBIG2Decode tra i filtri di flusso nella sezione 7.4.7, disponibile a partire da PDF 1.4. Si applica a un solo elemento: gli XObject immagine in cui /BitsPerComponent è 1 e il cui spazio colore si risolve in un singolo canale. Questo è il punto fondamentale. JBIG2 è un codec a due livelli, quindi non compete mai con DCT o JPXDecode sulle fotografie. Compete invece con CCITTFaxDecode, i filtri fax Group 3 e Group 4, esattamente sul tipo di pagina a due toni prodotta da uno scanner di documenti

Il decoder consuma l'organizzazione JBIG2 incorporata che lo standard chiama profilo PDF, in cui ogni flusso di immagine contiene una sequenza di segmenti anziché un semplice flusso di bit. Un flusso opzionale /JBIG2Globals trasporta segmenti condivisi tra più immagini nello stesso documento, che è il meccanismo che consente di memorizzare il contenuto ripetuto una sola volta per un intero file anziché una volta per pagina. HotPDF emette il flusso per singola immagine per impostazione predefinita e mantiene libero il canale globale a meno che un backend non lo richieda

L'architettura dell'encoder incentrata sui backend

Un encoder JBIG2 completo è un componente software di grandi dimensioni e le sue parti più aggressive sono state storicamente gravate da brevetti e distribuite con licenze non adatte a ogni prodotto. HotPDF risolve questa tensione separando l'interfaccia dal motore. L'unità HPDFJBIG2 definisce le chiamate effettuate dal resto della libreria e include un modesto encoder integrato in modo che JBIG2 funzioni fin dall'inizio. Quando si necessita di rapporti di compressione di livello produttivo, si registra un motore più potente e la libreria gli delega il lavoro, senza alcuna modifica al codice chiamante

Il passaggio è una singola chiamata di registrazione. Senza alcun backend registrato, l'encoder ripiega sul suo percorso integrato. Registrandone uno, ogni codifica successiva viene eseguita tramite esso

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;

Lo stesso hook esiste per la decodifica tramite RegisterJBIG2DecoderBackend, con IsJBIG2DecoderBackendAvailable per verificarlo. Questo è il motivo per cui una libreria viene distribuita con un piccolo percorso integrato unito a un punto di giunzione per i backend, invece di un unico encoder monolitico. Il percorso integrato mantiene il file binario leggero e libero da complicazioni di licenza, mentre il punto di giunzione consente a un team che ha concesso in licenza un encoder completo di inserirlo senza toccare minimamente il livello di scrittura del PDF

Cosa comportano realmente le opzioni di codifica

La codifica viene configurata tramite TJBIG2EncodeOptions, un record con i campi Lossless, UseGlobalSegments, UseSymbolDictionary e LossyLevel. Il wrapper compatibile con i componenti THPDFJBIG2Options pubblica Lossless, UseSymbolDictionary e LossyLevel in modo che possano essere impostati dall'Object Inspector e si converte internamente nel record. Tre intenti guidano le impostazioni

La ricostruzione senza perdita di dati (lossless) conserva ogni pixel. Impostando Lossless a True e lasciando LossyLevel a zero, la bitmap decodificata risulta identica bit per bit all'input. Questa è l'unica scelta sicura per disegni al tratto, disegni tecnici e qualsiasi pagina in cui un pixel mancante potrebbe alterare il significato, come una firma o un timbro. La codifica con dizionario dei simboli attiva la deduplicazione sensibile al testo ed è l'opzione che distingue JBIG2 dai filtri fax. Il livello di perdita (lossy level), un numero intero da 0 a 9, permette a un backend capace di scambiare la fedeltà con le dimensioni trattando segni quasi identici come lo stesso simbolo. Zero significa senza perdita di dati. L'encoder integrato onora solo il percorso lossless e ignora qualsiasi livello lossy diverso da zero, quindi i livelli più alti hanno effetto solo una volta registrato un backend che li implementa

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;

I dizionari dei simboli e perché le scansioni di testo vincono

Una pagina di testo scansionato non è in realtà un'immagine di parole. È la stessa lettera e stampata diverse centinaia di volte, la stessa t, la stessa virgola, dove ogni istanza è una copia leggermente rumorosa di un'unica forma sottostante. Un dizionario dei simboli cattura questa struttura. L'encoder raccoglie i segni distinti sulla pagina in un dizionario, memorizza ogni forma una volta, e poi registra la pagina come un elenco di posizioni che fanno riferimento alle voci del dizionario. Mille occorrenze dello stesso glifo costano una bitmap memorizzata più mille posizionamenti economici

È proprio qui che JBIG2 supera CCITT Group 4. Group 4 codifica ogni linea di scansione rispetto a quella superiore senza alcuna nozione di glifo, quindi paga l'intero costo di ogni lettera ogni volta che questa appare. JBIG2 paga una sola volta. Quando lo stesso dizionario viene promosso al flusso globale a livello di documento, il risparmio si moltiplica per una scansione multipagina, poiché le forme condivise da una pagina all'altra vengono memorizzate una sola volta per l'intero file. Su testi densi la differenza non è marginale. È il motivo per cui esiste JBIG2

Regione generica e MMR per tutto il resto

Non tutte le immagini a due livelli sono testo. Mappe, schemi, disegni ingegneristici e pagine miste contengono disegni al tratto che nessun dizionario può riassumere. Per questi casi, JBIG2 codifica una regione generica, un rettangolo di pixel compresso direttamente senza alcun addestramento sui simboli. Lo standard consente a una regione generica di utilizzare MMR, la codifica READ modificata modificata che il fax Group 4 già utilizza, la quale modella ogni riga di pixel rispetto alla riga superiore

Questo è il percorso che HotPDF fornisce nel suo encoder integrato. Quando non è registrato alcun backend e la richiesta è lossless, la libreria comprime la bitmap come una singola regione generica MMR e la avvolge nella struttura del segmento JBIG2 richiesta dal profilo PDF. Non ha bisogno di alcun dizionario, di alcun passaggio di addestramento e di nessuna seconda immagine a cui fare riferimento, quindi è l'impostazione predefinita affidabile per i disegni al tratto e i contenuti a due livelli misti. Non eguaglierà un encoder completo con dizionario dei simboli sul testo puro, ma è sempre corretto, sempre senza perdita di dati e sempre presente. La superficie dell'encoder per questo richiede una sola chiamata

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;

Attivazione durante la creazione di un documento

Per l'uso quotidiano non si interagisce direttamente con la classe dell'encoder. HotPDF espone JBIG2 come scelta per la compressione delle immagini nel documento. L'enumerazione THPDFImageCompressionType include icJBIG2 insieme alle opzioni Flate, JPEG e CCITT, e il documento possiede una proprietà JBIG2Options di tipo THPDFJBIG2Options che contiene le impostazioni utilizzate quando viene selezionata tale compressione. Configurarle entrambe prima di aggiungere le immagini a due livelli che si desidera comprimere in questo 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;

Una comodità degna di nota è il componente aggiuntivo DBGridHotPDFExport, che esegue il rendering di un TDBGrid direttamente in un PDF. Il suo output è in gran parte costituito da regole e testo a due livelli, quindi un documento configurato per JBIG2 mantiene tali esportazioni compatte senza alcuna gestione aggiuntiva da parte dell'utente. Due argomenti correlati su questo blog approfondiscono il flusso di lavoro circostante. Per scoprire come vengono inseriti immagini e font durante la creazione di report, si veda l'output di report con font e immagini in Delphi. Quando un documento compresso deve soddisfare un profilo di archiviazione, le regole descritte in validazione PDF/A, PDF/X e PDF/UA in Delphi indicano quali filtri sono accettati per un determinato livello di conformità. JBIG2 viene fornito come parte di HotPDF Component per Delphi e C++Builder, insieme alle API di caricamento, modifica e crittografia trattate altrove in questa sede