חוזה סרוק מורכב מכמה מאות נקודות לאינץ' של דיו שחור על נייר לבן. כאשר הוא מאוחסן כמפת סיביות של סיבית אחת לפיקסל הוא כבר קטן, אך מאה דפים כאלה עדיין ינפחו קובץ PDF מעבר לכל מה שתרצה לשלוח בדוא"ל. המסנן הנכון משנה את החשבון. JBIG2 הוא הדחיסה בעלת יחס הדחיסה הגבוה ביותר ש-ISO 32000-1 מגדיר עבור תמונות בשני צבעים (bilevel), ועל ערימה של טקסט סרוק הוא באופן שגרתי מקטין לחצי את מה ש-CCITT Group 4 מייצר. זהו המסנן שאליו יש לפנות כאשר הקלט נשלח בפקס, נסרק או צומצם בדרך אחרת לשני צבעים, ו-HotPDF יכול לכתוב אותו ישירות לתוך PDF
הפורמט משיג את היחס עם שני רעיונות שלקודק תמונות גנרי אין. הוא מודל את האופן שבו רצפים שחורים יושבים על רקע לבן, והוא מבחין שדף סרוק הוא ברובו אותן כמה מאות צורות של תווים שחוזרות על עצמן אלפי פעמים. ההבנה של שניהם היא מה שמאפשר לך לבחור את אפשרויות הקידוד במכוון במקום לנחש
היכן ממוקם JBIG2 במפרט ה-PDF
תקן ISO 32000-1 מונה את JBIG2Decode בין מסנני הזרם בסעיף 7.4.7, הזמין החל מ-PDF 1.4 ואילך. הוא חל על מקום אחד בלבד: אובייקטי XObject של תמונה שה-/BitsPerComponent שלהם הוא 1 ושמרחב הצבע שלהם הופך לערוץ בודד. זו כל הנקודה. JBIG2 הוא קודק בשני צבעים (bilevel), כך שהוא לעולם אינו מתחרה ב-DCT או JPXDecode בתצלומים. הוא מתחרה ב-CCITTFaxDecode, מסנני הפקס של Group 3 ו-Group 4, בדיוק על סוג הדפים בשני גוונים שסורק מסמכים מייצר
המפענח צורך את ארגון ה-JBIG2 המוטמע שהתקן מכנה פרופיל ה-PDF, שבו כל זרם תמונה מכיל רצף של מקטעים (segments) במקום זרם סיביות חשוף. זרם /JBIG2Globals אופציונלי נושא מקטעים המשותפים למספר תמונות באותו מסמך, וזהו המנגנון שמאפשר לשמור תוכן שחוזר על עצמו פעם אחת עבור קובץ שלם במקום פעם אחת לכל דף. HotPDF פולט את הזרם לכל תמונה כברירת מחדל ומשאיר את ערוץ הגלובליים פנוי אלא אם כן מנוע אחורי (backend) מבקש זאת
ארכיטקטורת המקודד המבוססת על backend תחילה
מקודד JBIG2 מלא הוא פיסת תוכנה גדולה, והחלקים האגרסיביים ביותר שבו היו היסטורית כבולים בפטנטים ונמסרו תחת רישיונות שאינם מתאימים לכל מוצר. HotPDF פותרת את המתח הזה על ידי הפרדת הממשק מהמנוע. היחידה HPDFJBIG2 מגדירה את הקריאות ששאר הספרייה מבצעת, והיא מגיעה עם מקודד מובנה צנוע כך ש-JBIG2 פועל היישר מהקופסה. כאשר אתה זקוק ליחסי דחיסה ברמת ייצור, אתה רושם מנוע חזק יותר והספרייה מאצילה את העבודה אליו, ללא כל שינוי בקוד הקורא שלך
המעבר הוא קריאת רישום בודדת. ללא backend רשום, המקודד חוזר למסלול המובנה שלו. רשום אחד וכל קידוד שלאחר מכן יפעל דרכו
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;
The same hook exists for decoding through RegisterJBIG2DecoderBackend, with IsJBIG2DecoderBackendAvailable to probe it. This is why a library ships a small built-in path plus a backend seam rather than one monolithic encoder. The built-in path keeps the binary lean and free of license entanglements, while the seam lets a team that has licensed a full encoder plug it in without touching the PDF-writing layer at all
על מה אפשרויות הקידוד באמת מתפשרות
קידוד מוגדר באמצעות TJBIG2EncodeOptions, רשומה עם השדות Lossless, UseGlobalSegments, UseSymbolDictionary ו-LossyLevel. העטיפה הידידותית לרכיבים THPDFJBIG2Options חושפת את Lossless, UseSymbolDictionary ו-LossyLevel כך שניתן יהיה להגדיר אותם מתוך ה-Object Inspector, והיא ממירה לרשומה באופן פנימי. שלוש כוונות מניעות את ההגדרות
שחזור ללא אובדן נתונים (Lossless) שומר כל פיקסל. קבע את Lossless כ-True והשאר את LossyLevel על אפס, ומפת הסיביות המפוענחת תהיה זהה לחלוטין לקלט ביט-לביט. זוהי הבחירה הבטוחה היחידה עבור איור קווי, שרטוטים טכניים וכל דף שבו פיקסל חסר עלול לשנות את המשמעות, כגון חתימה או חותמת. קידוד מילון סמלים מפעיל מניעת כפילויות מודעת לטקסט וזו האפשרות שמבדילה את JBIG2 ממסנני הפקס. רמת אובדן הנתונים (lossy level), מספר שלם מ-0 עד 9, מאפשרת ל-backend מסוגל להמיר נאמנות למקור בגודל על ידי התייחסות לסימנים כמעט זהים כאל אותו סמל. אפס משמעותו ללא אובדן נתונים. המקודד המובנה מכבד רק את המסלול ללא אובדן נתונים ומתעלם מכל רמת אובדן נתונים שאינה אפס, כך שהרמות הגבוהות יותר נכנסות לתוקף רק ברגע שנרשם backend שמיישם אותן
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
אזור גנרי ו-MMR לכל השאר
לא כל תמונה בשני צבעים היא טקסט. למפות, סכמות, שרטוטי הנדסה ודפים מעורבים יש איור קווי ששום מילון לא יכול לתמצת. עבור אלה, JBIG2 מקודד אזור גנרי, מלבן של פיקסלים הדחוס ישירות ללא כל אימון סמלים. התקן מאפשר לאזור גנרי להשתמש ב-MMR, קידוד ה-modified modified READ שבו הפקס Group 4 כבר משתמש, אשר ממדל כל שורת פיקסלים כנגד השורה שמעליה
זהו המסלול ש-HotPDF מספק במקודד המובנה שלו. כאשר אף backend לא רשום והבקשה היא ללא אובדן נתונים, הספרייה דוחסת את מפת הסיביות כאזור גנרי 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 כאפשרות דחיסת תמונה במסמך. ה-enumeration (מנייה) THPDFImageCompressionType כולל את icJBIG2 לצד אפשרויות Flate, JPEG ו-CCITT, והמסמך נושא מאפיין JBIG2Options מסוג THPDFJBIG2Options שמחזיק את ההגדרות בשימוש כאשר דחיסה זו נבחרת. הגדר את שניהם לפני שתוסיף את תמונות ה-bilevel שברצונך לדחוס בצורה זו
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 לטעינה, עריכה והצפנה המכוסים במקומות אחרים כאן