مقال تقني

أوامر التقاط الويب SpiderInfo في كتالوج PDF في Delphi

تكتب مكتبة PDF لـ Delphi (PDFlibPas) بيانات أوامر التقاط الويب في كتالوج المستند عبر AddWebCaptureCommand، فتملأ قاموس /SpiderInfo المعرّف في ISO 32000-1 §14.10. يسجّل كل أمر عنوان URL الذي تم جلبه، وعدد المستويات التي نزل فيها الجلب، ومجموعة صغيرة من الأعلام. إنه يسجّل النية — لا يجلب أي شيء

هذا التمييز هو السبب الوحيد الذي يجعل قراءة هذه الواجهة سهلة الخطأ. يسلّمك مهندس دعم ملف PDF مجمّعاً من التوثيق الإلكتروني لمورّد، ويسألك من أين جاءت الصفحة 40. إذا كان المنتج قد ملأ /SpiderInfo، فالإجابة موجودة في الكتالوج: عنوان URL، وعمق الزحف، وربما جسم POST الذي أنتج الصفحة. أما إذا لم يملأه، فلن يخبرك أي قدر من قراءة تدفق المحتوى. كتابة هذا الأصل رخيصة، ومزعجة بنيوياً، ويتم تخطيها في الغالب

ما الذي يسجّله قاموس /SpiderInfo في الكتالوج فعلاً؟

/SpiderInfo قاموس معلومات التقاط ويب معلّق على كتالوج المستند، ويحمل شيئين فقط تهمّ مكتبة PDF لـ Delphi: /V، وهو رقم إصدار قاعدة بيانات التقاط الويب الذي تكتبه المكتبة دائماً كـ 1.0، و /C، وهو مصفوفة من قواميس الأوامر. رقم الإصدار ليس إصدار PDF — إنه يحدد نموذج بيانات التقاط الويب نفسه، و 1.0 هي القيمة الوحيدة التي يعرّفها ISO 32000-1 §14.10. كل ما هو مثير للاهتمام يعيش في /C. يحمل كل قاموس أمر /URL (العنوان الذي تم طلبه)، و /L (عدد مستويات الروابط التي تم تتبعها منه)، و /F (أعلام العبور). ثلاث مفاتيح اختيارية تصف الطلب نفسه عندما لم يكن GET بسيطاً: /P لبيانات POST، و /CT لنوع محتوى تلك البيانات، و /H لترويسات الطلب الإضافية. تخطّط مكتبة PDF لـ Delphi هذه المفاتيح الستة جميعها على سجل واحد، TPDFlibWebCaptureCommand، حقوله URL و Levels و Flags و PostData و ContentType و Headers، كلها من نوع AnsiString باستثناء العدادين من نوع Integer. كما أن وجود /SpiderInfo يرفع الحد الأدنى لإصدار المستند: يستدعي AddWebCaptureCommand الدالة EnsureMinVersion('1.3', 'Catalog /SpiderInfo Web Capture command')، وتسجّل قواعد كشف الإصدار في مكتبة PDF لـ Delphi قاعدة مطابقة لـ PDF 1.3 بحيث يبلّغ مستند يُعاد قراءته لاحقاً عن نفس الحد الأدنى

بنية قاموس SpiderInfo في الكتالوج الذي تكتبه PDFlibPas: يحمل /SpiderInfo قيمة /V المضبوطة على إصدار قاعدة بيانات التقاط الويب 1.0 و /C كمصفوفة من المراجع غير المباشرة، ويحمل كل قاموس أمر مفاتيح /URL و /L و /F والمفاتيح الاختيارية /P و /CT و /H التي تخطّط على حقول سجل TPDFlibWebCaptureCommand
قائمة الأوامر معلّقة على كتالوج المستند كإدخال إصدار واحد زائد مصفوفة من المراجع غير المباشرة، بحيث يمكن الإشارة إلى جلب مشترك من أكثر من مكان واحد بينما يرتفع الحد الأدنى للإصدار إلى PDF 1.3

لماذا يجب أن يكون أمر التقاط الويب كائناً غير مباشر

