مقال تقني

إخراج PDF 2.0 وPDF/A-4 وPDF/UA-2 مع HotPDF في Delphi

يكتب HotPDF مستندات PDF 2.0 أصلية من Delphi وC++Builder، بما في ذلك ملفات PDF/A-4 الأرشيفية الثلاث وإخراج PDF/UA-2 القابل للوصول بعناصر بنية ذات فضاءات أسماء. اختيارها مسألة خاصيتين، لكن المعايير وراء تلك الخصائص تغيّرت أكثر مما يوحي رقم الإصدار: أزال PDF/A-4 حروف المطابقة التي تعلّمها الجميع مع PDF/A-2، وأدخل PDF/UA-2 فضاءات أسماء للبنية لم يحملها مستند الجزء 1 أبدًا

يغطي هذا المقال ما الذي يتغير فعلًا في الملف المُولَّد، وأي الأخطاء يحوّلها HotPDF إلى استثناء عند EndDoc بدلًا من مستند يفشل في التحقق عند موقع العميل

كيف يختلف تعرّف PDF/A-4 عن الجزأين 2 و3

يتعرّف PDF/A-4 على نفسه برقم الجزء وسنة المراجعة، بلا حرف مطابقة للجزء الأساسي. اضبط PDFACompliance على '4' فيُصدِر HotPDF pdfaid:part=4 مع pdfaid:rev=2020 وبلا مدخل pdfaid:conformance إطلاقًا. الحرف لم يُفقَد — الجزء 4 لا مستويات A/B/U له، لأن المتطلبات التي كانت تفصل بينها دُمجَت في الجزء الأساسي

يحتفظ امتدادان بحرف. يختار '4E' PDF/A-4e للمستندات الهندسية ويُصدِر مطابقة E، التي تسمح بمساري تعليقات ثلاثية الأبعاد وRichMedia التي تحظرها الملفات الأخرى. يختار '4F' PDF/A-4f ويُصدِر مطابقة F، التي تسمح بملف مضمَّن بأي صيغة. الثلاثة جميعها تفرض ترويسة PDF 2.0، وتتطلب فحوص نية الإخراج والبيانات الوصفية المعتادة لـ PDF/A، وتحظر التشفير — ملف أرشيفي مشفَّر تناقض لا يقبل المعيار

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-archive.pdf';
    Pdf.PDFACompliance := '4F';   // PDF/A-4f: associated files of any format
    Pdf.BeginDoc;
    Pdf.CurrentPage.SetFont('Arial', [], 11);
    Pdf.CurrentPage.TextOut(50, 760, 0, 'Invoice 2026-0731');
    Pdf.AddPDFAssociatedFile('invoice.xml', 'text/xml',
      'Structured invoice data', 'Data', LoadInvoiceBytes);
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

يضمّن AddPDFAssociatedFile الملف، ويبني FileSpec خاصته بـ /AFRelationship، ويسجّله في مصفوفة /AF للفهرس وفي شجرة أسماء EmbeddedFiles. كلا التسجيلين مطلوب؛ ملف مُدرَج في واحد منهما فقط هو السبب الأكثر شيوعًا لاجتياز فاتورة هجينة فحص عين سريع وإخفاقه أمام مُحقِّق حقيقي. تقبل سلسلة العلاقة Source أو Data أو Alternative أو Supplement أو Unspecified، ويجب أن يكون الملف النشط PDF/A-3 أو PDF/A-4e أو PDF/A-4f — ملف الجزء 4 الأساسي لا يقبل الملفات المرتبطة. الاسم الأقدم AddPDFA3AssociatedFile لا يزال يعمل للكود القائم

ما الذي يطلبه PDF/UA-2 ولم يطلبه PDF/UA-1

يفرض PDF/UA-2 PDF 2.0 ويُصدِر pdfuaid:part=2 مع pdfuaid:rev=2024، ويُدخِل فضاءات الأسماء إلى شجرة البنية. كان لمستند الجزء 1 مفردات مسطحة واحدة من الأدوار القياسية. يستطيع مستند الجزء 2 حمل أدوار مخصصة طالما ينتمي كل واحد إلى فضاء أسماء مُصرَّح به، وهذا ما يجعل الوسم الخاص بالمجال مقروءًا للتقنيات المساعدة بدلًا من تخمين

تطبّق هذا طريقتان. يُنشئ RegisterStructureNamespace أو يعيد استخدام قاموس /Type /Namespace غير مباشر ويُدرجه في StructTreeRoot /Namespaces، ويُعيد القاموس لتعيد استخدامه. يُنشئ AddStructureElementNS عنصر بنية يشير مدخل /NS الخاص به إلى ذلك القاموس، وهذا ما يرخّص اسم دور خارج المجموعة القياسية. الاستدعاءات المتكررة بنفس URI تعيد استخدام قاموس واحد بدلًا من تكديس المكررات

