Відсканований контракт - це кілька сотень точок на дюйм чорного чорнила на білому папері. Збережений як растрове зображення з одним бітом на піксель, він уже є невеликим, проте сотня таких сторінок все одно роздуває PDF до розмірів, які не зручно надсилати електронною поштою. Правильний фільтр змінює арифметику. JBIG2 - це стиснення з найвищим коефіцієнтом, яке ISO 32000-1 визначає для двоколірних (bilevel) зображень, і на стосі відсканованого тексту воно зазвичай вдвічі зменшує розмір, створений CCITT Group 4. Це той фільтр, до якого слід звертатися, коли вхідні дані отримані факсом, відскановані або іншим чином зведені до двох кольорів, і HotPDF може записувати його безпосередньо у PDF
Формат досягає такого коефіцієнта завдяки двом ідеям, яких не має звичайний кодек зображень. Він моделює те, як чорні серії розташовуються на білому тлі, і помічає, що відсканована сторінка - це переважно одні й ті самі кілька сотень форм гліфів, що повторюються тисячі разів. Розуміння обох цих аспектів дозволяє вам свідомо вибирати параметри кодування замість того, щоб вгадувати
Де знаходиться JBIG2 у специфікації PDF
ISO 32000-1 перелічує JBIG2Decode серед потокових фільтрів у §7.4.7, доступних починаючи з PDF 1.4. Він застосовується лише до одного місця: XObjects зображень, чий /BitsPerComponent дорівнює 1, і чий колірний простір зводиться до одного каналу. У цьому полягає вся суть. JBIG2 - це двоколірний кодек, тому він ніколи не конкурує з DCT або JPXDecode на фотографіях. Він конкурує з CCITTFaxDecode, факсовими фільтрами Group 3 та Group 4, саме на тих двоколірних сторінках, які створює сканер документів
Декодер споживає вбудовану структуру JBIG2, яку стандарт називає профілем PDF, де кожен потік зображення містить послідовність сегментів, а не простий бітовий потік. Необов'язковий потік /JBIG2Globals переносить сегменти, спільні для кількох зображень в одному документі, і це механізм, який дозволяє зберігати вміст, що повторюється, один раз для всього файлу, а не один раз для кожної сторінки. За замовчуванням HotPDF генерує потік для кожного зображення і залишає глобальний канал вільним, доки бекенд не зробить запит на нього
Архітектура кодувальника, орієнтована на бекенд
Повний кодувальник JBIG2 - це велика частина програмного забезпечення, і найбільш агресивні його частини історично були обтяжені патентами та постачалися за ліцензіями, які підходять не кожному продукту. HotPDF вирішує цю напруженість шляхом розділення інтерфейсу від рушія. Модуль HPDFJBIG2 визначає виклики, які робить решта бібліотеки, і він містить скромний вбудований кодувальник, завдяки чому JBIG2 працює одразу з коробки. Коли вам потрібні коефіцієнти виробничого рівня, ви реєструєте потужніший рушій, і бібліотека делегує йому роботу, не змінюючи ваш код виклику
Перемикання - це один виклик реєстрації. Якщо бекенд не зареєстровано, кодувальник повертається до свого вбудованого шляху. Зареєструйте один з них, і кожне наступне кодування проходитиме через нього
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;
Такий самий хук існує для декодування через RegisterJBIG2DecoderBackend, з можливістю перевірки за допомогою IsJBIG2DecoderBackendAvailable. Саме тому бібліотека постачається з невеликим вбудованим шляхом і стиком для бекенда, а не з одним монолітним кодувальником. Вбудований шлях зберігає бінарний файл компактним і вільним від ліцензійних ускладнень, у той час як стик дозволяє команді, яка ліцензувала повний кодувальник, підключити його взагалі не торкаючись рівня запису PDF
На що насправді впливають параметри кодування
Кодування налаштовується через TJBIG2EncodeOptions, запис із полями Lossless, UseGlobalSegments, UseSymbolDictionary та LossyLevel. Зручна для компонентів обгортка THPDFJBIG2Options публікує Lossless, UseSymbolDictionary та LossyLevel, щоб їх можна було встановити з Object Inspector, і внутрішньо перетворює їх на запис. Ці налаштування обумовлені трьома цілями
Реконструкція без втрат (lossless) зберігає кожен піксель. Встановіть значення Lossless у True та залиште LossyLevel на рівні нуля, і розкодоване растрове зображення буде побітово ідентичним до вхідного. Це єдиний безпечний вибір для штрихових ілюстрацій, технічних креслень та будь-якої сторінки, де пропущений піксель може змінити сенс, як-от підпис або штамп. Кодування за словником символів вмикає дедуплікацію з урахуванням тексту і є тією опцією, яка відрізняє JBIG2 від факсових фільтрів. Рівень втрат, ціле число від 0 до 9, дозволяє потужному бекенду жертвувати точністю заради розміру, розглядаючи майже ідентичні позначки як один і той самий символ. Нуль означає відсутність втрат. Вбудований кодувальник дотримується лише шляху без втрат і ігнорує будь-який ненульовий рівень втрат, тому вищі рівні набувають чинності лише після реєстрації бекенда, який їх реалізує
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;
Словники символів та чому текстові скани виграють
Сторінка відсканованого тексту насправді не є зображенням слів. Це та сама літера e, надрукована кілька сотень разів, та сама літера t, та сама кома, кожен екземпляр якої є дещо зашумленою копією однієї базової форми. Словник символів фіксує цю структуру. Кодувальник збирає унікальні позначки на сторінці в словник, зберігає кожну форму один раз, а потім записує сторінку як список позицій, які посилаються на записи в словнику. Тисяча появ одного й того самого гліфа обходиться в одне збережене растрове зображення плюс тисяча дешевих розміщень
Саме тут JBIG2 випереджає CCITT Group 4. Group 4 кодує кожну лінію сканування відносно рядка над нею без поняття про гліф, тому він сплачує повну вартість кожної літери кожного разу, коли літера з'являється. JBIG2 платить один раз. Коли один і той самий словник переноситься до глобального потоку на рівні документа, економія примножується при багатосторінковому скануванні, оскільки форми, які є спільними для сторінок, зберігаються один раз для всього файлу. На щільному тексті різниця не є маргінальною. Це і є причиною існування JBIG2
Загальний регіон (generic region) та MMR для всього іншого
Не кожне двоколірне зображення є текстом. Карти, схеми, інженерні креслення та змішані сторінки містять штрихові ілюстрації, які не може узагальнити жоден словник. Для них JBIG2 кодує загальний регіон (generic region) - прямокутник пікселів, стиснутий безпосередньо без жодного тренування на символах. Стандарт дозволяє загальному регіону використовувати MMR - кодування modified modified READ, яке вже використовується факсом Group 4 і яке моделює кожен рядок пікселів відносно рядка над ним
Саме цей шлях HotPDF постачає у своєму вбудованому кодувальнику. Коли не зареєстровано жодного бекенда, а запит здійснюється без втрат, бібліотека стискає растрове зображення як єдиний загальний регіон MMR і загортає його в структуру сегмента JBIG2, якої вимагає профіль PDF. Він не потребує ні словника, ні проходу тренування, ні другого зображення для посилання, тому є надійним варіантом за замовчуванням для штрихових ілюстрацій та змішаного двоколірного вмісту. Він не зрівняється з повноцінним кодувальником зі словником символів на чистому тексті, але він завжди коректний, завжди працює без втрат і завжди наявний. Інтерфейс кодувальника для нього - це один виклик
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;
Увімкнення під час створення документа
Для повсякденного використання ви не торкаєтесь класу кодувальника безпосередньо. HotPDF пропонує JBIG2 як вибір стиснення зображень для документа. Перелічення THPDFImageCompressionType включає icJBIG2 поруч з опціями Flate, JPEG і CCITT, а документ має властивість JBIG2Options типу THPDFJBIG2Options, яка містить параметри, що використовуються при виборі цього стиснення. Налаштуйте обидва перед тим, як додавати двоколірні зображення, які ви хочете стиснути у такий спосіб
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;
Одна зі зручностей, на яку варто звернути увагу, - це додаток DBGridHotPDFExport, який рендерить TDBGrid прямо у PDF. Його вихідні дані - це здебільшого двоколірні лінійки та текст, тому документ, налаштований на JBIG2, зберігає цей експорт компактним без будь-якої додаткової обробки з вашого боку. Дві пов'язані теми в цьому блозі детальніше розглядають супутній робочий процес. Про те, як розміщуються зображення та шрифти під час створення звітів, дивіться статтю виведення звітів зі шрифтами та зображеннями в Delphi. Коли стиснений документ має відповідати архівному профілю, правила, описані в статті перевірка PDF/A, PDF/X та PDF/UA в Delphi, розкажуть вам, які фільтри приймає певний рівень відповідності. JBIG2 постачається як частина HotPDF Component для Delphi та C++Builder, поруч з API для завантаження, редагування та шифрування, що описані в інших наших публікаціях