مقال تقني

تصدير XFDF واستيراده في Delphi مع PDFium Component

يتبادل PDFium Component لـ Delphi وLazarus بيانات النماذج والتعليقات التوضيحية عبر XFDF، صيغة التبادل بلغة XML المعرَّفة في ISO 19444-1. فتُسلسل TPdf.ExportXFDF كل قيمة حقل نموذج وكل تعليق توضيحي مدعوم في المستند المحمَّل إلى ملف XFDF أو TStream؛ وتقرأ TPdf.ImportXFDF مستند XFDF من جديد، فتعيد إنشاء التعليقات التوضيحية على صفحاتها وتطبّق قيم الحقول. وتغطي رحلة الذهاب والإياب الواحدة هذه سيري عمل يحتاج إليهما كل تطبيق مستندات في النهاية: مراجع يعلّم على ملف PDF في Acrobat ويرسل لك التعليقات بالبريد لدمجها، أو قاعدة امتثال تقضي بأن تقيم بيانات النموذج في ملف منفصل قابل للمقارنة والتدقيق بدلًا من خبزها داخل ملف PDF نفسه

مخطط رحلة XFDF ذهابًا وإيابًا في Delphi: تكتب TPdf.ExportXFDF حقول النماذج و18 نوعًا فرعيًا من التعليقات التوضيحية إلى حمولة XML صغيرة تدمجها TPdf.ImportXFDF من جديد في أي نسخة من ملف PDF
تحوّل ExportXFDF المستند كله إلى حمولة XML صغيرة وفق ISO 19444-1، وتطبّق ImportXFDF الحقول والتعليقات التوضيحية نفسها من جديد على أي نسخة من ملف PDF

هل يدعم PDFium استيراد XFDF وتصديره؟

مكتبة PDFium نفسها لا تدعمه. فواجهة C الأصلية لا تحتوي على أي دوال لـ XFDF أو FDF على الإطلاق — لا شيء في ترويساتها العامة يقرأ أيًّا من الصيغتين أو يكتبها، ولذا لا يوصلك أي قدر من التغليف إلى هناك. ومن ثم ينفّذ PDFium Component محرك XFDF بأكمله بلغة Pascal، في وحدة مخصصة تبني XML وتحلّله مباشرة فوق نموذج بيانات التعليقات التوضيحية وحقول النماذج القائم في TPdf. ويُصدر الكاتب XML يدويًا والقارئ محلّل آلة حالات مكتوب يدويًا، ولذا لا تضيف الميزة أي اعتماد على XMLDoc في Delphi أو وحدات DOM في FPC وتسلك السلوك نفسه تمامًا على Delphi وLazarus

ويهم خيار التصميم هذا حين تقيّم البدائل. فإذا كنت تستدعي دوال FPDF_* الخام من Pascal، فإن XFDF جدار: القدرة ببساطة ليست في مكتبة DLL. ويعبر المكوّن ذلك الجدار بمعاملة XFDF كمشكلة تسلسل محضة — جمع قيم الحقول وسجلات التعليقات التوضيحية التي يعرف الغلاف أصلًا كيف يقرأها، وكتابتها كـ XML قياسي، وعكس العملية عند الاستيراد

ماذا يحتوي ملف XFDF؟

يعرّف ISO 19444-1 صيغة XFDF بوصفها تمثيل XML لـ FDF، وتنقسم حمولتها إلى كتلتين علويتين. فعنصر <fields> يحمل أسماء حقول النماذج الهرمية وقيمها — كل ما كتبه المستخدم أو اختاره أو علّمه في AcroForm. وعنصر <annots> يحمل التعليقات التوضيحية: تُصدر TPdf.ExportXFDF ثمانية عشر نوعًا فرعيًا — text وhighlight وunderline وstrikeout وsquiggly وline وcircle وsquare وcaret وpolygon وpolyline وstamp وink وfreetext وfileattachment وsound وlink وredact. وتغيب تعليقات widget التوضيحية عمدًا عن <annots> لأن بياناتها تسافر في <fields>، والأنواع الفرعية الداخلية للعارض مثل Popup ليست جزءًا من مفردات XFDF

