مقال تقني

الفحص المبدئي (Preflight) لـ PDF/A و PDF/UA في Delphi باستخدام PDFlibPas

يجيب كل من PDF/A و PDF/UA على سؤالين لا علاقة لهما ببعضهما البعض، ومعاملتهما كخانة اختيار واحدة لإمكانية الوصول والأرشفة هو كيف تصل الملفات المعطلة إلى أرشيف يرتدي علامة امتثال. يسأل PDF/A عما إذا كان سيستمر عرض الملف بأمانة في غضون عشرين عامًا. يسأل PDF/UA عما إذا كانت التكنولوجيا المساعدة يمكنها قراءته اليوم. يمكن أن يمر مستند بأحدهما ببرود ويفشل في الآخر، لذلك فإن الحكم الصادق الوحيد يأتي من تشغيلهما معًا، ومن تشغيلهما قبل كتابة الملف، وليس بعد أن يثق نظام المصب في معرف المطابقة المخبوز في بياناته الوصفية. هذا المعرف هو إعلان ذاتي. لا يوجد شيء في التنسيق يتطلب أن يكون صحيحًا، والتطبيق الذي يكتب "PDF/A-1b" في XMP دون التحقق من صحته مقابل المعيار ينتج ملفًا يبدو متوافقًا مع كل مستهلك يقرأ الملصق فقط. تسد losLab PDF Library (PDFlibPas) هذه الفجوة لـ Delphi و C++Builder من خلال بناء كلا المدققين في المكتبة، وبالتالي يتم تشغيل الفحص داخل العملية (in-process) مع عدم وجود خدمة خارجية للوقوف عليها

معياران يفشلان الملفات لأسباب متعاكسة

ISO 19005 (PDF/A) هو عقد استنساخ. يجب أن يُعرض الملف المطابق بشكل متطابق بعد عقود من الآن على برامج لم ترَ النظام الذي أنتجه أبدًا، لذلك تهاجم القواعد التبعيات الخارجية: يجب تضمين كل خط، وتثبيت اللون بـ ICC OutputIntent مضمن أو التعبير عنه في مساحة مستقلة عن الجهاز، ولا يوجد تشفير في PDF/A-1، ولا يوجد JavaScript، وبيانات وصفية XMP تتفق مع قاموس معلومات المستند. بدلاً من ذلك، فإن ISO 14289 (PDF/UA) هو عقد دلالات. يتعين على التكنولوجيا المساعدة اجتياز المستند والخروج بالمعنى، الذي يعيش في طبقة مختلفة تمامًا: شجرة هيكل كاملة، ونص بديل على الأشكال، وعنوان مستند تم تعيينه للعرض، ومستويات عناوين لا تتخطى، وعلاقات رؤوس الجداول التي تستمر بمجرد خروج الصفحة عن الشاشة

لأن المعيارين يراقبان طبقات مختلفة، فإن الملفات التي تعضك هي تلك التي تقع بينهما. يمكن أن يكون المستند المثالي للأرشيف صامتًا بالنسبة لقارئ الشاشة. يمكن لواحد ذو علامات (tagged) جميلة أن يشير إلى خط سطح مكتب لن يكون موجودًا في غضون عشر سنوات. النشر في القطاع العام هو المكان المعتاد الذي يهبط فيه كلا المطلبين في وقت واحد، ولا يمكن لخط أنابيب هناك أن ينهار في بوابة واحدة. تذهب النتائج لأشخاص مختلفين. الخطوط غير المضمنة هي عيب في التعليمات البرمجية التي تُنشئ PDF، بينما ينتمي النص البديل المفقود إلى من يمتلك قوالب المحتوى، ويتم توجيه تقرير يمزج بين الاثنين مرتين فقط

أي جزء من PDF/A تستهدفه يهم بقدر ما إذا كنت قد وصلت إليه. تم تجميد PDF/A-1 على PDF 1.4 ويرفض الشفافية و JPEG2000، وكلاهما يصل إليه مخرج التقارير الحديث دون تفكير. يقبل PDF/A-2 (ISO 19005-2، مبني على ISO 32000-1) كليهما وهو الافتراضي المعقول لأرشيف جديد. يذهب PDF/A-3 إلى أبعد من ذلك ويسمح بالملفات المضمنة من أي نوع، وهو ما تعتمد عليه تنسيقات الفوترة الإلكترونية المنظمة. عادة ما يحمل الفريق الذي لا يزال يقوم بتوحيد PDF/A-1b في عام 2026 متطلبًا كتبه شخص ما قبل خمسة عشر عامًا، وإعادة التفاوض على الجزء المستهدف غالبًا ما يكون أرخص من إزالة الشفافية من كل مخطط يصدره النظام

نتائج مهيكلة في وقت الاستيعاب

نقطة دخول API المسطحة هي CheckFileCompliance، مع محدد الاختبار 1 لـ PDF/A و 2 لـ PDF/UA. يقوم بإرجاع مقبض قائمة سلسلة (string-list handle) عناصره عبارة عن نتائج فردية، واحدة في كل سطر، وهو بالضبط الشكل الذي تريد البوابة الآلية أن تمشي فيه:

