XFA، وهي بنية نماذج XML (XML Forms Architecture)، تم إهمالها. يحملها ISO 32000-1 في §12.7 مع ملاحظة أنه تمت إزالتها من PDF 2.0، وتتخلى العارضات الحديثة (modern viewers) عن محركات XFA الخاصة بها واحداً تلو الآخر. لا شيء من ذلك قد أفرغ الأرشيفات. تمت كتابة نماذج الاستيعاب الحكومية، وتطبيقات التأمين، والبيانات المصرفية كـ XFA للجزء الأفضل من عقدين من الزمن، ولا تزال هذه الملفات تصل إلى صناديق الوارد وخطوط أنابيب المستندات اليوم. عندما يتوقف العارض الذي اعتاد عرضها عن القيام بذلك، يتحول النموذج إلى صفحة فارغة بها عنصر نائب (placeholder) "please open in a different reader". الإصلاح الدائم هو تسوية (flatten) XFA إلى محتوى PDF ثابت يمكن لأي قارئ رسمه
الجزء الصعب من تلك التسوية (flattening) ليس الحقول. يتم تعيين مربعات النص ومربعات الاختيار على أدوات AcroForm الذكية (widgets) بشكل نظيف بما فيه الكفاية. الجزء الصعب هو النص المنسق الذي يخزنه XFA داخل عنصر رسم، في كتلة <exData contentType="text/html">. تلك الكتلة عبارة عن مجموعة فرعية من HTML بتنسيق مضمن (inline styling)، وغالباً ما تكون هناك مراسٍ (anchors). إن إدخالها إلى الصفحة يعني إعادة إنتاج كل من النص المنسق والارتباطات التشعبية الحية، والارتباطات التشعبية هي المكان الذي تستسلم فيه معظم عمليات التنفيذ بهدوء
كيف يبدو النص المنسق لـ XFA في الواقع
هيكل exData عبارة عن شريحة صغيرة من XHTML. الفقرة عبارة عن <p>؛ وامتداد الأحرف المنسق عبارة عن <span> مع CSS المضمن الخاص به للوزن (weight)، والميل (posture)، واللون، والحجم؛ والارتباط التشعبي عبارة عن <a href="..."> يلتف حول نصه المرئي. يمكن أن يحتوي السطر الواحد على عدة امتدادات (spans) متتالية، كل منها بتنسيق مختلف، ويمكن أن يكون أحدها مرساة. التنسيق ليس زخرفة يمكن إسقاطها. يجب أن يظل البند المعروض باللون الأحمر الغامق لأنه تحذير قانوني غامقاً وأحمراً بعد التسوية، وإلا فإن المستند المسوى سيشوه النسخة الأصلية
لذا لا يمكن لمحرك التسوية التعامل مع الكتلة كسلسلة واحدة. يجب أن يمشي في البنية المضمنة، ويحل النمط الفعال (effective style) لكل مسار (run) عن طريق وضع طبقات CSS المضمنة للامتداد (span) فوق الخط الأساسي لعنصر الرسم، وتخطيط المسارات واحداً تلو الآخر عبر السطر. يقوم HotPDF بنمذجة كل من هذه الأجزاء المخططة (laid-out fragments) كسجل TXFARichRun داخلي. يحمل السجل نص المسار، ونمطه الذي تم حله، ومربعه المُقاس، وبالنسبة للمرساة، الـ Href الذي يشير إليه
تخطيط المسارات من اليسار إلى اليمين
تحديد المواضع هو المكان الذي يتوقف فيه النص المنسق عن كونه مشكلة تحليل ويصبح مشكلة تنضيد حروف (typesetting). تتشارك المسارات في سطر واحد، لذلك يبدأ كل مسار من حيث انتهى المسار السابق. لا يوجد ترميز (markup) يسجل هذه المواضع؛ يجب قياسها. يقيس الروتين الداخلي LayoutRichText الخاص بالمحرك كل مسار بنفس مقاييس الخط التي سترسمه لاحقاً، ثم يضبط الإزاحة الأفقية للمسار على المجموع الجاري (running sum) لجميع عروض المسارات السابقة. يبدأ المسار الأول من أصل صندوق الرسم، ويبدأ المسار الثاني عند عرض المسار الأول، والمسار الثالث عند العرض المشترك لأول مسارين، وهكذا عبر السطر
هذا هو السبب في أهمية محاذاة خط القياس. تقيس تمريرة التخطيط التقدمات (advances)؛ وتقوم تمريرة تقديم منفصلة برسم الرموز. إذا اختلف هاتان التمريرتان حول الخط، فلن تقع الصناديق التي حسبها التخطيط تحت الرموز التي يرسمها العارض (renderer). يبقيها HotPDF متماشية عن طريق تعيين النمط الذي تم حله لكل مسار على مواصفات خط، من خلال المساعد الداخلي RunStyleToFontSpec، والذي يطابق الإعدادات الافتراضية الخاصة بالعارض لـ Arial بحجم 10 نقاط. يتوافق التقدم المقاس والنص المرسوم حينئذٍ، ويغطي الصندوق المحسوب للمسار بصدق الأحرف التي يراها القارئ
// Conceptual shape of one laid-out run. The engine builds an array of these
// internally; you never construct them yourself, but the fields explain how a
// link's hit box is derived from measured geometry rather than from text.
type
TRichRunInfo = record
Dx, Dy : Double; // top-left, relative to the draw-box origin
W, H : Double; // measured run box (width from the layout pass)
Text : AnsiString; // the run's visible characters
Href : AnsiString; // URI target for an <a> run, '' otherwise
end;
من مسار مرساة إلى تعليق توضيحي لارتباط PDF
الارتباط التشعبي في ملف PDF النهائي ليس جزءاً من محتوى الصفحة. إنه كائن منفصل، تعليق توضيحي لارتباط (Link annotation)، تم وصفه في ISO 32000-1 §12.5.6.5. يحتوي التعليق التوضيحي على /Rect يحدد المستطيل القابل للنقر على الصفحة وإجراءً يتم تشغيله عند النقر فوق المستطيل. بالنسبة للارتباط الخارجي، يكون الإجراء هو إجراء URI: /S /URI مع العنوان الهدف كسلسلة /URI الخاصة به. النص المرئي الموجود تحتها هو محتوى صفحة عادي؛ التعليق التوضيحي هو المنطقة النشطة (hot zone) غير المرئية الموضوعة فوقه
يتبع مسار التسوية هذا النموذج تماماً. عندما يحمل مسار Href، يقوم HotPDF أولاً برسم النص المنسق، ثم يبني تعليقاً توضيحياً لارتباط فوق صندوق المسار. نقطة الدخول العامة لذلك التعليق التوضيحي هي طريقة الصفحة AddURILink، والتي تنشئ الكائن /Type /Annot /Subtype /Link بإجراء /URI وتعيد قاموس التعليق التوضيحي. مستطيله هو الصندوق المقاس للمسار، المترجم من الإحداثيات المحلية لعنصر الرسم إلى إحداثيات الصفحة. والنتيجة هي ارتباط يهبط بدقة على نص المرساة وليس في أي مكان آخر
// The same public API the flatten path uses for each anchor run. It produces
// an ISO 32000-1 12.5.6.5 Link annotation: /Subtype /Link with a /URI action
// over the given rectangle. The optional description fills /Contents so a
// screen reader can announce the target.
var
LinkRect: TRect;
Annot: THPDFDictionaryObject;
begin
LinkRect := Rect(72, 690, 268, 706); // page-space hit box for the run
Annot := Pdf.CurrentPage.AddURILink(LinkRect,
'https://www.example.gov/appeal', 'File an appeal online');
end;
لماذا يجب أن يأتي صندوق الإصابة (hit box) من عروض مُقاسة
من المغري تخيل تحديد موقع الارتباط من خلال البحث في الصفحة عن نصها المرئي ورسم المستطيل حول أي شيء يتم العثور عليه. هذا لا يعمل، والسبب أساسي في كيفية تخزين النص المُسوى. يتم رسم المسارات المنسقة بخطوط فرعية مضمنة (embedded subset fonts). يقوم الخط الفرعي بإعادة ترقيم الرموز التي يحتفظ بها، لذا فإن تيار محتوى الصفحة يحمل أكواد CID السداسية العشرية، وليس أكواد الأحرف الأصلية. البايتات الموجودة على الصفحة ليست الحروف التي يقرأها الإنسان، ولا يمكن البحث فيها كنص. لا يعثر البحث عن تعليق المرساة على أي شيء، لأن هذا التعليق لا يوجد كنص حرفي في أي مكان في التيار
المرساة الوحيدة الموثوقة للمستطيل هي الهندسة التي أنتجتها تمريرة التخطيط بالفعل. تم حساب إزاحة كل مسار وعرضه المُقاس أثناء تدفق السطر، قبل إعادة ترقيم أي رمز، وهي تصف مكان ظهور النص مادياً. لذلك يأخذ HotPDF مستطيل الارتباط مباشرة من صندوق المسار الموضوع (laid-down box) بدلاً من أي بحث عن نص. نظراً لأن القياس استخدم خط العرض (render font)، فإن الصندوق يكون صحيحاً بغض النظر عن إنشاء مجموعة فرعية (subsetting). تنجو الهندسة من التشفير؛ بينما لا ينجو النص. هذه هي الحجة بأكملها لتحديد موضع العرض المُقاس (measured-width positioning)، وهو السبب في أن المُسوي (flattener) الذي يحاول تعديل الارتباطات عن طريق البحث عن النص ينتج عنه مناطق إصابة (hit zones) تنجرف أو تتلاشى
قيادة التسوية من التعليمات البرمجية الخاصة بك
بالنسبة لملف PDF يحتوي بالفعل على حزمة XFA، تكون نقطة الدخول هي FlattenLoadedXFA. قم بتحميل المستند، واستدعاء الطريقة، وحفظ النتيجة. تقرر المعلمة Editable ما يحدث لحقول النموذج: مرر True للاحتفاظ بها كأدوات AcroForm ذكية قابلة للتعبئة، أو False لتمييز كل أداة ذكية للقراءة فقط بحيث يكون الإخراج سجلاً مجمداً. يتم إنتاج كتل رسم النص المنسق، مع مساراتها المنسقة والتعليقات التوضيحية للارتباطات، في كلتا الحالتين. تعيد الدالة عدد الأدوات الذكية التي أرسلتها
var
Pdf: THotPDF;
Emitted, i: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('xfa_appeal_form.pdf');
// True keeps fields fillable; False freezes them read-only.
Emitted := Pdf.FlattenLoadedXFA(True);
// Anything the engine could not map is reported, not raised.
for i := 0 to Pdf.XFAFlattenWarnings.Count - 1 do
Writeln('XFA warning: ', Pdf.XFAFlattenWarnings[i]);
Pdf.SaveLoadedDocument('appeal_form_flat.pdf');
Writeln('Widgets emitted: ', Emitted);
finally
Pdf.Free;
end;
end;
اقرأ دائماً XFAFlattenWarnings بعد الاستدعاء. يتم مسح القائمة في بداية كل تسوية وتتراكم سطر لكل عنصر يرفض المحرك عرضه: نوع حقل غير مدعوم، أو صورة رسم لن يتم فك تشفيرها، أو كتلة exData مع عدم وجود امتدادات (spans) قابلة للاستخدام. لا يثير أي من ذلك استثناءً، لذا فإن قائمة التحذيرات الفارغة هي دليلك على أن كل شيء تم تعيينه (mapped)، وقائمة غير فارغة تخبرك بالضبط بالأصول التي يجب فحصها. عندما تحتفظ بـ XFA الخام كبايتات XDP بدلاً من ملف PDF مُحمل، فإن الطريقة الشقيقة ApplyXFAAsAcroForm تأخذ تلك البايتات مباشرة وتشارك نفس مسار التعليمات البرمجية ونفس سلوك التحذيرات. تسير طريقة AddXFAPacket التكميلية في الاتجاه الآخر، حيث يتم دمج حزمة XFA في المستند الذي تقوم ببنائه
تأكيد النتيجة في القارئ
افتح الملف المُسوى في Acrobat، أو أي عارض حالي، وتحقق من شيئين. أولاً، النص المنسق معروض وتنسيقه سليم: المسارات الغامقة تظهر غامقة، والمسارات الملونة تحمل لونها، وتستقر الامتدادات بالترتيب الصحيح على السطر بدلاً من التداخل أو التشغيل خارج الصندوق. ثانياً، الارتباطات التشعبية حية. مرر مؤشر الماوس فوق مرساة وسيظهر شريط الحالة العنوان الهدف؛ انقر فوقه وسيفتحه إجراء URI. استخدم فاحص التعليقات التوضيحية الخاص بالعارض لتأكيد أن كل واحد منها هو تعليق توضيحي /Link حقيقي يعانق /Rect الخاص به نص المرساة، جالساً فوق محتوى أصبح الآن مجرد رموز مرسومة عادية بدلاً من XFA المعروض بواسطة النموذج (form-rendered XFA). هذا المزيج، النص الثابت المنسق بالإضافة إلى التعليقات التوضيحية الحقيقية للارتباطات على المستطيلات الصحيحة، هو ما يجعل المستند المُسوى يعيش لفترة أطول من محركات XFA التي لم يعد بحاجة إليها
تمت تغطية تسوية الحقول نفسها، ومربعات النص، ومربعات الاختيار، وقوائم الخيارات التي تحيط بهذا النص المنسق، في الإرشادات التفصيلية الخاصة بنا حول تسوية نماذج XFA إلى أدوات AcroForm الذكية. للحصول على القصة الأوسع لبناء ووضع التعليقات التوضيحية للارتباط يدوياً، بخلاف تلك التي ينشئها مسار التسوية، راجع العمل مع التعليقات التوضيحية لـ PDF في HotPDF. كلاهما يعتمد على نفس التعليقات التوضيحية ونماذج النماذج التي يتم شحنها مع مكون HotPDF لكل من Delphi و C++Builder