var
  Root: THPDFDictionaryObject;
begin
  Pdf.PDFUACompliance := True;
  Pdf.PDFUAPart := 2;             // part 2 forces PDF 2.0
  Pdf.Lang := 'en-US';
  Pdf.BeginDoc;
  Root := Pdf.AddStructureElement('Document', nil);
  Pdf.AddStructureElementNS('WidgetGroup',
    'https://example.com/ns/widgets', Root);
  Pdf.EndDoc;
end;

Lang ليس زخرفة هنا. مستند موسوم بلا لغة طبيعية مُصرَّح بها يترك قارئ الشاشة يخمّن النطق، وتعامل PDF/UA الإغفال كعيب لا كتفضيل

أي أخطاء بنية يلتقطها EndDoc؟

أربعة، وكل واحد يقابل مستندًا كان سيصل إلى مُحقِّق مكسور وإلّا. يجب أن يحتوي جذر البنية على عنصر Document واحد ذو مستوى أعلى بالضبط. كل قاموس فضاء أسماء يجب أن يكون غير مباشر، وذا نوع Namespace، ويحمل URI فريدًا غير فارغ. كل مرجع /NS لعنصر بنية يجب أن يحلّ إلى قاموس مُدرَج فعلًا في مصفوفة /Namespaces للجذر. والدور بلا فضاء أسماء يجب أن يكون دورًا قياسيًا من PDF 2.0 أو يحلّ عبر RoleMap

تُطلَق هذه عند EndDoc لأنه آخر لحظة توجد فيها الشجرة كاملة في الذاكرة وأول لحظة تكمل فيها. التقاطها مبكرًا كان سيعني رفض حالات وسيطة صالحة؛ التقاطها لاحقًا كان سيعني عدم التقاطها إطلاقًا. النتيجة العملية لكودك هي أن عيب بنية يظهر عند نهاية التوليد برسالة تسمّي المشكلة، بدلًا من الظهور بعد أسابيع كتقرير veraPDF يحوّله أحدهم من عميل

أدوار PDF 2.0 التي تستحق المعرفة

يكتسب enum الأدوار المُنمَّط DocumentFragment وAside وTitle وFENote وSub وEm وStrong وArtifact. ثلاثة منها تغيّر كيف تُوسِم مستندات العمل المعتادة. Aside أخيرًا يمنح الأشرطة الجانبية والاقتباسات الجانبية موطنًا ليس Sect مُساء الاستخدام. يعلّم FENote الحاشيات والتعليقات الختامية كما هي، فيستطيع القارئ عرضها بدلًا من تسطيرها مع المتن. Em وStrong يستبدلان تخمين الدلالة الذي جاء من وسم التوكيد كتنسيق على مستوى span

يقبل التحميل الزائد بالنص الإضافي صيغة Hn المفتوحة، بما فيها H7 وما بعد. توقف PDF 1.7 عند H6، ما اضطر المستندات التقنية العميقة إلى تسطيح مخططها أو إعادة استخدام المستويات. إن تُولِّد مستندات معايير أو قوانين قانونية أو فهارس قطع غيار، فهذا وحده قد يكون سبب نقل الإخراج إلى PDF 2.0

ماذا تتحقق قبل تبديل إخراج الإنتاج

PDF 2.0 تغيير ترويسة بذيل طويل. أدوات استيعاب الأرشيف الأقدم، وبعض RIPs الطباعة، وعدد مفاجئ من عارضات خطوط الأعمال تقبل حتى PDF 1.7 فقط، وتفشل على الترويسة لا على شيء فعلته خاطئًا. قبل التبديل، أكِّد الأنظمة المستهلكة، وتذكَّر أن اختيار ملف PDF/A-4 يختار PDF 2.0 سواء طلبت ذلك أم لا

تسلسل آمن هو الإبقاء على PDF/A-3 للمستندات التي تذهب إلى قراء مجهولين، واستخدام PDF/A-4f للأرشيفات الداخلية حيث تتحكم في الاستيعاب، وتبنّي PDF/UA-2 فقط حيث تسمّيه سياسة الوصول. إن كنت تعمل الجانب الأرشيفي أولًا، فأدلة تحقق PDF/A وPDF/X وPDF/UA وفواتير ZUGFeRD وFactur-X الهجينة على PDF/A-3 تغطي اختيارات الملف التي تهم قبل رقم الإصدار، وتُري ملاحظات تقارير الفحص المسبق المؤتمتة كيف تجعل الحُكم جزءًا من بنائك بدلًا من خطوة يدوية

يُشحن HotPDF سطح تأليف PDF 2.0 كامله ككود VCL أصلي من أجل Delphi وC++Builder، فلا يحتاج إخراج PDF/A-4 وPDF/UA-2 إلى محرك خارجي أو مُ قابل لإعادة التوزيع — صفحة مكوّن HotPDF تسرد الملفات المدعومة وإصدارات RAD Studio