يربط PDFlibPas ملفًا مضمَّنًا بصفحة محددة بعينها بدل الوثيقة ككل، بكتابة مصفوفة /AF في قاموس الصفحة بينما تبقى الحمولة نفسها مسجلة في شجرة الأسماء EmbeddedFiles الخاصة بالوثيقة. وهذا الانفصال بالضبط هو ما تصفه ISO 32000-2 §14.13، وهو ما يمكّن القارئ من الإجابة عن السؤال الذي يعجز عنه مرفق المستوى الوثائقي: لأي صفحة تنتمي هذه البيانات
حالات الاستخدام أكثر تحديدًا من المرفقات العامة. تقرير مسح تحمل كل صفحة فيه سلسلة القياسات الخام التي يقف خلف رسمها البياني. دفعة ممسوحة ضوئيًا تحتفظ كل صفحة فيها بنتيجة OCR التي أنتجت طبقة النص الخاصة بها. حزمة مخططات هندسية تحمل كل ورقة فيها المستخرج CAD الذي رُسم منه. وفي كل حالة ستكون قائمة مرفقات المستوى الوثائقي كومة ملفات تحمل أسماء ترمّز أرقام الصفحات، وهذا اصطلاح لا بنية
حمولة واحدة وموضعان يُشار إليها منهما
والنقطة البنيوية المهمة أن الارتباط على مستوى الصفحة لا ينشئ نسخة ثانية من أي شيء. فالملف يُضمَّن مرة واحدة ويسجل في شجرة الأسماء EmbeddedFiles تمامًا كما يفعل مرفق المستوى الوثائقي، وبآلية مواصفات الملفات نفسها. والاختلاف في مكان كتابة المرجع ومفتاح العلاقة الخاص به: في قاموس الصفحة بدل كتالوج الوثيقة
ويلت ذلك نتيجتان. الأولى أن القارئ الذي لا يعرف سوى مرفقات المستوى الوثائقي يجد الحمولة رغم ذلك، لأنها في شجرة الأسماء حيث يبحث هذا القارئ. والثانية أن مسح الارتباط على مستوى الصفحة يزيل الربط لا الملف. فـ ClearPageAssociatedFiles تفصل الصفحة عن ملفاتها المرافقة وتبقي الحمولات متاحة عبر شجرة الأسماء، وهذا هو السلوك التحفظي: العملية التي تقول «امسح الارتباط» لا ينبغي أن تدمر بصمت بيانات قد تشير إليها أجزاء أخرى من الوثيقة
ولهذه الدالة شرط نجاح ضيق متعمد يستحق المعرفة. فهي تبلّغ عن النجاح فقط إذا كان قاموس الصفحة يحمل مفتاح /AF فعلًا. وصفحة لم يكن لها ارتباطات قط تعيد فشلًا لا تأكيدًا مطمئنًا، فيستحيل على المستدعي أن يخلط بين عملية لا تفعل شيئًا وتنظيف مكتمل
var
Lib: TPDFlib;
Idx, I: Integer;
begin
Lib := TPDFlib.Create(nil);
try
Lib.LoadFromFile('survey-report.pdf');
// أرفق سلسلة القياسات التي أنتجت الرسم البياني في الصفحة 3
Idx := Lib.AddPageAssociatedFileFromFile(3,
'series-03.csv', // ملف على القرص
'measurements.csv', // اسم العرض داخل PDF
'text/csv', // نوع MIME
'Raw measurement series for figure 3',
'Data'); // AFRelationship، ISO 32000-2 14.13
if Idx < 0 then
raise Exception.Create('page association refused');
for I := 0 to Lib.GetPageAssociatedFileCount(3) - 1 do
Writeln('page 3 associated file, embedded index ',
Lib.GetPageAssociatedFileEmbeddedIndex(3, I));
Lib.SaveToFile('survey-report-with-data.pdf');
finally
Lib.Free;
end;
end;
وسلسلة العلاقة ليست نصًا حرًا في الممارسة. فـ ISO 32000-2 تعرّف مفردات: Source وData وAlternative وSupplement وEncryptedPayload وFormData وSchema وUnspecified، والمستهلكون يعتمدون عليها. فـ Data للأعداد التي يقف خلفها رسم بياني، وSource للوثيقة التي وُلّدت منها الصفحة، وAlternative لتمثيل مكافئ. اختر من المفردات حتى لو لم يقرأها شيء في خط معالجتك بعد، لأن الأداة التالية في السلسلة قد تقرأها
لماذا يحتاج البحث نفسه إلى FollowRef في الاتجاهين
لأن تتبّع المراجع يجيب عن سؤالين مختلفين، وعلى الكود أن يعرف أيهما يطرح. فالبحث عن مفتاح يتبع المراجع غير المباشرة يعيد الكائن الذي يشير إليه المرجع، والبحث الذي لا يتبع يعيد المرجع نفسه. وكلاهما صحيح، واستخدام الخطأ منهما ينتج سلوكًا خاطئًا صامتًا لا خطأ
وقراءة ملف مرافق توضح الاتجاه الأول. للحصول على رقم الكائن للتدفق المضمَّن وراء مواصفة الملف بمفتاحَي /EF و/F، على البحث أن لا يتبع، لأن التتبع يحل المرجع إلى كائن التدفق فيضيع رقم الكائن. والقاعدة تتعمم: أي مسار كود يحتاج هوية كائن لا محتوى كائن عليه أن يأخذ المرجع الخام
والمحتوى الاختياري يوضح الاتجاه المعاكس، وقد كلّف اكتشافه جهدًا أكبر. فقاموس خصائص المحتوى الاختياري يُكتب في الكتالوج ككائن غير مباشر، فالكود الذي يقرؤه دون تتبع يحصل على مرجع لا قاموس. وفحص النوع على تلك القيمة يفشل بعدها، ويتفرع المسار الاحتياطي الطبيعي — إن لم توجد تهيئة فأنشئ واحدة — فيستبدل التهيئة التي كانت هناك أصلًا. ولا شيء يرفع استثناء. فالطبقات التي تصفها مقالة مجموعات المحتوى الاختياري والطبقات تفقد ببساطة حالة الرؤية الافتراضية لها
والعبرة تتعمم أبعد من الحالتين. حين يستطيع البحث أن يعيد مرجعًا أو الكائن نفسه، ففحص النوع المجرد ليس معالجة أخطاء: إنه تفرع سؤُوب اتُّخذ في النهاية لسبب خاطئ. حدد صراحة ما يحتاجه كل موقع استدعاء، وفضّل الواجهة العامة التي تجيب عن السؤال مباشرة، كخاصية عدّ للمحتوى الاختياري، على التسلل إلى مُتّصِل محمي للوصول إلى قاموس الكتالوج
// مرفقات المستوى الوثائقي وارتباطات المستوى الصفحي تتعايشان،
// ويمكن وسم الملف المضمَّن كمرتبط على مستوى الوثيقة أيضًا
if Lib.IsEmbeddedFileAssociated(0) = 0 then
Lib.SetEmbeddedFileAssociated(0, 1, 'Supplement');
Writeln('document associated files: ', Lib.GetAssociatedFileCount);
Writeln('page 3 associated files : ',
Lib.GetPageAssociatedFileCount(3));
// الإزالة تفك ارتباط الصفحة؛ تبقى الحمولات متاحة في شجرة الأسماء
if Lib.ClearPageAssociatedFiles(3) > 0 then
Writeln('page 3 associations removed, payloads still reachable');
ماذا تفعل أنماط المطابقة بالمرفقات
تقيد مواصفات الأرشفة ما يجوز تضمينه، ويُفرض القيد عند نقطة الدخول لا وقت الحفظ. فـ PDF/A-1 يحظر الملفات المضمَّنة كليًا، وPDF/A-2 يسمح بوثائق PDF/A مضمَّنة فقط، وPDF/A-3 هو الملف الذي فتح التضمين لأنواع ملفات اعتباطية، وهذا بالضبط سبب بناء صيغ الفواتير الهجينة عليه
ترفض PDFlibPas الإرفاق حين لا يسمح به نمط المطابقة النشط، عند الاستدعاء، لا بعد مئات العمليات أثناء الإخراج. وهذا اختيار متعمد لموضع أرخص ما تكون معالجة الخطأ عنده: الرفض عند موقع الاستدعاء يسمّي الملف الذي كنت تضيفه، بينما الرفض وقت الحفظ يسمّي وثيقة ويترك لك أن تعرف أي مرفقات من أربعين تسببت في ذلك
ولهذا أيضًا تظهر الملفات المرافقة كثيرًا في الفوترة الإلكترونية. فالفاتورة الهجينة PDF يقرؤه إنسان مع حمولة XML قابلة للقراءة آليًا مرفقة وموسومة بالعلاقة الصحيحة، وكل من ملف الحاوية ومفتاح العلاقة جزء من المواصفة لا اصطلاحان. وهذه البنية مغطاة في بناء فواتير Factur-X وZUGFeRD الهجينة، مع جانب البيانات الوصفية في مخطط امتداد XMP لـ PDF/A-3
متى تكون الارتباطات على مستوى الصفحة لا الوثيقة
متى احتاج مستهلكٌ أن يعرف لأي صفحة تنتمي البيانات، وحينئذ فقط. فمرفقات المستوى الوثائقي أبسط، وأوسع دعمًا بين العارضات، وكافية كلما وصفت الحمولة الوثيقة كلها: XML فاتورة، أو بيان توقيع، أو أرشيف مصادر. واتجه إلى الارتباط على مستوى الصفحة حين تكون الحمولة محصورة بصفحة فعلًا وهوية الصفحة جزء من معناها
والدعم هو القيد العملي. فالملفات المرافقة على مستوى الصفحة بنية من PDF 2.0، ودعم العارضات لها أرفع منه لمرفقات المستوى الوثائقي. ولأن الحمولة في شجرة الأسماء على كل حال، فالعارضة التي تتجاهل /AF على الصفحات تعرض الملف في قائمة مرفقاتها رغم ذلك، فالتدهور لطيف. لكن إذا كان ربط الصفحة جوهريًا لمستهلكك لا مجرد بيانات وصفية مفيدة، فتحقق من القارئ الذي تستهدفه فعلًا ولا تفترض
والملفات المرافقة على مستوى الصفحة ومرفقات المستوى الوثائقي وبوابات مواصفات الأرشفة التي تحكم الاثنين ترد مع مكتبة PDFlibPas Delphi PDF. وإذا كنت أيضًا تصلح ملفات أقدم أثناء إدخالها، فعمل البيانات الوصفية والمطابقة في التحويل إلى PDF/A مع إصلاح البيانات الوصفية هو ما يقرر أي مسارات الإرفاق هذه متاحة لك من الأساس