تداول HotPDF قيم صندوق القائمة متعدد التحديد ذهاباً وإياباً عبر FDF و XFDF بإبقاء قيمة الحقل مصفوفة من الطرف إلى الطرف. ومنذ الإصدار 2.755.0 تكتب ExportLoadedFormToFDF و ExportLoadedInterchangeToFDF و ExportLoadedFormToXFDF كل خيار مختار سلسلة FDF خاصة أو عنصر <value> خاص في XFDF، وتفحص طرق الاستيراد المقابلة كل قيمة مقابل خيارات الحقل وتعيد بناء فهارس التحديد /I قبل أن تغير شيئاً. لا شيء يُلصق في سلسلة واحدة في الطريق
الفشل الذي يعالجه هذا سهل الاستنساخ. خذ نموذج طلب بصندوق قائمة متعدد التحديد لخيارات المنتجات، ودع مستخدماً يختار اثنين منها، وصدّر بيانات النموذج لنظام مكتب خلفي، ثم استورد الملف المحرر مجدداً إلى الـ PDF. قبل هذا التغيير كان صندوق القائمة يعود فارغاً أو خاطئاً. السبب أن إحدى قيم التصدير كانت تحوي فاصل سطر، وكان المسار القديم قد سطّح التحديدات سلسلة واحدة مفصولة بالأسطر. واستخراج تحديدات متعددة من تلك السلسلة لم يكن موثوقاً يوماً، ومع قيمة تصدير تحوي هي نفسها فاصل سطر يستحيل أن تعمل أصلاً
لماذا يكسر ضم قيم متعددة التحديد بفواصل الأسطر الجولة ذهاباً وإياباً؟
ضم التحديدات في سلسلة واحدة يرمي الحدود بين القيم، ويمكن أن تحوي القيمة الفاصل نفسه، فلا مستورد يستطيع تقسيم السلسلة عائداً بشكل صحيح. يجيز §12.7.4.4 من ISO 32000-1 لمدخل /V لحقل اختيار أن يكون سلسلة نص واحدة أو مصفوفة سلاسل نصية، وصندوق القائمة بعلم MultiSelect (بت 22 من /Ff) يستخدم صيغة المصفوفة متى اختير أكثر من خيار. ويعرف القسم نفسه /I مصفوفة فهارس خيارات صفرية الأساس بترتيب تصاعدي، يستخدمها العارضون للتمييز بين خيارين يتشاركان صدفة قيمة تصدير. وفي HotPDF يقرأ الجالب العددي GetFormFieldValue صيغة السلسلة وحدها، فتمرير مصفوفة عبره خفّض التصدير إلى سلسلة فارغة، وكان الاستيراد القديم في XFDF يضم عناصر <value> المكررة بـ LF. تخيل خياراً مصدراً Deep، ثم تغذية سطر، ثم Blue: بعد الضم يمكن أن تكون Deep\nBlue\nRed تحديدين أو ثلاثة، والملف لا يعطي سبيل لمعرفة أيهما. الإصلاح هو التوقف عن استخدام قيمة عددية في وسط الجولة كلياً
ماذا تحوي ملفات FDF و XFDF المصدرة؟
تكتب HotPDF قيمة متعددة التحديد مصفوفة مكتوبة النوع في FDF وعنصر <value> واحداً لكل تحديد في XFDF، فتبقى الحدود مرئية على القرص. في FDF يحفظ كل عنصر تهجئته في PDF المصدر: السلاسل السداسية تخرج سداسية، والسلاسل الحرفية تهربها مساعدة واحدة تحول CR و LF إلى \r و \n. وفي XFDF يحمل الجذر xml:space="preserve" كما يشترط ISO 19444-1، أي أن أي فراغ أبيض داخل عنصر نصي يُعد بيانات. لذا تكتب HotPDF وسم البداية والنص المهرب ووسم النهاية لكل <value> قطعة واحدة، وتُبقي الإزاحة خارج العنصر، وترمز CR و LF و TAB مراجع محارف حتى لا يستطيع محلل XML يطبق توحيد نهايات الأسطر تغيير البايتات الأصلية
<!-- FDF: مصفوفة مكتوبة النوع لكل حقل -->
<< /T (options) /V [(Deep\nBlue) (Red)] >>
<< /T (region) /V [<45553132>] >>
<!-- XFDF: <value> واحدة لكل تحديد -->
<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">
<fields>
<field name="options">
<value>Deep
Blue</value>
<value>Red</value>
</field>
</fields>
</xfdf>
حالتان حديتان للتصدير تستحقان المعرفة قبل كتابة كود الاستدعاء. أولهما تبني ExportLoadedFormToFDF جسم FDF كاملاً في الذاكرة قبل إنشاء الملف الهدف (أُصلح في 2.755.1)، فقيمة لا يمكن تصديرها، مثل مصفوفة تحوي ما ليس سلاسل، ترفع استثناء دون بتر ملف قائم. وثانيتهما أن التحديد الفارغ على صندوق قائمة يقدم كذلك قيمة تصدير سلسلة فارغة ملتبس في XFDF، لأن <value/> قد تعني لا شيء مختار أو أن الخيار الفارغ مختار. ترفع ExportLoadedFormToXFDF استثناء في تلك الحالة بدل التخمين، وترفعه قبل فتح الملف الهدف. وليس لدى FDF ذلك الالتباس، إذ إن /V [] و /V [()] متمايزتان. وكلا المصدّرين في FDF يتخطى أيضاً الطرافية الودجية فقط التي بلا اسم /T، مطابقاً المصدّر في XFDF، لأن لا مستورد يستطيع أبداً مطابقة تلك المدخلات مجدداً بحقل
var
Pdf: THotPDF;
Written: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
begin
// صناديق القوائم متعددة التحديد تُكتب /V [(...) (...)]
Written := Pdf.ExportLoadedFormToFDF('order-form.fdf');
try
Pdf.ExportLoadedFormToXFDF('order-form.xfdf');
except
on E: Exception do
// تحديد فارغ زائد خيار تصدير فارغ: لا يستطيع XFDF التفريق
// بينهما، ويُترك ملف .xfdf القائم بلا مساس
ShowMessage('XFDF export refused: ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
end;
كيف يتحقق HotPDF من قيمة متعددة التحديد عند الاستيراد؟
يقبل HotPDF مصفوفة مستوردة فقط حين يكون الهدف حقل اختيار بعلم MultiSelect مضبوط وكل قيمة في المصفوفة تطابق قيمة تصدير في مصفوفة /Opt للحقل. كل خانة خيار تستخدم مرة واحدة، فقائمة بخيارين يتشاركان قيمة التصدير b تقبل [<62> <62>] تحديدين متمايزين وترفض b ثالثاً. وتبقى /I المعاد بناؤها بترتيب /Opt لا بترتيب القيم الواردة، إذ يشترط §12.7.4.4 فهارس تصاعدية. يبني HotPDF /V و /I الجديدتين كائنين منفصلين ويسندهما فقط بعد أن تجتاز كل قيمة التحقق، فقيمة مرفوضة لا تترك أبداً نصف مصفوفة أو فهارس قديمة. وتُكتب النسخة إلى الحقل المستورد لا إلى مصفوفة سلف مشتركة، وتبقى تهجئات hex الواردة من FDF سداسية عبر الحفظ، وتوسم الحقول التي تعتمد حساباتها على صندوق القائمة لإعادة الحساب. وإن كنت تحتاج فقط ضبط قيمة واحدة، يمر ضبط قيمة حقل نموذج واحد في PDF محمّل عبر المسار العددي، الذي لا يعالج بحكم تصميمه تحديدات متعددة
بعض الأدوات الأخرى تكتب قيم تصدير ASCII عادية سلاسل سداسية دون علامة ترتيب بايتات، مثل <416272>، ثم تصدّر XFDF بكتابة تلك الأرقام السداسية نصاً. فشلت المقارنة الحرفية الصارمة في طريق العودة، وأجهض الاستيراد. أضاف الإصدار 2.755.1 محاولة واحدة: حين لا تطابق قيمة أي خيار يفك HPDFHexSpellingText النص حمولة hex ويقارن النتيجة مجدداً. المحاولة تنطبق على المدخل الذي كان سيرفع استثناء وإلا، فلا تغير أبداً قيمة طابقت بالفعل. وجعل الإصدار نفسه المسارين العددي والمصفوفي يستخدمان مفكك Unicode نفسه، الذي يفهم PDFDocEncoding و UTF-16 بعلامة ترتيب بايتات أي منها و UTF-8. قبل ذلك كان يمكن لقيمة منطقية واحدة أن تطابق على مسار وتفشل على الآخر في مستندات تمزج الترميزات
لماذا قد يفقد ملف FDF سليم حقولاً أثناء التحليل؟
ماسح FDF لا يتتبع السلاسل السداسية يمكن أن يقطع قاموس حقل نصفين حين ينتهي قيمة سداسية بجوار منتهٍ القاموس مباشرة. في << /T (region) /V <416273>>> يغلق أول > السلسلة السداسية، لكن ماسحاً ساذجاً يقرأها مع > التالية نهاية القاموس ويُسقط الحقل بصمت. كان مستورد FDF على مستوى الملف يتتبع أصلاً هل هو داخل سلسلة سداسية، وفي 2.755.1 فعل ماسحا المصفوفة والقاموس خلف ImportLoadedInterchangeFromFDF الشيء نفسه. وقضية ثانية تخص المراجع غير المباشرة. ملف FDF مستند بجملة PDF صغير بترقيم كائنات خاصه (§12.7.7 من ISO 32000-1)، فقيمة مثل /V [11 0 R] تشير إلى الكائن 11 من ملف FDF، لا إلى الكائن 11 من الـ PDF الذي تملؤه. محلل FDF المبسط في HotPDF لا يحل المراجع داخل الملف، فيرفض مثل تلك المصفوفة بدل قراءة أي كائن كان 11 صدفة في المستند الهدف
استيراد الملف والتدفق و XFDF تبلّغ الأخطاء بشكل مختلف
طرق الاستيراد الثلاث تتحقق بالطريقة نفسها لكنها تبلّغ الفشل بشكل مختلف، ويستحق اختيار واحدة عمداً. تتخطى ImportLoadedFormFromFDF أي حقل يفشل في التحقق وتعيد عدد الحقول التي طبقتها فعلاً، فعدد أدنى من المتوقع هو العلامة الوحيدة على مشكلة. وترفع ImportLoadedInterchangeFromFDF و ImportLoadedFormFromXFDF استثناء عند أول حقل مرفوض. كل حقل يُوثق على حدة، فالحقول المعالجة قبل الاستثناء تحفظ قيمها الجديدة. لا تعامل أياً منها بوصفها عملية على ملف التبادل كله: إن احتجت سلوك الكل أو لا شيء، ارمِ المستند المحمّل حين يحدث استثناء بدل حفظه
var
Pdf: THotPDF;
Source: TMemoryStream;
Status: AnsiString;
Info: THPDFFDFInterchangeInfo;
begin
Pdf := THotPDF.Create(nil);
Source := TMemoryStream.Create;
try
Source.LoadFromFile('order-form-reviewed.fdf');
if Pdf.LoadFromFile('order-form.pdf', '') > 0 then
try
// الحقول فقط؛ قيمة خارج /Opt أو هدف غير متعدد التحديد يرفع استثناء
if Pdf.ImportLoadedInterchangeFromFDF(Source, True, False, Status, Info) then
Pdf.SaveLoadedDocument('order-form-filled.pdf');
except
on E: Exception do
ShowMessage('Import rejected, nothing saved: ' + E.Message);
end;
finally
Source.Free;
Pdf.Free;
end;
end;
توسيع نداءات XFDF دون كسر المستدعين القائمين
دعم المصفوفة في وحدة XFDF الأدنى مستوى يسكن سجلاً منفصلاً، THPDFXFDFArrayAccess، وحمولات زائدة جديدة من HPDFXFDFExportFields و HPDFXFDFImportFields، لا حقولاً إضافية أُلحقت بنهاية سجل THPDFXFDFAccess القائم. السبب التوافق الثنائي. الكود الذي يملأ THPDFXFDFAccess متغيراً محلياً يضبط غالباً الخانات التي يعرفها فقط ولا يصفّر البقية أبداً، فمؤشر دالة جديد يُلحق بذلك السجل يحوي خردة مكدس، وتأخذ المكتبة على أنه نداء حقيقي. وبسجل منفصل يحتفظ المستدعون القدماء بالتخطيط القائم والحمولات القديمة، وتمرر تلك الحمولات داخلياً سجل مصفوفة كل عناصرها nil. ما زالت حمولة الاستيراد العددي الأصلية تضم القيم المكررة بـ LF توافقاً، وحمولة الواعية بالمصفوفة وحدها تبقيها متمايزة. وحين تربط مخزن بياناتك الخاص، ابدأ من Default(THPDFXFDFArrayAccess). أعِد True من GetFormFieldValueArray لأي حقل ذي قائمة، بمن في ذلك ما لا شيء مختار فيه، و False للعودة إلى النداء العددي
uses HPDFXFDF;
// مؤشر دالة عادي، ليس "of object": يحمل Context مخزنك الخاص
function StoreGetSelections(Context: Pointer; FieldIndex: Integer;
out Values: THPDFXFDFValueArray): Boolean;
begin
Result := TFormStore(Context).IsListField(FieldIndex);
if Result then
Values := TFormStore(Context).Selections(FieldIndex);
end;
procedure ExportStore(Store: TFormStore; out Bytes: TBytes);
var
Access: THPDFXFDFAccess;
ArrayAccess: THPDFXFDFArrayAccess;
begin
Access := MakeStoreAccess(Store); // ارتباطاتك العددية القائمة
ArrayAccess := Default(THPDFXFDFArrayAccess); // كل خانة غير مستخدمة nil
ArrayAccess.GetFormFieldValueArray := StoreGetSelections;
HPDFXFDFExportFields(Access, ArrayAccess, Bytes);
end;
تبادل متعدد التحديد يعمل على صناديق قوائم موجودة أصلاً وعليها بت MultiSelect في /Ff. لكيفية إنشاء حقول الاختيار وبتات أعلامها في المقام الأول، انظر إضافة ListBox وحقول AcroForm أخرى إلى PDF محمّل. وللتعليقات التوضيحية التي تمر عبر شجرة <annots> في XFDF، انظر استيراد وتصدير تعليقات XFDF التوضيحية في HotPDF. والمرجع الكامل للواجهة وتنزيل النسخة التجريبية على صفحة مكون PDF من HotPDF لـ Delphi