PDF Library for Delphi מקבלת תמונות AVIF, HEIF ו-JPEG XL כקלט דרך AddModernImageFromFile והוריאנטים שלה לזרם ולמחרוזת, ומשמרת אלפא, פרופיל ה-ICC המוטמע וערוצי 16 סיביות בדרך אל תוך אובייקט התמונה של ה-PDF. זיהוי פורמט מתבצע בקריאת מספר-קסם חסומה, ופענוח רץ דרך backend הניתן להחלפה, כך שום דבר חיצוני לא נקרא עבור קובץ שהוא בעצם לא אחד מהפורמטים האלה
פורמטים אלה הגיעו לזרימות עבודה של מסמכים דרך טלפונים. iOS מפיק HEIC כברירת מחדל כבר שנים, מכשירי אנדרואיד מפיקים AVIF, וטכנאי שדה שמצלם חלק פגום שולח תמונה שמחולל דוח PDF שנבנה ב-2015 לא יכול לפתוח בכלל. נתיב הנפילה הגנרי, פענוח דרך מפת סיביות פלטפורמה, מפיק באופן אמין צבע 8 סיביות ומאבד אלפא ופרופיל צבע בדרך
מה נתיב התמונה המודרנית משמר שהמרת מפת סיביות מאבדת?
שלושה דברים, ולכל אחד יש זרימת עבודה שתלויה בו. אלפא שורדת, מה שחשוב עבור לוגואים וגזירי מוצר המורכבים על תוכן עמוד. פרופיל ה-ICC שורד, מה שחשוב לכל דבר שיודפס או יותאם צבע. וערוצי 16 סיביות שורדים, מה שחשוב לתמונות רפואיות ומדעיות שבהן קוונטיזציה ל-8 סיביות הורסת בדיוק את הגוונים שהתמונה נלכדה למענם
העברת תמונה דרך מפת סיביות פלטפורמה מאבדת את שלושתם בצעד אחד, והיא עושה זאת בשקט: קובץ ה-PDF שנוצר נראה בערך נכון, ואף אחד לא שם לב עד שמדפסת שואלת למה האדום התאגידי שגוי. ערך האפשרות 8 בקריאות התמונה המודרנית הוא הדגל ששומר יחד על אלפא, ICC וערוצי 16 סיביות, וזו ברירת המחדל לקריאות הללו
הוספת אחת לעמוד
הקריאה מחזירה מזהה תמונה, שנבחר ומצויר, או מצויר ומשוחרר בצעד אחד:
uses
PDFlibrary, PDFlibModernImage;
var
Lib: TPDFlib;
ImageID: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.SetPageSize('A4');
Lib.NewPage;
// Options = 8 שומר אלפא, ICC וערוצי 16 סיביות
ImageID := Lib.AddModernImageFromFile('site-photo.heic', 8);
if ImageID > 0 then
Lib.DrawImageAndRelease(ImageID, 40, 40, 515, 340)
else
Lib.DrawText(40, 40, 'image could not be decoded');
Lib.SaveToFile('inspection-report.pdf');
finally
Lib.Free;
end;
end;
הזיהוי קודם לפענוח ומכוון להיות צר. הספרייה קוראת כותרת חסומה, מזהה את סימני מותג פורמט קובץ המדיה הבסיסי בתקן ISO שמזהים AVIF ו-HEIF, ומזהה גם את החתימות הגולמיות וגם את חתימות המכל של JPEG XL, ואז משחזרת את מיקום הזרם של הקורא. קלט לא מוכר או מוסווה לעולם לא מגיע למקודד החיצוני, מה ששומר על קובץ הפעלה ששמו שונה מלהימסר למפענח כאילו היה תמונה
איפה בעצם מתרחש הפענוח?
פורמטי תמונה מודרניים הם קודקים גדולים ומורכבים, והכנסת אחד לתוך ספריית PDF הייתה בחירת עיצוב מוזרה. ה-backend המוגדר כברירת מחדל טוען באופן דינמי מודול MagickWand הניתן לפריסה בתהליך ומחפש אותו בסדר מתועד: קובץ או ספרייה מפורשים שהגדרתם, משתני סביבה, ספריית קובץ ההפעלה, ונתיב החיפוש של המערכת
אפליקציות שכבר משגרות מפענח, או שאסור להן לטעון מודול חיצוני כלל, רושמות callback משלהן במקום זאת. החוזה קטן: קראו את זרם הקלט, כתבו PNG לזרם הפלט, כבדו את הכיוון המבוקש:
function MyDecoder(InStream, OutPNG: TStream;
ImageFormat: TPDFlibModernImageFormat;
ApplyOrientation: Boolean): Boolean;
begin
// פענחו את InStream עם הקודק שלכם וכתבו בייטי PNG ל-OutPNG
Result := DecodeWithBundledCodec(InStream, OutPNG,
ImageFormat, ApplyOrientation);
end;
begin
RegisterModernImageDecoderBackend(MyDecoder);
// ... הוספת תמונות ...
ClearModernImageDecoderBackend; // חזרה ל-backend ברירת המחדל
end;
הפריסה מקבלת נוחות אחת וריסון מכוון אחד. אם ספריית הקודק מכילה תת-ספרייה modules\coders, הספרייה ממלאת את משתני סביבת הקודק שפריסה כזו זקוקה להם, אבל רק כשהאפליקציה המארחת עוד לא הגדירה אותם. אפליקציה עם אסטרטגיית פריסת runtime משלה שומרת עליה
למה PNG באמצע?
גישור דרך PNG בזיכרון במקום מאגר פיקסלים גולמי נראה כמו צעד נוסף ולמעשה הוא הזול ביותר שנכון. PNG מבטא כל מה שחייב לשרוד, אלפא, סוג צבע, עומק סיביות ופרופיל ICC מוטבע, ולספרייה כבר יש נתיב בוגר ומבחן-היטב מ-PNG לתוך אובייקט תמונה של PDF עם המסננים ומרחב הצבע הנכונים. שימוש חוזר בו אומר שפורמטים מודרניים יורשים שנים של עבודת נכונות במקום לקבל מימוש מקביל
הגשר כולו בזיכרון, כך שאין קבצים זמניים נוצרים ואין צורך בניקוי אחרי קריסה. פרט אחד דרש טיפול מפורש: כמה המרות משמיטות את פרופיל ה-ICC בעת החלפת פורמט. ה-backend לכן לוכד את פרופיל המקור לפני החלפת הפורמט, דוחס אותו עם Flate, בונה קטע iCCP תקין עם CRC מחושב מחדש, ומסיר כל קטע sRGB שיתנגש איתו. בבדיקה, AVIF מפוענח שמר RGBA 16 סיביות עם אלפא 16 סיביות, והפרופיל שחולץ מה-PDF שנוצר תאם את פרופיל המקור בייט לבייט ב-60,960 בייטים
הערות מעשיות לפני הפעלה בייצור
בדקו זמינות בהפעלה ולא בתצלום הראשון. ModernImageCodecAvailable מדווחת אם ניתן להשתמש ב-backend, ו-SetModernImageCodecLibrary מצביעה על קובץ או ספרייה מפורשים כשהפריסה שלכם ממקמת את הקודק במקום לא סטנדרטי:
Lib.SetModernImageCodecLibrary('C:\MyApp\codecs');
if Lib.ModernImageCodecAvailable = 0 then
Log('modern image input unavailable - HEIC and AVIF will be refused');
עקבו אחרי גודל הקובץ של התוצאה. תמונת RGBA 16 סיביות עם פרופיל מוטבע היא אובייקט תמונה גדול ב-PDF, ודוח עם ארבעים כאלה יהיה גדול. כשהמסמך מיועד לצפייה במסך ולא לדפוס, דגימה-למטה לפני ההטמעה היא הפשרה הנכונה, ומנופי הגודל הכלליים מכוסים באופטימיזציית גודל קובץ PDF
לבסוף, החליטו על מדיניות צבע במכוון. שמירת פרופיל המקור נכונה לעבודת ארכיון ודפוס; המרה למרחב רחב-מסמך נכונה כשקבוצה מעורבת של תצלומים חייבת להיראות עקבית, ומסלול ההמרה מתואר בצביעה מחדש של מסמך למרחב צבע אחר. אם צריך לאשר מה בדיוק נחת בקובץ, נתיב הבדיקה בחילוץ טקסט, תמונה וגופן מדווח על אובייקטי התמונה שמסמך נושא
קלט תמונה מודרנית, ניהול צבע ואופטימיזציית תמונה הם חלק מאותה ספרייה לדלפי, C++Builder ו-Free Pascal; רשימת התכונות המלאה נמצאת בעמוד PDF Library for Delphi