تشريح PDFium Component لملف XFDF يُظهر كتلة fields الهرمية لقيم AcroForm بجانب كتلة annots التي تسرد 18 نوعًا فرعيًا مدعومًا من التعليقات التوضيحية
تحمل كتلة fields قيم AcroForm الهرمية، بينما تنقل كتلة annots 18 نوعًا فرعيًا من التعليم وتستبعد عناصر widget والأنواع الداخلية للعارض

ولجعل رحلة الذهاب والإياب أمينة، وُسّع سجل TPdfAnnotation بالبيانات الوصفية التي يتوقعها XFDF: Name (المعرّف الفريد NM)، وSubject، وModificationDate، وCreationDate، وIcon، وOpacity، ونقاط طرفي الخط، ورؤوس المضلعات والخطوط المتعددة، ومسارات إيماءات الحبر. ويقترن كل حقل جديد بقيمة منطقية حارسة Has*، ولذا يظل الكود الذي بنى تعليقات توضيحية على إصدارات سابقة يُصرَّف ويظل ينتج القواميس نفسها — فالحقل غير المضبوط لا يُكتب أبدًا. وإذا كنت تنشئ التعليم برمجيًا أصلًا، فالسجل نفسه الذي تستخدمه لـ تعليقات تعليم النص التوضيحية بالنقاط الرباعية يحمل الآن كل ما يحتاجه XFDF

كيف تصدّر بيانات النماذج والتعليقات التوضيحية إلى XFDF؟

استدعاء واحد يؤدي المستند كله. حمّل ملف PDF مع تفعيل تعبئة النماذج، واستدعِ ExportXFDF، فيجتاز المكوّن كل صفحة، ويجمع قيم الحقول والتعليقات التوضيحية، ويكتب XML. والقيمة المُعادة هي عدد بايتات UTF-8 المكتوبة، ما يوفّر فحص سلامة رخيصًا في السجلات

var
  Pdf: TPdf;
  Bytes: Integer;
begin
  Pdf := TPdf.Create(Self);
  Pdf.FormFill := True;            // مطلوب كي تكون قيم الحقول حية
  Pdf.FileName := 'expense-report.pdf';
  Pdf.Active := True;
  Bytes := Pdf.ExportXFDF('expense-report.xfdf');
  ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;

ولكلا الاتجاهين تحميل زائد بـ TStream، ولذا لا شيء يفرض ملفًا مؤقتًا على القرص. والتصدير إلى TMemoryStream هو الشكل الطبيعي حين يكون XFDF متجهًا إلى استجابة HTTP أو كائن blob في قاعدة بيانات أو مدخل أرشيف موقَّع

var
  Buffer: TMemoryStream;
begin
  Buffer := TMemoryStream.Create;
  try
    Pdf.ExportXFDF(Buffer);        // XML نفسه، دون أي ملف
    Buffer.Position := 0;
    // سلّم الدفق إلى استجابة ويب أو عمود blob أو مدخل zip
  finally
    Buffer.Free;
  end;
end;

كيف تدمج ImportXFDF التعليقات من جديد في المستند؟

جانب الاستيراد هو حيث يكتمل سير عمل المراجع. فزميل يعلّق على العقد في Acrobat، ويصدّر التعليقات كـ XFDF، ويرسل لك بضعة كيلوبايتات من XML بدلًا من نسخة ثانية من ملف PDF. وتحلّل TPdf.ImportXFDF ذلك الملف، وتنشئ كل تعليق توضيحي على الصفحة التي تسمّيها سمته page، وتكتب قيم الحقول في تعليقات widget التوضيحية المطابقة. وتُعيد الدالة العدد المجمّع للحقول والتعليقات التوضيحية المطبَّقة، كي تؤكد الواجهة بالضبط مقدار ما وصل

var
  Applied: Integer;
begin
  Applied := Pdf.ImportXFDF('review-comments.xfdf');
  StatusBar.SimpleText :=
    Format('%d fields and annotations merged', [Applied]);
end;

