يفك مكون PDF الأصلي لـ Delphi، HotPDF، صور CCITTFaxDecode بدعم كامل لامتداد وضع uncompressed في T.4/T.6 ولاسترداد الصفوف المتضررة الموجَّه بـ DamagedRowsBeforeError. وضع uncompressed لا يُدخل إليه إلا عبر كود الامتداد المنتهي بـ 111، والصفوف المتضررة لا تُصلَح إلا لبيانات Group 3 مع ضبط EndOfLine. كل ما عدا ذلك يفشل بنظافة بدلاً من أن ينتج صفحة مائلة
الصور المشفرة بأسلوب الفاكس هي الذيل الطويل في فك صور PDF. معظم تدفقات CCITT التي تصادفها خرجت من مشغل ماسح ضوئي أو خط التقاط مستندات لا يصدر أبداً سوى Group 4 الساذج، ومفكك يعالج الحالة الشائعة ينجح في كل اختبار ترميه عليه. ثم يصل أرشيف من خادم فاكس عمره خمسة عشر عاماً، وتُعرض صفحة بضجيج قطري من الصف 400 نزولاً، فتكتشف أن الجزأين من توصيات ITU-T اللذين لا ينفذهما أحد هما بالضبط الجزآن اللذان يستخدمهما ذلك الملف. تستعرض هذه المقالة هذين الجزئين كما يعالجهما HotPDF، والأماكن التي يمكن أن يخطئ فيها المفكك مع بقي يبدو صائباً
أي معاملات CCITTFaxDecode تغيّر تدفق البتات فعلاً؟
أربعة مداخل في قاموس DecodeParms تغيّر كيف يؤطّر المفكك تدفق البتات: K و EndOfLine و EncodedByteAlign و DamagedRowsBeforeError. تعرّفها ISO 32000-1 §7.4.6 (الجدول 11)، والخطأ في أي منها يزيح كل صف بعد أول عدم تطابق. أما BlackIs1 و Columns و Rows و EndOfBlock فمهمة أيضاً، لكنها تؤثر في القطبية والهندسة وموضع توقف الفك، لا في كيفية إيجاد حد الصف
Kيختار مخطط الترميز: سالب يعني Group 4 ثنائي الأبعاد خالص (T.6)، وصفر يعني Modified Huffman أحادي البعد (Group 3 1D)، وموجب يعني Group 3 2D مختلط، حيث تقول بتة وسم بعد كل EOL هل الصف التالي 1D أم 2DEndOfLineيقول إن كل صف Group 3 يسبقه كود EOL، إحدى عشرة بتة صفر يتبعها واحدEncodedByteAlignيقول إن كل صف مشفَّر يبدأ عند حد بايت، فيجب على المفكك أن يتجاهل بتات الحشو قبل قراءتهDamagedRowsBeforeErrorهو عدد الصفوف التالفة التي يمكن للمفكك أن يتسامح معها قبل الاستسلام، بقيمة افتراضية 0
يقرأ HotPDF كل هذه من قاموس الصورة عندما تستدعي THotPDF.ExtractLoadedImage أو THotPDF.DecodeLoadedImageStream. وانحراف مقصود واحد يستحق المعرفة: عندما تكون Columns مفقودة، يرجع HotPDF إلى Width الخاصة بالصورة بدلاً من الافتراضي في المواصفة وهو 1728، وعندما تكون Rows مفقودة يستخدم Height. المولّد الذي يحذف Columns في مسح A4 بعرض 2480 بكسل مخطئ، لكنه شائع بما يكفي لأن الاعتماد على هندسة الصورة يعطي صفحة صالحة للإستعمال حيث كان الافتراضي الحرفي سيعطي خراباً
لماذا لا يمكن معاملة 000000 بوصفها نهاية الكتلة؟
في الصفوف ثنائية الأبعاد، ست بتات صفر مسبقة غامضة، والمفكك الذي يقرأها بوصفها بداية EOFB سيقرأ مدخلاً صالحاً بوضع uncompressed بوصفه نهاية الصورة. بيانات Group 4 لا تحوي EOL بين الصفوف؛ والأكواد الغنية بالأصفار الوحيدة هي EOFB، وهي EOL متتاليَين متلاصقان، وأكواد الامتداد من ITU-T T.4 و T.6. كلاهما يبدأ بـ 000000، والبتة السابعة هي موضع الافتراق
يحل مفكك CCITT في HotPDF البادئة بتةً بتة. البتة السابعة 1 تعني أن البتات الثلاث التالية كود امتداد، والنمط الكامل هو 0000001xxx. فقط xxx = 111 يدخل وضع uncompressed؛ وأي قيمة امتداد أخرى تعالَج بوصفها تالفة، إذ لا فائدة لمرشح فاكس PDF في غيرها. أما البتة السابعة 0 فلا يمكن إلا أن تُكمل EOL، فيطلب المفكك بعدها أربع بتات صفر إضافية وواحدة ختامية، إحدى عشرة صفراً وواحداً في المجموع، قبل أن يبلّغ عن EOFB. وأي شيء أقصر أو أطول خطأ، لا نهاية صورة متساهلة
النسخة المتساهلة مغرية لأنها تجعل فئة كاملة من الملفات المعطوبة تبدو سليمة: يتوقف الفك مبكراً فحسب وتحصل على صورة قصيرة دون استثناء. وهي بالضبط الخلل الذي يبترم الفاكسات المشروعة عند أول امتداد من البيانات غير المضغوطة، وهو في نموذج ممسوح ضوئياً عادةً توقيع كثيف أو خاتم. يختار HotPDF القراءة الصارمة، فالصورة القصيرة تعني أن المرمِّز انتهى هناك فعلاً
كيف يرمّز وضع uncompressed البكسلات؟
يستبدل وضع uncompressed ترميز الأطوال بالأنماط الحرفية للبكسلات، كل كود كلمة يحمل خمسة بكسلات على الأكثر، حتى يعيد كود خروج الأمور إلى العادي. الصفوف أحادية البعد تدخله بكود الـ 12 بت 000000001111؛ وصفوف ثنائية البعد تستخدم امتداد الـ 10 بت 0000001111 المشروح أعلاه. يتحول إليه المرمِّزون في المناطق شبه أنصاف النغمات (halftone) حيث أطوال الجري من بكسل أو بكسلَين ستكلّف أكثر من البكسلات ذاتها
داخل الوضع، تُحصى أكواد الكلمات بصفرها المسبق. صفر إلى أربعة أصفار يتبعها 1 تعني ذلك العدد من البكسلات البيضاء ثم بكسلاً أسود واحداً. خمسة أصفار يتبعها 1 تعني خمسة بكسلات بيضاء ولا شيء غير ذلك. وستة إلى عشرة أصفار يتبعها 1 هي أكواد خروج: الأصفار فوق الستة تُصدر حتى أربعة بكسلات بيضاء إضافية، والبتة الوحيدة بعد كود الخروج، بتة الوسم، تضبط لون الجري التالي عندما يُستأنف ترميز الأطوال العادي
{ كلمات كود الوضع غير المضغوط (ITU-T T.4 / T.6)
W = بكسل أبيض، B = بكسل أسود، T = بت وسم
1 B
01 W B
001 W W B
0001 W W W B
00001 W W W W B
000001 W W W W W
0000001T خروج ثم استئناف الرموز باللون T
00000001T W، خروج
000000001T W W، خروج
0000000001T W W W، خروج
00000000001T W W W W، خروج
أكثر من عشرة أصفار: تالف }
بتة الوسم هي الجزء الدقيق. ترميز الأطوال يحمل حالة: الصف 1D يتبادل جريات بيضاء وسوداء، والصف 2D يتتبع لون عنصر التغيير الحالي a0. بعد كود الخروج، لا بد أن يستأنف المفكك باللون الذي سمّاه الوسم، وقد يطابق آخر بكسل حرفي أو لا يطابقه. يسجّل مفكك HotPDF تغيّراً لونياً في الموضع الحالي عندما يخالف الوسم اللون الذي كان يتتبعه، فيبدأ الجري التالي من اللون الصحيح. تجاهل الوسم وسيخرج بقية الصف بألوان معكوسة، وفي وضع 2D ترث كل صف لاحق الضرر عبر سطره المرجعي
ماذا يسترد DamagedRowsBeforeError وماذا لا يسترد؟
يتيح DamagedRowsBeforeError لـ HotPDF استبدال صف تالف ومواصلة، لكن فقط عندما تكون K صفراً أو موجباً و EndOfLine صائباً. هذان الشرطان ليسا اعتباطيين. الاسترداد يحتاج وسيلة لإيجاد موضع بداية الصف التالي بعد أن تتوقف البتات عن منطقها، والمعلم الموثوق الوحيد في تدفق CCITT هو كود EOL. بيانات Group 4 بلا EOL، وبيانات Group 3 بلا EOL إلزامي لا تضمن وجود واحد، ففي الحالتين ينتهي فك الصف التالف بغض النظر عن قيمة المعامل
عندما يفشل صف، يمسح مفكك HotPDF إلى الأمام حتى EOL التالي، وهي جرية من إحدى عشرة بتة صفر على الأقل يتبعها واحد، ويستبدل صفحة بديلة. الصف التالف الأول بعد صف سليم يُستبدل بنسخة من الصف السابق، وهو في صفحة نص ممسوحة عادةً قريب بما يكفي ليكون غير مرئي. أما الصف التالف الذي يلي مباشرة صفاً تالفاً آخر فيُستبدل بصف أبيض بالكامل بدلاً من ذلك، لأن نسخ النسخة سيمط سطر نص واحد في شريط أسود طويل. ثم يصبح البديل هو السطر المرجعي للصف 2D التالي، وهو ما يبقي تدفقاً مختلط الوضع قابلاً للفك بعد الإصلاح
حدان سهلا الإغفال. الميزانية تراكمية عبر الصورة كلها، لا لكل جولة صفوف سيئة، فـ DamagedRowsBeforeError 3 يتسامح مع ثلاثة صفوف تالفة في المجموع والرابع يفشل بالفك. والفك الفاشل في HotPDF كله أو لا شيء: يعيد THotPDF.ExtractLoadedImage قيمة nil بدلاً من صورة نقطية بحافة سفلية ممزقة. و THPDFLoadedImageInfo.Decodable يبلّغ أن المرشح مدعوم، لا أن هذا التدفق بعينه سليم، فافحص الصورة النقطية من أجل nil دائماً
لماذا يجب أن يتوقف المسح عند بداية الـ EOL
مسح إعادة المزامنة يترك القارئ عند أول بتة صفر من الـ EOL، لا بعدها. للمفكك حلقة تأطير واحدة تستهلك الـ EOL، وتفحص الـ EOL المضاعف الذي ينهي الكتلة، وتقرأ بتة وسم الوضع المختلط. إذا بلع مسح الاسترداد الـ EOL نفسه، رأت تلك الحلقة صفاً بلا EOL، وعاملته بوصفه تالفاً أيضاً، وحرقت وحدة ثانية من الميزانية بينما أسقطت صفاً سليماً. وأسوأ من ذلك، بتة وسم مُتخطَّاة تعني أن صفاً ثنائي البعد سيُفك بوصفه أحادي البعد أو العكس. تنطبق القاعدة نفسها عندما يصطدم مفكك جرية 1D بـ EOL غير متوقع في منتصف صف: يعود إلى الوراء إلى موضع بدء كود الجري، فيبقى العلامة في مكانها لمسح الاسترداد
حصر صور CCITT في ملف PDF محمّل
قبل أن تثق بدفعة فاكسات مؤرشفة، اسرد أي الصور تستخدم CCITTFaxDecode وأيها تفشل في الفك. يمنحك THotPDF.GetLoadedImageInfo الهندسة واسم المرشح دون فك أي شيء، و THotPDF.ExtractLoadedImage يخبرك هل نجا التدفق. وللصورة الأوسع عن كيفية توجيه مُوزِّع الصور لكل مرشح، انظر دليل استخراج صور PDF المحمّلة ومرشحات فكها
var
Pdf: THotPDF;
Info: THPDFLoadedImageInfo;
Bmp: TBitmap;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('fax-archive.pdf', '') <= 0 then
Exit;
for I := 0 to Pdf.GetLoadedImageCount - 1 do
begin
if not Pdf.GetLoadedImageInfo(I, Info) then
Continue;
if Pos('CCITT', UpperCase(String(Info.Filter))) = 0 then
Continue;
Bmp := Pdf.ExtractLoadedImage(I); // nil عندما يكون التدفق متضرراً أكثر من اللازم
try
if Bmp = nil then
Writeln(Format('obj %d: %dx%d CCITT failed to decode',
[Info.ObjectNumber, Info.Width, Info.Height]))
else
Bmp.SaveToFile(Format('fax_%d.bmp', [I]));
finally
Bmp.Free; // المستدعي يملك الصورة النقطية
end;
end;
finally
Pdf.Free;
end;
end;
للإخفاقات، احتفظ بالدليل. THotPDF.ExtractLoadedImageToStream بالوضع الافتراضي lismEncoded يكتب بايتات التدفق المخزَّن كما هي دون مساس، وهذا ما تريد تسليمه إلى مفكك ثانٍ، أو تغليفه في حاوية TIFF للفحص، أو إرفاقه بتقرير خلل. إعادة ترميز فاكس متضرر لا تفعل سوى إخفاء موضع الضرر
procedure SaveRawFax(Pdf: THotPDF; ImageIndex: Integer; const FileName: string);
var
Fs: TFileStream;
begin
Fs := TFileStream.Create(FileName, fmCreate);
try
if not Pdf.ExtractLoadedImageToStream(ImageIndex, Fs, lismEncoded) then
raise Exception.CreateFmt('Image %d has no stream data', [ImageIndex]);
finally
Fs.Free;
end;
end;
وإذا سرت في تدفقات الصور بنفسك عبر THotPDF.CollectLoadedImageStreams، فمرّر كل THPDFStreamObject إلى THotPDF.DecodeLoadedImageStream وستحصل على مسار CCITT والمعاملات وقواعد الاسترداد نفسها. حرر القائمة المعادة ولكن ليس كائنات التدفق فيها، فهي تبقى مملوكة للمستند
هل ترفع DamagedRowsBeforeError في ملفات لا تتحكم فيها؟
لا، لا بتعديل الـ PDF، وعادةً لا إطلاقاً. المعامل يخص المولّد، وهو الطرف الوحيد الذي يعرف هل تُفقد قناة الإرسال بتات. ملف يعلن DamagedRowsBeforeError 0 ثم يحوي صفاً تالفاً يخبرك أن نسخة الأرشيف تالفة، والنتيجة الصادقة هي فشل فك يمكنك تسجيله. ترقيع الصفوف بصمت في عقد أو سجل طبي يحوّل عيباً قابلاً للكشف إلى صورة تبدو معقولة بسطر مفقود، وهذا أسوأ نتيجة لأي أحد سيعتمد عليها لاحقاً
النمط الأأمن للأرشيفات غير الموثوقة هو إبقاء الفك صارماً، وتسجيل أرقام الكائنات التي فشلت، وتوجيهها إلى إنسان أو إلى مفكك منفصل. وإذا كان الأرشيف كبيراً وقادماً من مصادر كثيرة، فإن تشغيل فك الصور خارج العملية يحدد ما يمكن لتدفق معادٍ فعله بالتطبيق المضيف؛ تتناول مقالة عزل مرمازات الصور عند معالجة PDF غير موثوق ذلك الإعداد، ويقارن دليل ضغط JBIG2 ثنائي المستوى المرشح ثنائي المستوى الآخر الذي ستجده في الأرشيفات الممسوحة نفسها
الأسئلة الشائعة
هل يدعم HotPDF وضع CCITT uncompressed في صور Group 4؟
نعم. يتعرف HotPDF على امتداد الـ 10 بت 0000001111 في الصفوف ثنائية البعد وكود الدخول 12 بت 000000001111 في الصفوف أحادية البعد، ويفك أكواد الكلمات الحرفية، ويحترم بتة وسم الخروج عند استئناف ترميز الأطوال
لماذا لا أثر لـ DamagedRowsBeforeError على فاكس Group 4 لدي؟
الاسترداد يتطلب علامات EOL لإعادة المزامنة. يطبق HotPDF DamagedRowsBeforeError فقط عندما تكون K صفراً أو موجباً و EndOfLine صائباً؛ بيانات Group 4 (بـ K سالب) بلا EOL، فأي صف تالف ينهي الفك
ماذا يعيد ExtractLoadedImage لصورة CCITT متضررة؟
يعيد THotPDF.ExtractLoadedImage قيمة nil عندما يتجاوز الضرر ما يسمح به DamagedRowsBeforeError أو تعذّر استرداده إطلاقاً. ولا يعيد أبداً صورة نقطية جزئية، فاختبر من أجل nil واستخدم ExtractLoadedImageToStream للاحتفاظ بالبايتات الخام
يعالج HotPDF صور CCITT و JBIG2 و JPEG و JPEG 2000 و Flate في ملفات PDF محمّلة من كود Delphi و C++Builder عادي، دون DLL خارجي لمسار الفاكس. التفاصيل والإصدارات على صفحة مكون HotPDF PDF لـ Delphi