تضغط losLab PDF Library لـ Delphi الصفحة الممسوحة ضوئياً بتقنية Mixed Raster Content عبر TPDFlib.CompressPageAsMRC. تعرض الطريقة الصفحة، وتقتطع قناعاً أمامياً 1-bit بدقة كاملة عند عتبة معينة، وتخفض دقة الباقي إلى خلفية JPEG، ثم تعيد بناء الصفحة كصورتين: خلفية /DCTDecode وقناع stencil من نوع /CCITTFaxDecode يُرسم بلون التعبئة الجاري
الصفحة التي وُجدت من أجلها هي نفسها التي تزخر بها كل أرشيفات المستندات: مسح ضوئي ملون بـ 300 DPI لفاتورة أو عقد، مخزن كملف JPEG واحد كبير. رمّزها بجودة تكفي لإبقاء النص مقروءاً فيصبح الملف ضخماً؛ واخفض الجودة فيتآكل الحرف وتظهر حوله هالات وتنسحب أطرافه، لأن أسوأ ما في JPEG على الإطلاق هو الحافة السوداء الحادة على ورق أبيض. تقنية Mixed Raster Content تكفّ عن مطالبة مرمّز واحد بفعل عملين
لماذا يجعل JPEG وحده النص الممسوح ضبابياً وكبيراً معاً؟
يتعامل JPEG وحده مع النص الممسوح بفلادة، لأن مرمّز DCT ينفق بتاته على التدرجات الناعمة ويقرّب الحواف الحادة بمعاملات عالية التردد تتخلص منها عملية التكميم (quantization) أولاً. النص في المسح الضوئي ليس سوى حواف حادة تقريباً. والإبقاء على تلك الحواف نظيفة يفرض جودة عالية على الصفحة بأكملها، بما في ذلك نسيج الورق والشعار ولطخة القهوة، لا شيء منها يحتاج ذلك أصلاً
فكرة MRC تقسم الصفحة حسب ماهية كل منطقة فعلاً. يصبح الحبر صورة ثنائية المستوى (bilevel) يسجل فيها مرمّز فاكس مثل CCITT سلاسل البكسلات السوداء والبيضاء دون فقد وبكثافة عالية جداً. وكل ما عدا ذلك، درجة الورق والصور والخلفيات الملونة، يصبح صورة متصلة التدرج (continuous-tone) يمكن تخزينها بجزء من الدقة، لأن أحداً لا يقرأ خلفية بدقة 300 DPI. تحافظ الطبقة الثنائية على حدّة النص بالدقة الكاملة؛ وتحفظ طبقة الخلفية اللون بتكلفة زهيدة
كيف تقسم CompressPageAsMRC الصفحة إلى طبقتين؟
تشغّل TPDFlib.CompressPageAsMRC مساراً (pipeline) ثابتاً واحداً لكل صفحة. تعرض الصفحة عند الـ DPI المطلوب في صورة نقطية 24-bit، وتصنف كل بكسل بإضاءة موزونة وفق Rec. 601 (بألف الواحدة 299/587/114) مقابل Threshold، وتكتب النتيجة في قناع 1-bit بالدقة نفسها. وبالتوازي تقلص العرض نفسه بمعامل BGScale بمتوسط صندوقي، فقيمة BGScale تساوي 4 تحول كل كتلة بكسلات 4×4 إلى بكسل خلفية واحد
ثم يستبدل محتوى الصفحة. تزال المعاملات الأصلية، وتغرز الخلفية المخفضة الدقة كصورة /DCTDecode في /DeviceRGB (ISO 32000-1 §7.4.8)، ويترمز القناع بمرمّز CCITT الخاص بالمكتبة كصورة /CCITTFaxDecode (§7.4.6) مع وسمه /ImageMask true، وترسم الصورتان على صندوق الصفحة الكامل والقناع في الأعلى. التوقيع مختصر:
function CompressPageAsMRC(Page: Integer; DPI: Double;
BGScale, JPEGQuality, Threshold, Options: Integer): Integer;
Pageيبدأ من 1 ويجب أن يكون داخلPageCountDPIهو دقة عرض الصورة النقطية المصدر، من 50 إلى 1200BGScaleهو معامل تخفيض دقة الخلفية، من 1 إلى 8JPEGQualityهو جودة JPEG للخلفية، من 1 إلى 100Thresholdهو عتبة الإضاءة، من 1 إلى 255؛ البكسلات عند العتبة أو فوقها ورق، والأغمق منها حبرOptionsمحجوز ويجب أن يكون 0
هكذا تبدو دفعة نموذجية على ملف ممسوح ضوئياً. استدعِ SetFillColor قبل كل صفحة مباشرة، لسبب يشرحه القسم التالي
uses
SysUtils, PDFlibrary;
procedure CompressScannedFile(const SrcFile, DstFile: string);
var
Lib: TPDFlib;
P, Replaced: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile(SrcFile, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s', [SrcFile]);
for P := 1 to Lib.PageCount do
begin
Lib.SetFillColor(0, 0, 0); // لون حبر طبقة الـ stencil
// قناع 300 DPI وخلفية 75 DPI وجودة JPEG تساوي 60 واقتطاع عند إضاءة 160
Replaced := Lib.CompressPageAsMRC(P, 300, 4, 60, 160, 0);
if Replaced = 0 then
raise Exception.CreateFmt('MRC failed on page %d', [P]);
end;
if Lib.SaveToFile(DstFile) <> 1 then
raise Exception.CreateFmt('Cannot save %s', [DstFile]);
finally
Lib.Free;
end;
end;
لماذا تُرسم المقدمة بلون التعبئة الجاري؟
طبقة المقدمة قناع stencil، والقناع من هذا النوع لا يملك لوناً خاصاً به. يعرّف ISO 32000-1 §8.9.6.2 الصورة ذات /ImageMask true بأنها شكل 1-bit: قيم العينات تحدد أين يذهب الطلاء، والطلاء هو أي لون غير خطي (nonstroking color) جارٍ في حالة الرسوميات. هذا بالضبط ما يجعل الطبقة رخيصة، وهو أيضاً سبب عدم أخذ CompressPageAsMRC أي معامل لون
يرسم TPDFlib.CompressPageAsMRC القناع باللون الذي ضبطه SetFillColor، لذا فترتيب الاستدعاء مهم. المرسام يبث لون التعبئة بكسل: يسجل SetFillColor اللون ويوسمه بوصفه متغيراً، ولا يكتب رسم الـ stencil المعامل rg داخل كتلة q/Q الخاصة به إلا ما دام ذلك التغيير معلقاً. ارسم شكلاً معبأً في الأثناء فيستهلك ذلك الشكل التغيير المعلق بدلاً منه. وضبط اللون مباشرة قبل كل صفحة، كما في الدفعة أعلاه، يزيل أي تخمين. الأسود هو الاختيار المعتاد؛ والأزرق الكحلي الداكن يناسب النماذج ذات الحبر الأزرق التي تريد مخرجاتها أن تبدو أقل شبهاً بنسخة ضوئية
// نموذج بحبر جاف أزرق: اعرض طبقة الحبر بالكحلي الداكن
Lib.SetFillColor(0.08, 0.12, 0.35);
Replaced := Lib.CompressPageAsMRC(PageNo, 300, 4, 55, 170, 0);
يتبع الموضع أصل إحداثيات المكتبة. تقرأ CompressPageAsMRC قيمة GetOrigin وترسم الصورتين من الحافة العليا لصندوق الصفحة: إحداثي علوي 0 عندما يكون الأصل أعلى اليسار (SetOrigin(1))، وإحداثي علوي PageHeight مع الأصل الافتراضي أسفل اليسار. لا تحتاج لتعديل شيء، لكن هذا يفسر سبب هبوط الطبقتين في مكانهما الصحيح أيّاً كان الأصل الذي يستخدمه مسارك أصلاً
من نتيجات دلالات الـ stencil ما يستحق القول بوضوح: كل بكسل مصنف كحبر يأخذ اللون نفسه. الختم الأحمر والتوقيع الأزرق والنص الأساسي الأسود ينتهي جميعها إلى لون تعبئة واحد على طبقة المقدمة. لا يحتفظ سوى خلفية JPEG بأثر باهت منخفض الدقة للألوان الأصلية تحتها
اختيار DPI و BGScale و Threshold وجودة JPEG
قيمة DPI تحدد حدّة النص، و300 هي القيمة الافتراضية المعقولة للمسوح المكتبية. يبقى القناع بالدقة الكاملة للعرض، لذا يحدد DPI مدى رفاعة الخط الذي ينجو من الاقتطاع. ولا تكسب شيئاً بالتجاوز إلى أعلى من دقة المسح الأصلية، لأن العارض سيكتفي بتكبير صورة المسح المغروسة. الذاكرة هي الحد الآخر: صفحة US Letter بدقة 300 DPI تُعرض في 2550×3300 بكسل، أي 25,245,000 بايت من بيانات البكسل 24-bit قبل اشتقاق القناع والخلفية، والصفحة نفسها عند سقف 1200 DPI تصبح 10200×13200 بكسل، نحو 404 MB
BGScale يبادل تفاصيل الخلفية بالحجم. عند 300 DPI، يعطي المعامل 4 خلفية بدقة 75 DPI، أي 638×825 بكسل لصفحة Letter، وهذا وفير لدرجة الورق ومناطق اللون المسطح. أما الصفحات ذات الصور الفوتوغرافية أو الرسائل نصف المنتنة (halftone) التي سينظر إليها القارئ فعلاً فتستحق 2؛ والنص العادي على ورق أبيض يحتمل 6 أو 8، لأن الخلفية لا تتجاوز درجة لون تقريباً
Threshold هو المعامل الذي يغير مظهر الصفحة. رفعه يصنف مزيداً من بكسلات الرمادي المتوسط كحبر، فيغلّظ الخطوط الرفعة وعلامات القلم الرصاص لكنه يجلب أيضاً ضجيج الماسح وظل الورق بوصفها بقعاً. وخفضه يعطي ورقاً أنظف وحروفاً أرفع أحياناً مكسورة. القيم حول 128 إلى 170 مدى بداية معقول للنص الأسود على أبيض؛ والمفاضلة نفسها هنا هي التي تناقشها مقالة عرض صفحات PDF أحادية اللون 1-bit داخل Delphi، حيث تقود عتبة الإضاءة نفسها RenderPageToMonochromeFileEx. بخلاف تلك الطريقة، لا يملك CompressPageAsMRC مرحلة إزالة بقع، فالمسوح المضوضاة تنفع معها عتبة أدنى قليلاً
JPEGQuality أهميته أقل مما في مسح أحادي الصورة، لأن الخلفية لم تعد تحمل أي حواف نص. الجودة من 50 إلى 70 غير ملحوظة عادة على خلفية 75 DPI خلف قناع حاد
ماذا يخبرك القيمة المعادة وماذا لا يخبرك؟
تعيد TPDFlib.CompressPageAsMRC حجم محتوى الصفحة الذي استبدله بالبايت، وتعيد 0 عند فشل شيء ما. إنها مؤشر نجاح لا مقياس توفير. في الصفحة الممسوحة النموذجية يكون تدفق المحتوى الأصلي بضع عشرات من البايتات، معاملات تكفي فقط لوضع صورة واحدة، فالرقم الموجب الصغير هو النتيجة المعتادة حتى حين يتقلص حجم الصفحة بالميجابايتات. أما الصفحة التي كان تدفق محتواها الأصلي فارغاً فتُبلّغ 0 رغم أنها أُعيد بناؤها
سلوك الفشل ليس ذرياً، وذلك هو الجزء الذي تصمم حوله. فحوصات الوسائط والعرض وترميز JPEG تجري قبل المساس بالصفحة، فقيمة BGScale خارج المدى أو JPEGQuality تساوي 101 أو فشل عرض تترك الصفحة كما هي تماماً. لكن بعد مسح المحتوى الأصلي، فإن فشل غرز إحدى الطبقتين أو رسمه يعيد 0 أيضاً، وعندها قد تكون الصفحة قد أعيد بناؤها جزئياً بالفعل. عامل أي 0 بوصفه سبباً لإتلاف المستند في الذاكرة بدل حفظه، ولا تكتب النتيجة أبداً فوق الملف المصدر
ولمعرفة ما وفّرته العملية فعلاً، قِس المستند. مقارنة طول SaveToString قبل وبعد أسرع فحص؛ ولجواب مفصل بحسب الفئات، يبيّن تدقيق حجم ملف PDF بواسطة AuditDocumentSpace إن كان الوزن انتقل من الصور كما هو متوقع
function MRCSavings(Lib: TPDFlib): Int64;
var
Before: Int64;
P: Integer;
begin
Before := Length(Lib.SaveToString);
for P := 1 to Lib.PageCount do
begin
Lib.SetFillColor(0, 0, 0);
if Lib.CompressPageAsMRC(P, 300, 4, 60, 160, 0) = 0 then
raise Exception.CreateFmt('Page %d failed; discard this document', [P]);
end;
Result := Before - Length(Lib.SaveToString);
end;
أين تتوقف طبقتا MRC هاتين دون ITU-T T.44
تنفذ CompressPageAsMRC نسخة MRC مبسطة عن قصد. النموذج الكامل في ITU-T T.44 يستخدم ثلاث طبقات لكل شريط: صورة خلفية، وصورة مقدمة تحمل ألوان الحبر، وقناع ثنائي المستوى ينتقي بينهما بكسلاً ببكسل، مع متسع لعدة أزواج من الأقنعة والمقدمات في صفحة واحدة. وبلغغة PDF ستكون الطبقة الثالثة صورة مقدمة ملونة منخفضة الدقة بقناع صريح (§8.9.6.3) بدل stencil يرسم بلون واحد. تتخطى PDF Library لـ Delphi طبقة لون المقدمة، وهذا ما يجعل النتيجة صغيرة إلى هذا الحد وهو أيضاً سبب انهيار الحبر الملون في لون التعبئة
- تناسب الطريقة النص الأسود أو أحادي اللون على ورق ممسوح؛ أما المستندات المرمزة بالألوان والنص المظلل والأختام الملونة فتفقد لون حبرها
- الصفحة المعاد بناؤها نقطية خالصة: أي نص حقيقي أو رسم متجهي أو محارف قابلة للتحديد في الصفحة الأصلية تتحول بكسلات، فاستخدمها على المسوح لا على الصفحات الرقمية الأصل، حيث تكون تجزئة الخطوط وتخفيض دقة الصور هي الأدوات الصحيحة
- يستخدم القناع مرمّز CCITT الخاص بالمكتبة بوضع T.4 ثنائي الأبعاد، مكتوباً
/CCITTFaxDecodeمع/K 1؛ ويمتلك المرمّز أيضاً وضع Group 4 عبرAddImageCCITTFromBitmapStream، لكنCompressPageAsMRCلا يكشفه، ولا يستخدم JBIG2 - التعليقات وحقول النماذج لا تُسطّح؛ تبقى كائنات فوق المحتوى المعاد بناؤه
- الخلفية
/DeviceRGB، فالمخرجات لا تناسب مسارات PDF/X-1a، ويرفضSetFillColorنفسه في وضع PDF/X-1a - المسح الذي يحتاج OCR أصلاً يجب التعرف عليه قبل MRC، ما دامت البكسلات الأصلية ما تزال موجودة بكامل عمق الألوان
FAQ
هل تجعل CompressPageAsMRC النص قابلاً للبحث؟
لا. يستبدل CompressPageAsMRC محتوى الصفحة بصورتين، فتزال معه كل معاملات النص في الصفحة الأصلية، بما فيها طبقة نص OCR الخفية. شغّل OCR على الصفحة المضغوطة لاحقاً، أو أعِد طبقة النص بعد الضغط
هل يمكنني ضغط بعض صفحات المستند فقط؟
نعم. يعمل CompressPageAsMRC على صفحة واحدة تبدأ من 1 في كل استدعاء، فتختار أنت الصفحات ويمكنك حتى تنويع BGScale و Threshold بحسب الصفحة، مثل معامل 2 لطبقة صور فوتوغرافية و6 لصفحات النص العادي
ماذا يجب أن تكون قيمة المعامل Options؟
مرر 0. المعامل Options محجوز للاستخدام المستقبلي في الإصدار الحالي ولا بتات معرفة له، فالقيمة غير الصفرية لا تحمل أي معنى اليوم
تتوفر CompressPageAsMRC منذ losLab PDF Library لـ Delphi إصدار v3.561.0 مع قطع البناء التي جُمعت منها، العرض و JPEG و CCITT وأقنعة الـ stencil، وكلها متاحة طرقاً عامة إن احتجت نسخة مختلفة من المسار. المكتبة ودعمها لـ C++Builder والمرجع الكامل للـ API على صفحة منتج PDF Library لـ Delphi