والمحلّل ماسح وسوم مكتوب يدويًا مبني للتسامح لا للصرامة: تُتخطّى العناصر المجهولة، وتُتجاهل تعليمات المعالجة وإعلانات DOCTYPE والتعليقات، وتُسقَط أسماء الأعلام غير المعروفة، وتُفكّ قيم السمات عبر المجموعة الكاملة لكيانات XML ومراجع الأحرف الرقمية، بما فيها الأزواج البديلة في UTF-16. وملفات XFDF المنتَجة بواسطة Acrobat أو عارض ويب أو أداة أخرى تُدمج دون تكلّف. وبمجرد وجود التعليقات التوضيحية في المستند، تستمر دورة المراجعة بآليات الرد والحالة المشروحة في بناء سير عمل لمراجعة التعليقات التوضيحية مع PDFium Component، وتشارك قيم الحقول المستوردة في منطق ترتيب التنقّل العادي المشروح في التنقّل بين حقول النماذج

لماذا تهم الفواصل العشرية في XFDF؟

كل تعليق توضيحي في XFDF يتموضع بسلاسل إحداثيات — rect="70.5,540,200,560" وأخواتها — ويشترط ISO 19444-1 النقطة فاصلًا عشريًا. وتنسيق الأعداد العشرية الافتراضي في Delphi يتبع الإعدادات المحلية لـ Windows، ولذا على نظام ألماني أو فرنسي تحوّل FloatToStr الساذجة 70.5 إلى 70,5، ما يفسد قائمة إحداثيات مفصولة بفواصل إلى هراء ترفضه المعالجات الأخرى أو تسيء قراءته. ويطبّع PDFium Component كل رقم يكتبه: يُعاد كتابة الفاصل العشري المحلي إلى نقطة وتُشذَّب الأصفار الزائدة، ولذا يكون الملف المصدَّر متطابقًا بايتًا ببايت سواء أُنتج على جهاز en-US أو de-DE. وإذا سبق لك أن نقّحت أداة PDF عملت في المكتب وفشلت عند موقع عميل أوروبي، فقد قابلت هذه الفئة من الأخطاء بعينها — ويجدر بك معرفة أن المكوّن يتولاها عنك

ما حدود رحلة الذهاب والإياب؟

حدّان يستحقان الذكر بوضوح. أولًا، ينقل XFDF بيانات التعليقات التوضيحية لا مظاهرها المصيَّرة — فدفوق المظهر ليست جزءًا من الصيغة، ولذا يعيد العارض المستقبِل توليد مظهر كل تعليق توضيحي من خصائصه. فالتظليل أو المربع سيُصيَّر بشكل صحيح في كل مكان؛ أما الختم ذو المظهر المخصص فسيتراجع إلى ما يرسمه العارض الهدف لاسم ذلك الختم. ثانيًا، تملك واجهة PDFium الأساسية دوال ضبط للسلاسل لكن لا شيء لأرقام التعليقات التوضيحية أو هندسة المسارات، ولذا فإن Opacity ونقاط طرفي الخط والرؤوس وإيماءات الحبر للتصدير فقط: يحفظها المكوّن بأمانة عند كتابة XFDF، لكنه لا يستطيع إعادة كتابتها في ملف PDF عند الاستيراد. أما قيم الحقول والمحتويات والألوان والمستطيلات والنقاط الرباعية والتواريخ وبيانات الهوية الوصفية فكلها تقطع رحلة الذهاب والإياب في الاتجاهين

ثلاث درجات من أمانة رحلة XFDF ذهابًا وإيابًا في Delphi: خصائص تسافر في الاتجاهين، وهندسة للتصدير فقط مثل الرؤوس ومسارات الحبر، ودفوق مظهر لا تغادر الملف أبدًا
تقطع الخصائص الأساسية رحلة الذهاب والإياب في الاتجاهين، وتبقى الهندسة الشبيهة بالمسارات للتصدير فقط، ويعيد العارض الذي يستقبل الملف توليد دفوق المظهر

وللبدء عمليًا، يُشحن نموذج XfdfLab ضمن مجموعات العروض التوضيحية لـ Delphi وC++Builder وLazarus ويوصّل كلا الاستدعاءين بأزرار فوق أي ملف PDF تفتحه. ودعم XFDF مضمَّن في الإصدار الحالي من PDFium Component، إلى جانب واجهات التعليقات التوضيحية والنماذج البرمجية التي يُبنى عليها