القاعدة البنيوية الوحيدة التي توقع الناس في الخطأ: يجب تسجيل قاموس الأمر ككائن غير مباشر قبل وضعه في /C، لأن ISO 32000-1 §14.10 يحدد /C كمصفوفة من المراجع غير المباشرة لقواميس الأوامر، وليس مصفوفة من القواميس. ضع قاموساً مباشراً في تلك المصفوفة فستحصل على شيء يُحلَّل كـ PDF صالح لكنه لا يعني شيئاً كسجل التقاط ويب — عارض أو أداة أرشفة تجتاز قائمة الأوامر تجد كائناً سطرياً حيث يجب أن يكون مرجع، والإدخال ببساطة ليس أمراً. تتعامل مكتبة PDF لـ Delphi مع هذا داخل AddWebCaptureCommand: تبني القاموس، وتسلّمه إلى OnNewIndObj للحصول على رقم كائن ورقم جيل، وتلفّ الزوج في مرجع غير مباشر، وتلحق المرجع بالمصفوفة. إذا فشل OnNewIndObj في إنتاج رقم كائن موجب، تعيد الدالة 0 وتُترك المصفوفة كما هي. سبب إصرار المواصفة على عدم المباشرة هو أن الأمر الواحد يمكن مشاركته — يمكن الإشارة إلى نفس الجلب من أكثر من مكان واحد في بنية التقاط الويب — والكائنات المشتركة في PDF هي، بحكم التعريف، غير مباشرة

كيف تضيف أمر التقاط ويب من Delphi؟

املأ السجل بالكامل، ثم استدعِ AddWebCaptureCommand، التي تعيد فهرس الأمر الجديد الذي يبدأ من 1 أو 0 إذا رفض التحقق منه. الاكتمال مهم هنا أكثر من المعتاد. تهيئ Delphi حقول AnsiString المُدارة في السجل المحلي إلى فارغة، لكن حقول Integer — Levels و Flags — لا تحصل على هذه المعاملة، لذا فإن السجل الذي تنسى ملءه يصل حاملاً ما كان على المكدس. يتطلب AddWebCaptureCommand أن يكون Levels على الأقل 1 وأن يكون Flags غير سالب، وقيمة قمامة المكدس إما أن تُرفض تماماً أو، الأسوأ، تُقبل بصمت كعمق يبدو معقولاً

مسار التحقق في AddWebCaptureCommand ضمن PDFlibPas: املأ كل حقول السجل لأن Levels و Flags عددان صحيحان عاديان يحملان قمامة المكدس، واشترط أن يكون Levels على الأقل 1، وارفض Flags خارج القائمة البيضاء 1 و 2 و 4، وأنشئ /SpiderInfo عند الحاجة، وسجّل الأمر ككائن غير مباشر عبر OnNewIndObj، وأعد الفهرس الذي يبدأ من 1 أو 0
كل رفض يحدث قبل لمس المستند: السجل الذي يفشل في فحوصات العدّاد أو الأعلام يعيد 0، ويترك الكتالوج كما كان تماماً، ولا يكتب أبداً نائباً فارغاً في الملف
var
  Lib: TPDFlib;
  Command: TPDFlibWebCaptureCommand;
  Index: Integer;
begin
  Lib := TPDFlib.Create;
  try
    if Lib.LoadFromFile('captured-site.pdf', '') = 1 then
    begin
      // قم بتعيين كل حقل: Levels و Flags عددان صحيحان عاديان
      // ويحملان قمامة المكدس إذا تخطيتهما.
      Command.URL := 'https://example.com/docs/index.html';
      Command.Levels := 2;
      Command.Flags := PDF_WEB_CAPTURE_SAME_SITE or PDF_WEB_CAPTURE_SAME_PATH;
      Command.PostData := '';
      Command.ContentType := '';
      Command.Headers := '';

      // يعيد فهرس الأمر الجديد الذي يبدأ من 1، أو 0 عند الرفض.
      Index := Lib.AddWebCaptureCommand(Command);
      if Index = 0 then
        raise Exception.Create('Web Capture command rejected');

      Lib.SaveToFile('captured-site.pdf');
    end;
  finally
    Lib.Free;
  end;
end;

الاستدعاء الناجح الأول هو أيضاً ما ينشئ البنية. يطلب AddWebCaptureCommand مصفوفة الأوامر من المساعد الداخلي WebCaptureCommands(True)، وهذا المساعد ينشئ /SpiderInfo مع إدخال /V ثم مصفوفة /C فارغة فقط إذا كانت مفقودة. إنه متحفظ عمداً فيما يجده: إذا كان الكتالوج يحمل بالفعل مفتاح /SpiderInfo قيمته ليست قاموساً، أو مفتاح /C قيمته ليست مصفوفة، يعيد المساعد nil بدلاً من الكتابة فوق الكائن الموجود. البنية الموروثة المعطوبة تجعل الإضافة تفشل؛ وهي لا تجعل مكتبة PDF لـ Delphi تدمر ما كتبه المنتج السابق

