تقبل PDF Library for Delphi صور AVIF وHEIF وJPEG XL كمُدخَل عبر AddModernImageFromFile ونسخها من التدفق والسلسلة النصية، مع الحفاظ على قناة ألفا، وملف ICC المضمَّن، والقنوات بعمق 16 بت في طريقها إلى كائن صورة PDF. ويحدث اكتشاف الصيغة بقراءة محدودة لرقم سحري، ويعمل فك الترميز عبر خلفية قابلة للاستبدال، بحيث لا يُستدعى أي شيء خارجي لملف ليس فعليًا واحدة من تلك الصيغ
ووصلت هذه الصيغ إلى سير عمل المستندات عبر الهواتف. فقد أنتج iOS صيغة HEIC افتراضيًا لسنوات، وتُنتج أجهزة Android صيغة 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 سيكون خيار تصميم غريبًا. تُحمِّل الخلفية الافتراضية ديناميكيًا وحدة MagickWand قابلة للنشر داخل العملية نفسها، وتبحث عنها بترتيب موثَّق: ملف أو دليل صريح تضبطه أنت، ثم متغيرات البيئة، ثم دليل الملف التنفيذي، ثم مسار بحث النظام
أما التطبيقات التي تشحن بالفعل فاكّ ترميز، أو التي يجب ألا تحمّل وحدة خارجية إطلاقًا، فتُسجِّل استدعاء رد نداء خاصًا بها بدلًا من ذلك. والعقد صغير: اقرأ تدفق الإدخال، واكتب 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);
// ... add images ...
ClearModernImageDecoderBackend; // عودة إلى الخلفية الافتراضية
end;
ويحصل النشر على تسهيل واحد وقيد متعمَّد واحد. فإذا احتوى دليل الترميز على دليل فرعي modules\coders، تملأ المكتبة متغيرات بيئة الترميز التي يحتاجها هذا التخطيط، لكن فقط حين لم يكن التطبيق المضيف قد ضبطها بالفعل. ويحتفظ تطبيق له استراتيجية نشر وقت تشغيل خاصة به بها
لماذا PNG في المنتصف؟
يبدو الجسر عبر PNG في الذاكرة بدلًا من مخزن بكسل خام خطوة إضافية، وهو في الواقع أرخص خطوة صحيحة. فـ PNG يُعبِّر عن كل ما يجب أن ينجو: ألفا، ونوع اللون، وعمق البت، وملف ICC مضمَّن، وتملك المكتبة بالفعل مسارًا ناضجًا ومُختبَرًا جيدًا من PNG إلى كائن صورة PDF بالمرشحات ومساحة الألوان الصحيحة. وإعادة استخدامه تعني أن الصيغ الحديثة ترث سنوات من عمل الصحة بدلًا من الحصول على تنفيذ مواز
والجسر بأكمله في الذاكرة، لذا لا تُنشأ ملفات مؤقتة ولا حاجة إلى تنظيف عند الانهيار. وثمة عقبة احتاجت إلى معالجة صريحة: بعض التحويلات تُسقِط ملف ICC عند تغيير الصيغة. لذا تلتقط الخلفية ملف المصدر قبل تبديل الصيغة، وتضغطه بـ Flate، وتبني كتلة iCCP صالحة بمجموع تحقق CRC مُعاد حسابه، وتزيل أي كتلة sRGB قد تتعارض معها. وفي الاختبار، احتفظ ملف AVIF مفكوك الترميز بـ RGBA بعمق 16 بت مع قناة ألفا بعمق 16 بت، وطابق الملف المُستخرَج من ملف PDF الناتج ملف المصدر بايتًا ببايت عند 60,960 بايت
ملاحظات عملية قبل تفعيلها في بيئة الإنتاج
تحقق من التوفر عند بدء التشغيل لا عند أول صورة. تُبلغ ModernImageCodecAvailable عمّا إذا كان بالإمكان استخدام خلفية، وتُشير 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
وأخيرًا، قرر سياسة الألوان عمدًا. فالحفاظ على ملف المصدر صحيح لعمل الأرشفة والطباعة؛ والتحويل إلى مساحة على مستوى المستند بأكمله صحيح حين يجب أن تبدو مجموعة مختلطة من الصور متسقة، ومسار التحويل موصوف في إعادة تلوين مستند إلى مساحة ألوان أخرى. وإذا احتجت إلى تأكيد ما وصل فعليًا إلى الملف، يُبلغ مسار الفحص في استخراج النص والصور والخطوط عن كائنات الصور التي يحملها مستند
وإدخال الصور الحديثة، وإدارة الألوان، وتحسين الصور جزء من المكتبة نفسها لـ Delphi وC++Builder وFree Pascal؛ وتوجد قائمة الميزات الكاملة على صفحة PDF Library for Delphi