Zeskanowana umowa to kilkaset punktów na cal czarnego atramentu na białym papierze. Przechowywana jako bitmapa jeden bit na piksel jest już niewielka, ale sto takich stron nadal powiększa PDF ponad to, co wygodnie wysłać e-mailem. Właściwy filtr zmienia to równanie. JBIG2 jest kompresją o najwyższym współczynniku, jaką ISO 32000-1 definiuje dla obrazów dwutonowych, i na stosie zeskanowanego tekstu rutynowo zmniejsza rozmiar do połowy tego, co produkuje CCITT Group 4. To filtr, po który sięgasz, gdy dane wejściowe to faks, skan lub obraz inaczej sprowadzony do dwóch kolorów, a HotPDF może go zapisywać bezpośrednio w PDF
Format osiąga takie współczynniki dzięki dwóm pomysłom, których ogólny koder obrazów nie posiada. Modeluje on, jak czarne ciągi pikseli leżą na białym tle, i zauważa, że zeskanowana strona to w większości te same kilkaset kształtów glifów powtarzających się tysiące razy. Zrozumienie obu pozwala wybierać opcje kodowania świadomie, a nie na oślep
Miejsce JBIG2 w specyfikacji PDF
ISO 32000-1 wymienia JBIG2Decode wśród filtrów strumieni w §7.4.7, dostępny od PDF 1.4. Stosuje się go w jednym miejscu: do XObject-ów obrazów, których /BitsPerComponent wynosi 1 i których przestrzeń kolorów rozwiązuje się do jednego kanału. To właśnie sedno. JBIG2 to koder dwutonowy, więc nie konkuruje z DCT ani JPXDecode przy fotografiach. Konkuruje z CCITTFaxDecode, filtrami faksu Group 3 i Group 4, dokładnie na tego rodzaju dwutonowych stronach, jakie produkuje skaner dokumentów
Dekoder przetwarza osadzoną organizację JBIG2, którą standard nazywa profilem PDF, gdzie każdy strumień obrazu zawiera sekwencję segmentów zamiast gołego strumienia bitów. Opcjonalny strumień /JBIG2Globals niesie segmenty współdzielone przez kilka obrazów w tym samym dokumencie - to mechanizm pozwalający przechowywać wielokrotnie powtarzającą się treść raz dla całego pliku zamiast raz na stronę. HotPDF domyślnie emituje strumień per-obraz i pozostawia kanał globals wolny, chyba że backend o to poprosi
Architektura koder-backend
Kompletny koder JBIG2 to duże oprogramowanie, a jego najbardziej agresywne części były historycznie obciążone patentami i dystrybuowane na licencjach nieodpowiednich dla każdego produktu. HotPDF rozwiązuje to napięcie przez oddzielenie interfejsu od silnika. Jednostka HPDFJBIG2 definiuje wywołania, których używa reszta biblioteki, i dostarcza skromny wbudowany koder, żeby JBIG2 działał od razu po wyjęciu z pudełka. Gdy potrzebne są współczynniki klasy produkcyjnej, rejestruje się mocniejszy silnik i biblioteka deleguje do niego bez żadnych zmian w kodzie wywołującym
Przełącznik to jedno wywołanie rejestracji. Bez zarejestrowanego backendu koder wraca do wbudowanej ścieżki. Zarejestruj jeden, a każde kolejne kodowanie przebiega przez niego
uses
HPDFJBIG2;
// Sprawdź, co jest aktywne, opcjonalnie zainstaluj mocniejszy silnik.
if not IsJBIG2EncoderBackendAvailable then
// Backend produkcyjny niedostępny: HotPDF używa wbudowanej ścieżki MMR.
RegisterJBIG2EncoderBackend(MyVendorJBIG2Encode);
// Później, aby wrócić do wbudowanego zachowania:
// ClearJBIG2Backends;
Ten sam hook istnieje dla dekodowania przez RegisterJBIG2DecoderBackend, z IsJBIG2DecoderBackendAvailable do jego sprawdzenia. Właśnie dlatego biblioteka dostarcza małą wbudowaną ścieżkę plus szew backendu zamiast jednego monolitycznego kodera. Wbudowana ścieżka utrzymuje binaria małe i wolne od obciążeń licencyjnych, a szew pozwala zespołowi, który udzielił licencji na pełny koder, podłączyć go bez dotykania warstwy zapisującej PDF
Co tak naprawdę wymieniają opcje kodowania
Kodowanie jest konfigurowane przez TJBIG2EncodeOptions, rekord z polami Lossless, UseGlobalSegments, UseSymbolDictionary i LossyLevel. Przyjazny komponentowi wrapper THPDFJBIG2Options publikuje Lossless, UseSymbolDictionary i LossyLevel tak, by można je było ustawiać z Object Inspectora, i konwertuje do rekordu wewnętrznie. Trzy intencje sterują ustawieniami
Bezstratna rekonstrukcja zachowuje każdy piksel. Ustaw Lossless na True i pozostaw LossyLevel na zero, a zdekodowana bitmapa będzie bit w bit identyczna z danymi wejściowymi. To jedyny bezpieczny wybór dla grafiki liniowej, rysunków technicznych i każdej strony, gdzie pominięty piksel może zmienić znaczenie - podpisu czy pieczęci. Kodowanie słownikowe włącza deduplikację z uwzględnieniem tekstu i jest opcją odróżniającą JBIG2 od filtrów faksu. Poziom stratności, liczba całkowita od 0 do 9, pozwala zdolnemu backendowi wymieniać wierność na rozmiar, traktując niemal identyczne znaki jako ten sam symbol. Zero oznacza bezstratność. Wbudowany koder respektuje tylko bezstratną ścieżkę i ignoruje każdy niezerowy poziom stratności, więc wyższe poziomy działają dopiero po zarejestrowaniu backendu, który je implementuje
var
Options: TJBIG2EncodeOptions;
begin
Options := DefaultJBIG2EncodeOptions; // Lossless True, słownik symboli włączony
Options.Lossless := True;
Options.LossyLevel := 0; // 0 zachowuje każdy piksel
Options.UseSymbolDictionary := True; // deduplikuj powtarzające się glify
// Przekaż Options do backendu lub niech THPDFJBIG2Options je przeniesie.
end;
Słowniki symboli i dlaczego skany tekstu wygrywają
Strona zeskanowanego tekstu to w rzeczywistości nie obraz słów. To ta sama litera e wydrukowana kilkaset razy, to samo t, ten sam przecinek - każda instancja to nieco zaszumiona kopia jednego podstawowego kształtu. Słownik symboli chwyta tę strukturę. Koder zbiera odrębne znaki na stronie do słownika, przechowuje każdy kształt raz, a następnie zapisuje stronę jako listę pozycji odnoszących się do wpisów słownika. Tysiąc wystąpień tego samego glifu kosztuje jedną przechowywaną bitmapę plus tysiąc tanich umieszczenia
Właśnie tutaj JBIG2 wyprzedza CCITT Group 4. Group 4 koduje każdą linię skanowania względem linii powyżej bez pojęcia o glifie, płacąc pełny koszt każdej litery za każdym razem, gdy się pojawia. JBIG2 płaci raz. Gdy ten sam słownik jest promowany do strumienia globals na poziomie dokumentu, oszczędność kumuluje się na wielu stronach skanu, bo kształty wspólne dla kolejnych stron są przechowywane raz dla całego pliku. Przy gęstym tekście różnica nie jest marginalna - to właśnie powód istnienia JBIG2
Region ogólny i MMR dla pozostałych przypadków
Nie każdy obraz dwutonowy to tekst. Mapy, schematy, rysunki techniczne i strony mieszane mają grafikę liniową, której żaden słownik nie podsumuje. Dla nich JBIG2 koduje region ogólny - prostokąt pikseli skompresowany bezpośrednio bez żadnego trenowania symboli. Standard pozwala regionowi ogólnemu używać MMR, zmodyfikowanego kodowania READ używanego już przez faks Group 4, które modeluje każdy wiersz pikseli względem wiersza powyżej
To ścieżka, którą HotPDF dostarcza we wbudowanym koderze. Gdy żaden backend nie jest zarejestrowany i żądanie jest bezstratne, biblioteka kompresuje bitmapę jako jeden ogólny region MMR i owija go w strukturę segmentów JBIG2, jakiej wymaga profil PDF. Nie potrzebuje słownika, przebiegu treningowego ani drugiego obrazu do referencji, więc jest niezawodnym domyślnym dla grafiki liniowej i mieszanej dwutonowej treści. Nie dorówna pełnemu koderowi słownikowemu na czystym tekście, ale jest zawsze poprawny, zawsze bezstratny i zawsze dostępny. Interfejs kodera to jedno wywołanie
var
Encoder: THPDFJBIG2Encoder;
ImageData: TJBIG2ByteArray;
Scanlines: TJBIG2ScanlineArray; // jedna tablica bajtów na wiersz, MSB-first
W, H: Integer;
begin
// Scanlines, W i H opisują stronę 1-bitową; każdy wiersz to (W + 7) div 8 bajtów.
Encoder := THPDFJBIG2Encoder.Create;
try
if Encoder.EncodeToByteArray(Scanlines, W, H, ImageData) then
// ImageData zawiera teraz strumień JBIG2 gotowy do XObject /JBIG2Decode.
;
finally
Encoder.Free;
end;
end;
Włączanie przy budowaniu dokumentu
Do codziennego użytku nie dotykasz bezpośrednio klasy kodera. HotPDF udostępnia JBIG2 jako wybór kompresji obrazów w dokumencie. Wyliczenie THPDFImageCompressionType zawiera icJBIG2 obok opcji Flate, JPEG i CCITT, a dokument niesie właściwość JBIG2Options typu THPDFJBIG2Options z ustawieniami używanymi przy wyborze tej kompresji. Skonfiguruj oba przed dodaniem obrazów dwutonowych, które chcesz skompresować w ten sposób
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.ImageCompressionType := icJBIG2; // kieruj obrazy 1-bitowe przez JBIG2
Pdf.JBIG2Options.Lossless := True; // zachowaj każdy piksel
Pdf.JBIG2Options.UseSymbolDictionary := True;
Pdf.JBIG2Options.LossyLevel := 0;
// Dodaj strony i umieść zeskanowane obrazy 1-bitowe tutaj.
finally
Pdf.Free;
end;
end;
JBIG2 jest częścią komponentu HotPDF dla Delphi i C++Builder, obok API ładowania, edycji i szyfrowania omawianych w innych artykułach