القائمة البيضاء للأعلام: ثلاث بتات محددة ولا شيء غير ذلك

يتحقق AddWebCaptureCommand من Flags ضد قائمة بيضاء ويرفض أي قيمة تحمل بتاً مضبوطاً خارج الثوابت الثلاثة التي تعرّفها مكتبة PDF لـ Delphi: PDF_WEB_CAPTURE_SAME_SITE = 1 (اتبع فقط الروابط التي تبقى على نفس الموقع)، و PDF_WEB_CAPTURE_SAME_PATH = 2 (اتبع فقط الروابط الواقعة تحت نفس المسار)، و PDF_WEB_CAPTURE_SUBMIT = 4 (كان هذا الطلب إرسال نموذج وليس استرجاعاً بسيطاً). مرّر 8، أو 16، أو -1 عرضياً، وسيعيد الاستدعاء 0 دون لمس المستند

القائمة البيضاء للأعلام التي يفرضها AddWebCaptureCommand في PDFlibPas: حقل البتات /F يقبل ثلاث بتات محددة بالضبط، PDF_WEB_CAPTURE_SAME_SITE مضبوطة على 1، و PDF_WEB_CAPTURE_SAME_PATH على 2 و PDF_WEB_CAPTURE_SUBMIT على 4، بينما يرفض أي بت غير معرّف مثل 8 أو سالب 1 عرضي وقت الكتابة
البت غير المعرّف لا يمكن تمييزه عن البت التالف، لذا فإن رفض القيمة وقت الكتابة يبقي الالتباس خارج الملف، وما زال التركيب القانوني مثل 1 أو 2 معاً = 3 يمر

هذا أشد مما تفعله محللات PDF عادة، وعن قصد. /F حقل بتات لا بنية ذاتية الوصف فيه، لذا فإن البت غير المعرّف لا يمكن تمييزه عن البت التالف، وليس لدى المستهلك الذي يقرأ القيمة لاحقاً أي طريقة لتمييز امتداد مستقبلي عن خطأ كتابي. الرفض وقت الكتابة يُبقي الالتباس خارج الملف تماماً. لاحظ أيضاً أن /P و /CT و /H تُكتب فقط عندما تكون الحقول المقابلة في السجل غير فارغة، لذا ينتج GET عادي قاموس أمر بثلاثة مفاتيح — /URL و /L و /F — دون نوائب نصية فارغة تلوّثه، وهو ما يتوقعه القارئ الذي يحلل الملف لطلب لم يحمل أي جسم

// إرسال نموذج: جسم POST زائد نوع محتواه.
Command.URL := 'https://example.com/search';
Command.Levels := 1;
Command.Flags := PDF_WEB_CAPTURE_SUBMIT;          // 4
Command.PostData := 'q=iso32000&scope=all';
Command.ContentType := 'application/x-www-form-urlencoded';
Command.Headers := 'Accept-Language: en-US';
if Lib.AddWebCaptureCommand(Command) = 0 then
  raise Exception.Create('submission command rejected');

// نفس الموقع زائد نفس المسار تركيب قانوني: 1 أو 2 = 3.
Command.Flags := PDF_WEB_CAPTURE_SAME_SITE or PDF_WEB_CAPTURE_SAME_PATH;

// البت 3 غير معرّف في المواصفة، لذا هذا الواحد لا يصل إلى الملف أبداً.
Command.Flags := 8;
if Lib.AddWebCaptureCommand(Command) <> 0 then
  raise Exception.Create('undefined flag bit should have been refused');

قراءة الأوامر مرة أخرى: مفتاح واحد، تمثيلان قانونيان

يعيد GetWebCaptureCommand الرقم 1 عند النجاح و 0 بخلاف ذلك، وعليه أن يكون أكثر تسامحاً من الكاتب، لأنه يقرأ ملفات كتبها منتجون آخرون. عدم تماثلان مهمان. أولاً، يقبل القارئ إدخال الأمر سواء كان مرجعاً غير مباشر أو قاموساً مباشراً، ويفك الإشارة عند الحاجة — تكتب مكتبة PDF لـ Delphi دائماً الشكل غير المباشر الذي تتطلبه المواصفة، لكن رفض قراءة ملف مهمل لا يفيد أحداً. ثانياً، يُسمح لـ /P أن يكون إما نصاً أو تدفقاً، وهذا التعميم يتجاوز التقاط الويب بكثير. يسمح ISO 32000-1 مراراً لمفتاح واحد بأن يحمل إما نصاً أو تدفقاً عندما يمكن أن تكون القيمة كبيرة بلا حدود — أجسام POST هنا، والنمط نفسه يظهر في أماكن أخرى من الصيغة للبيانات الوصفية والحمولات المضمّنة. لذلك يأخذ المساعد الداخلي ReadString في GetWebCaptureCommand علماً AllowStream، وقراءة /P فقط تمرر True: تُفك قيمة التدفق عبر سلسلة فلاترها وتُعاد كحقل PostData، بينما تبقى /URL و /CT و /H نصوصاً فقط لأن المواصفة لا تقدم لها بديل التدفق. إذا كتبت كود قراءة PDF خاصاً بك، فهذه عادة تستحق السرقة — تحقق من المواصفة أي المفاتيح ثنائية النوع بدلاً من افتراض أنها نصوص