function GateArchiveUpload(Pdf: TPDFlib; const FileName: string): Boolean;
var
  ListId, I: Integer;
begin
  ListId := Pdf.CheckFileCompliance(FileName, '', 1, 0);  // 1 = PDF/A
  if ListId = 0 then
  begin
    // 0 means "no findings" OR "file unreadable" -- disambiguate before passing
    Result := Pdf.LastErrorCode = 0;
    Exit;
  end;
  for I := 0 to Pdf.GetStringListCount(ListId) - 1 do
    LogFinding(FileName, Pdf.GetStringListItem(ListId, I));
  Pdf.ReleaseStringList(ListId);
  Result := False;
end;

يقرر تفصيلان ما إذا كان هذا يعمل دون مراقبة. الأول هو قيمة مرجعة تعني شيئين متعارضين. يُرجع CheckFileCompliance 0 عندما يكون الملف متوافقًا تمامًا وأيضًا عندما لا يمكن فتح الملف على الإطلاق، لأنه داخليًا تنهار قائمة نتائج فارغة إلى 0 في كلتا الحالتين. ستوجه البوابة التي تقرأ 0 كنجاح تحميلات الفاسدة مباشرة إلى الأرشيف، لذلك قم بإزالة الغموض باستخدام LastErrorCode قبل أن تثق بالصفر، كما تفعل البوابة أعلاه. والثاني يتعلق بمكان الملف في دورة حياته. يعمل المدقق على قارئ تدفق المكتبة بدلاً من نموذج المستند الكامل، ويفتح الملف مباشرة بمشاركة القراءة ولا يستدعي LoadFromFile أبدًا، وهذا هو السبب في أنه يمكن أن يمضغ من خلال إدخال متعدد الجيجابايت دون بناء شجرة كائن. يفشل نفس التدفق المفتوح بينما لا تزال عملية أخرى تحتفظ بالملف للكتابة، والتحميل قيد التقدم هو تلك الحالة تحديدًا. البوابة بعد انتهاء النقل

يؤتي تصميم التدفق ثماره مرة أخرى تحت الحمل. يفتح كل فحص إدخاله للقراءة فقط ويشاركه للقراءة، لذلك يتوسع تدقيق المجموعة (corpus audit) عبر خيوط أو عمليات العمال (worker threads or processes) مع مثيل TPDFlib واحد لكل عامل ولا يوجد تنازع بينهم. المورد الذي يحتاج إلى الانضباط هو المقبض نفسه. تبقى كل نتيجة غير صفرية من CheckFileCompliance مخصصة حتى تستدعي ReleaseStringList، والبوابة طويلة الأمد التي تنسى تحريرها لا تتعطل، بل تنزف الذاكرة ببطء حتى يذهب شخص ما للبحث عن السبب

تقارير للبشر، مقارنات (diffs) لبوابات البناء

قائمة النتائج هي الشكل الصحيح للبوابة والشكل الخاطئ لرسالة بريد إلكتروني لفريق النموذج. يعرض CreatePreflightReport نفس التحليل كنثر يمكن قراءته، ويضيف CreatePreflightReportEx محدد تنسيق التقرير، ويكتبه SavePreflightReport على القرص حتى يتمكن التقرير من السفر داخل حزمة المستند المسلمة. تجعل الكثير من عقود الأرشيف هذا التقرير قابلاً للتسليم في حد ذاته، وليس مجرد قطعة أثرية داخلية

العضو في هذه العائلة الذي يكتسب مكانه بهدوء هو ComparePreflightReports. الامتثال هو سطح تراجع (regression surface) مثل أي جزء آخر من السلوك. يمكن أن يؤدي تعديل قالب، أو خط شركة مرخص حديثًا، أو ترقية مكتبة إلى إدخال نتيجة لم تكن موجودة في الإصدار الماضي، ولا يعلن أي منها عن نفسه. احتفظ بتقارير ذهبية لمجموعة من الوثائق التمثيلية تحت التحكم في الإصدار، وقم بتجديدها بعد كل تغيير، وقم بتشغيل ComparePreflightReports لحساب الدلتا. مقارنة فارغة هي قطعة أثرية تستحق الاحتفاظ بها. النتيجة المفاجئة تفشل البناء، وهو مكان أرخص بكثير لاكتشافه من التدقيق

توليد المخرجات التي تمر في الجولة الأولى

يكتسب الفحص المبدئي (Preflight) مكانته على الملفات الواردة من مكان آخر. بالنسبة للمستندات التي ينتجها الكود الخاص بك، فإن العثور على الانتهاكات بعد الإنشاء وتصحيحها مرة أخرى هو طريق بطيء. يحمل PDFlibPas وضعًا على جانب الإنشاء لكل معيار، ويمكنك تشغيل كليهما في نفس المستند:

