كان مكتب مسح ضوئي استشرت له يشغّل job ليلية تختم آلاف السجلات المرقمنة بعبارة "جاهزة للأرشفة". بعد ستة أشهر، أخذ مدقق خارجي عينة من الأرشيف باستخدام veraPDF ووجد مخالفات PDF/A في ملفات كان job قد مررها. لم يكن job يكذب تماماً؛ لقد فحص profile الخطأ، ودمج كل نتيجة في bit واحد pass/fail، وتخلص من ملفات التقرير التي كانت ستكشف التباين فوراً. تذكر هذه الحادثة عندما توصل محرك preflight في PDFium Component، وهي مكتبة PDF ذات source code لـ Delphi وC++Builder وLazarus، بأداة command-line: استدعاءات validation هي الجزء السهل، والعقد حولها، من profiles وexit codes وreport retention، هو حيث ينجح batch preflight أو يتعفن بصمت.
العقد: ما الذي يستطيع scheduler رؤيته فعلاً
يرى CI runner أو Windows Task Scheduler شيئين فقط من أداتك: exit code والملفات التي تركتها وراءها. كل ما عدا ذلك، من log lines وألوان console وprogress output، مخصص للبشر الذين يشاهدون مباشرة. لذلك قبل لمس API، ثبّت مفردات exit-code واجعلها مملة:
0— كل ملف طابق كل profile مطلوب1— ملف واحد على الأقل أنتج validation findings2— الأداة نفسها فشلت على ملف واحد على الأقل، مثل input تالف أو lock أو crash
التمييز بين الكودين 1 و2 هو ما تتخطاه الفرق ثم تندم. PDF تالف لا يمكن فتحه ليس فشل validation، ومعاملته كواحد تعني أن شحنة كاملة من scans التالفة تظهر في metrics كانهيار مطابقة مفاجئ بدلاً من incident تشغيلي حقيقي.
يستحق عنصران آخران في العقد علماً لكل منهما: timeout لكل ملف ودليل quarantine. PDF مرضي، آلاف الصفحات وبنى كائنات متداخلة بعمق، قد يحبس validation pass لدقائق، والنافذة الليلية لا تهتم بالسبب. اقطع job الملف عند deadline، واحسبه tool failure، وانقل input جانباً للفحص، وأبقِ batch متحركاً. ثم يصبح دليل quarantine corpus ذاتي التجميع لأسوأ المستندات التي يرسلها العملاء فعلاً، وهذا أثمن لاختبارات الإصدار من أي عينة اصطناعية.
اختيار المعايير: PDF/A-2b ليس PDF/A-3a
يختار تعداد TPdfPreflightStandard عائلات المعايير المهمة عملياً: ppsPdfA لمطابقة الأرشفة ISO 19005، وppsPdfUa للوصولية ISO 14289، وppsPdfX لتبادل الطباعة، إضافة إلى ppsPdfE وppsPdfR وppsPdfVT لسير عمل الهندسة وraster وvariable-data. داخل كل عائلة، يكشف المحرك مستوى المطابقة الذي يدعيه المستند ويبلّغ عنه لكل معيار في ConformanceName في النتيجة، والمستوى مهم. يقرر PDF/A-2b قابلية إعادة الإنتاج البصرية فقط؛ أما PDF/A-3a فيطلب أيضاً logical structure tagging ويسمح بملفات مصدر مضمّنة، وهو سقف أشد وأصعب بكثير على المواد الممسوحة. إذا كانت سياسة الاحتفاظ لديك تقول PDF/A-2b، فإن إسقاط الملفات بسبب نقص structure tags يغرق التقرير بملاحظات لا ينوي أحد إصلاحها؛ وقبول أي label PDF/A بلا فحص المستوى يقصّر في الوعد. تضيف متطلبات الوصولية الحكومية على نحو متزايد PDF/UA فوق ذلك، ولا يكلف إدراجه شيئاً إضافياً لأن BuildPdfPreflightReport، من وحدة FPdfPreflightReport، يقبل set:
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
استدعاء واحد، والمعياران معاً، وسجل تقرير موحد.
قراءة التقرير: الصمت ليس مطابقة
يسرد التقرير findings لكل معيار. لذلك تعني قائمة issues فارغة "لم تُعثر مشكلات في المعايير التي شُغّلت"؛ وهي ليست العبارة نفسها "الملف يطابق المعيار الذي يهمك". إذا أسقط typo في التكوين ppsPdfA من المجموعة، فستكون قائمة issues فارغة أيضاً. امشِ دائماً عبر Report.Results وأثبت شيئين لكل معيار نويت تشغيله: أن entry له موجود أصلاً، وأن علم IsCompliant، المدعوم بـ Status = pfsPass، صحيح. هذه بالضبط حالة الفشل وراء قصة التدقيق أعلاه: ساوى job بين "لا findings" و"archive ready" من دون أن يفحص أبداً أي معايير قُيّمت.
الفخ الثاني مخبأ في معنى finding: يحمل كل TPdfPreflightIssue قيمة Code وCategory وDescription وRecommendation؛ إنه يسمي القاعدة المخالفة، لا رقم صفحة. هذا يشكّل feedback loop: يخبر التقرير فريق الإنتاج فئة العيب التي يجب إصلاحها، مثل خط غير مضمّن أو معرف XMP مفقود، أما تحديد الكائن المخالف المحدد فهو عمل أداة remediation لا validator. اكتب مستهلكي التقرير على قيم Code المستقرة بدلاً من تحليل نص description قد يعاد صياغته بين الإصدارات.
ملفات تقرير للآلات وللشخص المناوب
يكتب سجل التقرير findings نفسها بخمس صيغ: SaveJsonToFile وSaveCsvToFile وSaveHtmlToFile وSaveTextToFile وSaveMarkdownToFile، مع دوال مطابقة بأسلوب ToJson عندما تريد string بدلاً من ملف. قاوم اختيار صيغة واحدة فقط. JSON للـ pipeline: أرفقه بسجل job ودع CI يحلل issue codes وحالات كل معيار. HTML للمشغل الذي يتلقى الإنذار: يفتح في أي browser بلا أدوات. كتابة الاثنين تكلف سطراً إضافياً لكل ملف وتزيل أسوأ تجربة on-call في batch processing، وهي reverse-engineering لـ JSON blob في الثانية صباحاً. أبقِ التسمية حتمية: اشتق اسم كل report من اسم input file لا من timestamp، وإلا ستتداخل تقارير parallel runs بحيث لا تستطيع إعادتها إلى مدخلاتها.
تنتمي عتبات severity إلى التكوين لا الكود. finding نفسه، مثل annotation بلا alternate description، فشل صارم في بوابة PDF/UA submission وملاحظة قابلة للتجاهل في أرشيف داخلي. اكشف fail-on level لكل profile حتى تتغير السياسة بلا recompile، وسجّل المستوى المطبق داخل job summary، لأن أحداً في الربع القادم لن يتذكر أي threshold استخدم batch أكتوبر الماضي.
عزل الملفات بحيث لا يسقط PDF سيئ batch كله
procedure RunPreflightBatch(const InputDir, ReportDir: string;
out FilesWithFindings, ToolFailures: Integer);
var
SR: TSearchRec;
Pdf: TPdf;
Report: TPdfPreflightReport;
begin
FilesWithFindings := 0;
ToolFailures := 0;
if FindFirst(InputDir + '*.pdf', faAnyFile, SR) = 0 then
try
repeat
Pdf := TPdf.Create(nil); // fresh instance per file: no state bleed
try
try
Pdf.FileName := InputDir + SR.Name;
Pdf.Active := True;
if not Pdf.Active then // load failures are silent, not raised
raise EPdfError.Create('Cannot open ' + SR.Name);
Report := BuildPdfPreflightReport(Pdf, [ppsPdfA, ppsPdfUa]);
Report.SaveJsonToFile(ReportDir + ChangeFileExt(SR.Name, '.json'));
Report.SaveHtmlToFile(ReportDir + ChangeFileExt(SR.Name, '.html'));
if Report.TotalIssueCount > 0 then
Inc(FilesWithFindings);
except
on E: Exception do
begin
Inc(ToolFailures); // exit-code-2 territory, not a validation verdict
WriteLn(ErrOutput, SR.Name + ': ' + E.Message);
end;
end;
finally
Pdf.Free;
end;
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
تعيش ثلاثة اختيارات متعمدة في تلك الحلقة. كائن TPdf جديد لكل ملف يضمن أن مستنداً يفسد engine state لا يسمم خلفاءه. فحص Active الصريح مهم لأن ضبط Active := True يبتلع أخطاء التحميل بدلاً من رفعها؛ من دون الحارس سيدخل ملف truncated في استدعاء validation قبل أن يفشل برسالة مضللة. ويجلس try..except الداخلي داخل نطاق الملف الواحد، لذلك يزيد exception عداد الفشل وتستمر الحلقة؛ المدقق يريد تقارير للملفات الجيدة 4,999 حتى عندما يكون الملف 5,000 truncated. وتُكتب صيغتا التقرير قبل حساب الحكم، بحيث تبقى الأدلة حتى إذا كان في summary logic عيب.
بعد ذلك تنضغط خريطة exit-code إلى بضعة أسطر في project file:
begin
RunPreflightBatch(ParamStr(1), ParamStr(2), Findings, Failures);
if Failures > 0 then
Halt(2)
else if Findings > 0 then
Halt(1);
// falling through exits with 0: every file conformed
end.
ما الذي لن يفعله preflight لك
المحرك يكشف ولا يصلح. finding عن خط غير مضمّن أو color space معتمد على device هو work order لمن ينتج الملفات، لا شيء يستطيع validator ترقيعه في مكانه. خطط feedback loop تبعاً لذلك: يجب أن تصل التقارير إلى حيث يقرأها فريق الإنتاج، وإلا ستتكرر findings نفسها كل ليلة حتى يتساءل أحدهم لماذا لا ينحني المنحنى. ومن المفيد أيضاً cross-check لعينة من الأحكام مع validator مستقل، مثل veraPDF لـ PDF/A وpreflight في Acrobat لـ PDF/X، قبل أن يفعل ذلك مدقق خارجي. اتفاق محركين دليل قوي؛ محرك واحد وحده رأي.
Frequently asked questions
هل يمكنني التحقق من PDF/A وPDF/UA في المرور نفسه؟
نعم. يأخذ BuildPdfPreflightReport set من المعايير، لذلك يقيم [ppsPdfA, ppsPdfUa] المعيارين في تشغيل واحد بتقرير موحد. كما تتزاوج فحوص PDF/UA طبيعياً مع عمل الوصولية في العارض مثل الأنماط في بناء قارئ PDF قابل للوصول في Delphi.
لماذا لا يعرض تقريري أي issues لملف يرفضه veraPDF؟
تأكد أولاً من أن المعيار شُغّل فعلاً: امشِ عبر Report.Results بحثاً عن entry يطابق Standard الخاص به وتحقق من علم IsCompliant، بدلاً من الاستنتاج من قائمة findings فارغة. إذا تطابقت المعايير وظل المحركان يختلفان، فاحتفظ بالمستند كحالة regression مسماة؛ الأحكام المتباينة على ملفات عملاء حقيقية هي بالضبط ما تحتاجه اختبارات الإصدار.
هل يصلح preflight المشكلات التي يجدها؟
لا. إنه يبلّغ مخالفات اللون والخط والبنية وmetadata باستخدام codes وcategories وrecommendations؛ ويحدث remediation في سير العمل المنتج. خصص ميزانية لذلك loop، لا للفحص وحده.
Profiles وصيغ التقارير وpreflight API الكامل موثقة على صفحة المنتج: PDFium Component. المحرك نفسه يقود الفحوص التفاعلية في review UI، لذلك يمكن لهذه CLI وPDF intake review workbench لديك مشاركة مفردات validation واحدة.