var
  Lib: TPDFlib;
  Command: TPDFlibWebCaptureCommand;
  I, Total, Removed: Integer;
begin
  Total := Lib.GetWebCaptureCommandCount;
  // الفهرسة العامة تبدأ من 1، بما يطابق بقية مكتبة PDF لـ Delphi.
  for I := 1 to Total do
    if Lib.GetWebCaptureCommand(I, Command) = 1 then
      Writeln(Format('%s  levels=%d  flags=%d  post=%d bytes',
        [Command.URL, Command.Levels, Command.Flags, Length(Command.PostData)]));

  // يزيل قاموس /SpiderInfo بالكامل ويبلغ عن عدد الأوامر المسقطة؛
  // يعيد 0 عندما لا يوجد ما يُمسح.
  Removed := Lib.ClearWebCaptureCommands;
  Writeln(Format('cleared %d command(s)', [Removed]));
end;

يعدّ ClearWebCaptureCommands الأوامر، ثم يطهر مفتاح /SpiderInfo بالكامل من الكتالوج بدلاً من مجرد إفراغ /C. ترك /SpiderInfo بمصفوفة أوامر فارغة سيزعم أن المستند يحمل أصلاً من التقاط الويب وأنه لم يُجلب شيء، وهذا ادعاء مختلف وكاذب عن عدم تسجيل أي أصل. الغريزة نفسها تنطبق على البيانات الوصفية للكتالوج عموماً — قاموس الصدفة الفارغ بيان، وليس بقايا محايدة. إذا كنت تدقق ما يدّعيه مستند قبل إعادة كتابته، فإن نمط الاستبطان في قراءة إجراءات المخططات والتعليقات التوضيحية من ملف PDF قائم يتزاوج طبيعياً مع مرحلة اقرأ ثم امسح هنا

كتابة البيانات الوصفية ليست تنفيذ التقاط الويب

كن صريحاً مع نفسك بشأن النطاق: يحفظ AddWebCaptureCommand سجلاً لجلب مقصود أو مكتمل، وهذا كل ما يفعله. لا تنفذ مكتبة PDF لـ Delphi طلبات HTTP، ولا تتبع الروابط التي يصفها Levels الخاص بك، ولا تحترم علمي SAME_SITE أو SAME_PATH — تلك الأعلام توثيق لكيفية تصرف بعض الزاحف، وليست تعليمات تنفذها المكتبة. كود الجلب الخاص بك، مهما كان، يبقى مشكلتك بالكامل

قطعتان أخريان من ISO 32000-1 §14.10 خارج هذه الواجهة أيضاً. يعرّف التقاط الويب مجموعات المحتوى — بنى /Contents التي تربط قطع الصفحات الفردية بالمادة المصدرية التي جاءت منها — وشجرة أسماء تخطّط المعرّفات الرقمية إلى تلك المجموعات. لا تحتفظ مكتبة PDF لـ Delphi بأي منهما. ما تحصل عليه هو قائمة الأوامر: الإجابة عالية المستوى عن "من أين جُلب هذا المستند، وإلى أي عمق ذهب الزحف"، وهي عادةً، لأغراض الأصل والتدقيق واستيعاب الأرشفة، الجزء الذي يسأل عنه أي شخص فعلاً. إذا كنت تريد أن تكون عناوين URL قابلة للنقر للقراء بدلاً من كونها مسجلة للمدققين فقط، فهذه مهمة منفصلة — انظر بناء إجراءات GoToR و GoToE و Launch لجهة الإجراءات، و مشغلات إجراءات دورة حياة المستند والصفحة لإطلاق السلوك عند الفتح أو الإغلاق. بيانات أوامر /SpiderInfo في الكتالوج كما هو موصوف هنا جزء من PDF Library for Delphi، مكتبة PDF الأصلية لـ Delphi و C++Builder