var
  Pdf: TPDFlib;
  Diag: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.NewDocument;
    Pdf.SetPDFAMode(1);
    Pdf.LoadOutputIntentProfile('sRGB-IEC61966-2.1.icc', 'RGB');
    Pdf.SetPDFUAMode('en-US');
    Pdf.SetInformation(1, 'Quarterly Statement');  // /Title: required for PDF/UA
    // ... draw tagged content here ...
    Diag := Pdf.GetPDFUADiagnostics;
    if Diag <> '' then
      Writeln('fix before shipping: ', Diag);
    Pdf.SaveToFile('statement.pdf');
    // the preflight that counts runs on the saved file:
    Writeln(Pdf.CreatePreflightReport('statement.pdf', '', 1, 0));
  finally
    Pdf.Free;
  end;
end;

يختبئ الفخ في وقت الحفظ. تحدث العديد من إصلاحات المطابقة أثناء تسلسل المستند بدلاً من تمكين الوضع: فرض علامة الطباعة على التعليقات التوضيحية، وكتابة AFRelationship الافتراضي للملفات المضمنة في PDF/A-3، وتطبيع ترتيب الجدولة وأوصاف حقول النماذج لـ PDF/UA. المستند الموجود في الذاكرة ليس متطابقًا بالبايت مع المستند الذي يهبط على القرص، لذا فإن حكم الفحص المبدئي الوحيد الذي يعني أي شيء هو الحكم المحسوب من الملف المحفوظ. تحقق من صحة statement.pdf نفسه. لا تستنتج الامتثال من الكائن الذي لا يزال في الذاكرة، لأن البايتات التي ستحكم عليها ليست البايتات التي شحنتها

تتبع سيناريوهات الفاتورة التي تحمل XML قابلاً للقراءة آليًا جنبًا إلى جنب مع المستند المرئي نمط ZUGFeRD و Factur-X، والذي تم بناؤه على PDF/A-3. يجب أن تحدد علاقة المرفق بشكل صريح باستخدام SetPDFA3DefaultAFRelationship، حيث يتطلب ISO 19005-3 من كل ملف مضمن الإعلان عن دوره بالنسبة للمستند. اتركه غير محدد وسيكون XML المضمن مجرد كتلة كبيرة (blob) بدون غرض معلن، وهو ما يلاحظه المدقق

الحكام المستقلون: veraPDF و Acrobat

يجب ألا يكون المنتج هو القاضي الوحيد لإنتاجه الخاص. تمنحك فاحصات PDFlibPas أحكامًا سريعة ومهيكلة داخل العملية، وهو ما تريده في المسار السريع (hot path)، ولكن يجب أن تمر بوابة الإصدار لدفعة أرشيفية عبر مدقق لم يكتبه أحد في فريقك. veraPDF هو التطبيق المرجعي الذي تتم صيانته من قبل المجتمع لـ PDF/A والأداة التي تسميها معظم الأرشيفات في معايير القبول الخاصة بها، لذا فهي الأداة التي يجب مطابقتها. تجعل ملفات تعريف الفحص المبدئي (preflight) لـ Acrobat قاطع تعادل مفيد عندما يختلف veraPDF والتحقق داخل العملية. سجل اسم المدقق وإصداره بجوار كل تقرير مخزن. الادعاء بأن الملف قد مر بـ veraPDF يقول القليل جدًا بدون رقم البناء الذي مر به، حيث تشدد الأداة قواعدها بين الإصدارات

المدققون يختلفون في حواف المعايير، وعندما يفعلون ذلك فإن الإجابة ليست اختيار الأداة التي تعجبك. قم بتقليص الملف إلى عينة دنيا لا تزال تثير الخلاف واقرأها مقابل النص القياسي. ساعة من ذلك تظهر عادةً أحد شيئين: خطأ حقيقي في أداة يستحق التقديم في المنبع (upstream)، أو فقرة كان فريقك يقرؤها بشكل خاطئ ويجب أن يكتبها في ملاحظات الامتثال حتى لا يعيد الشخص التالي التقاضي بشأنها

يحصل الإدخال المشفر على اختصار. يأخذ كلا المدققين وسيطة كلمة مرور، لكن ملف PDF/A-1 الذي يحتوي على قاموس تشفير غير متوافق بالفعل، لأن ISO 19005-1 يحظر التشفير صراحةً، لذلك يمكن إبعاد تقديم مشفر قبل إجراء أي تحليل أعمق. معرفة ما يمنحه قاموس التشفير بالفعل هو مهمة بحد ذاتها، تمت تغطيتها في تشفير PDF وتدقيق الأذونات

تعود نتائج PDF/UA دائمًا تقريبًا إلى كيفية تأليف شجرة الهيكل في المقام الأول، وتعيش تقنيات وضع العلامات وراء ذلك في بناء أشجار هيكل PDF الموسومة في Delphi. الأرشيفات التي تتطلب أيضًا توقيعات رقمية يجب أن تقرن هذه البوابة بسير العمل في توقيع PAdES والتحقق منه. يتوفر مرجع واجهة برمجة تطبيقات الفحص المبدئي الكامل على صفحة منتج losLab PDF Library for Delphi