تستطيع PDF Library for Delphi مطابقة النص بالتكافؤ القانوني بدلًا من وحدة الترميز، بحيث يجد استعلام مكتوب كحرف مُركَّب مسبقًا محتوى مُخزَّنًا كحرف أساس زائد علامة تركيب، والعكس صحيح. ويتحكم بذلك خياران للبحث: يُفعِّل soCanonicalEquivalent تطبيع يونيكود أثناء المطابقة، ويُقيِّد soGraphemeClusters كل نتيجة وكل خطوة حرف بديل بمجموعات أحرف مرئية كاملة
والعلة التي يصلحها هذا من أكثر العلل المُبلَّغ عنها وأقلّها فهمًا في بحث المستندات. يبحث مستخدم عن اسم، ولا يرى نتائج، فينسخ الاسم من المستند، ويلصقه في مربع البحث، ويجده. لا شيء معطوب بشكل واضح: السلسلتان تبدوان متطابقتين، وتُطبَعان بشكل متطابق، لكن مقارنتهما تُظهر عدم التطابق، لأن إحداهما U+00E9 والأخرى U+0065 متبوعة بـ U+0301
لماذا تُقارَن الكلمة نفسها على أنها غير متطابقة؟
يسمح يونيكود بعدة ترميزات للحرف المجرد نفسه. فحروف لاتينية بعلامات تشكيل توجد كنقاط ترميز مُركَّبة مسبقًا وكتسلسلات أساس زائد تركيب. ومقاطع الهانغول توجد كمقاطع مُركَّبة مسبقًا وكجامو مُفكَّكة. وأيّ منهما يحتويه ملف PDF يعتمد على المُنتِج، والمنصة، وأحيانًا الخط، ولا شيء من ذلك مرئي للشخص الذي يجري البحث
وسبب عدم حل طيّ حالة الأحرف البسيط لهذا هو بنيوي لا عرضي. فطيّ حالة الأحرف وطيّ علامات التشكيل واحد لواحد على مستوى وحدة الترميز: للسلسلة المطوية الطول نفسه للأصل، بحيث يكون موضع تطابق في النص المطوي موضع تطابق في الأصل. أما التطبيع فليس واحدًا لواحد. فحرف مُركَّب مسبقًا واحد يصبح وحدتَي أو ثلاث وحدات ترميز، وتسلسل مُفكَّك ينهار عائدًا إلى واحدة، وبعد ذلك التحويل، لم تعد المواضع تتوافق مع النص الذي استخرجته
إبقاء إحداثيات النتائج مُشيرة إلى النص الأصلي
وهذا هو الجزء الذي يحدد ما إذا كان البحث المُطبَّع قابلًا للاستخدام لا صحيحًا فحسب. تسجّل كل وحدة ترميز ينتجها التطبيع موضع البداية والنهاية للنص الأصلي بترميز UTF-16 الذي أنتجها. وترث التفكيكات التكرارية نطاق مصدر أصلها، وتدمج التركيبات نطاقات مُدخَلاتها، وحين يُعثَر على تطابق تمسح المكتبة فاصل التخطيط بحثًا عن أصغر بداية وأكبر نهاية
والأثر هو أن MatchStart وMatchLength والسلاسل السياقية ونقطتَي الدخول للاستبدال تستمر جميعًا في الإشارة إلى النص المستخرَج الأصلي، لا الوسيط المُطبَّع. ومن دون ذلك التخطيط، يمكن لبحث مُطبَّع أن يخبرك أن نتيجة موجودة لكن ليس بموثوقية أين كانت، ما يجعل التظليل خاطئًا والتنقيح خطيرًا
والمُطبِّع نفسه مستقل بذاته: جداول مُدمَجة للتفكيك القانوني، والتركيب، وصنف التركيب القانوني من يونيكود 15.1، مع معالجة الهانغول بالقواعد الخوارزمية لا بمُدخَلات الجداول. ولا يُحمَّل شيء من ملف بيانات خارجي ولا تُستدعى أي واجهة برمجة تطبيع تابعة للمنصة، بحيث تُنتج خدمة Windows، وخدمة خلفية Linux، وبناء FPC نتائج متطابقة على المُدخَل نفسه
البحث بالتكافؤ القانوني
الخيارات مجموعة، لذا يجتمع التكافؤ القانوني مع السلوكيات الموجودة مثل مطابقة الكلمة الكاملة، والأحرف البديلة، وطيّ عدم الحساسية لعلامات التشكيل:
uses
PDFlibrary;
var
Lib: TPDFlib;
Hits: array of TPDFlibSearchHit;
Found, I: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('contracts.pdf', '');
SetLength(Hits, 500);
Found := Lib.SearchText('Bäcker', [soCanonicalEquivalent, soWholeWord],
'', Hits); // نطاق صفحات فارغ = المستند بأكمله
for I := 0 to Found - 1 do
Log(Format('page %d: "%s" at %d (%d chars)',
[Hits[I].Page, Hits[I].MatchText, Hits[I].MatchStart,
Hits[I].MatchLength]));
finally
Lib.Free;
end;
end;
والتطبيع اختياري لسبب. فبناء نص NFD وتخطيط مواضعه يكلّف عملًا، ومعظم عمليات البحث في مستندات ASCII فقط لا تحتاج إليه أبدًا. وحين يُستخدَم الخيار، تُخزِّن كل كتلة نص مؤقتًا صيغتين مُحوَّلتين، واحدة بعلامات التركيب مُزالة وأخرى بدونها، بحيث تُطبَّع دفعة من الاستعلامات على الكتلة نفسها مرة واحدة بدلًا من مرة لكل استعلام. ويستمر طيّ حالة الأحرف في السير على المسار الأرخص واحد لواحد دون تغيير
ما الذي ينكسر من دون حدود مجموعات الأحرف المرئية؟
وحدات الترميز ليست أحرفًا، والأحرف ليست ما يُدركه المستخدمون. فرمز إيموجي علم نقطتا ترميز مؤشر إقليمي. ورمز إيموجي عائلة عدة نقاط ترميز موصولة بواصلات عرض صفري. والتلاصق الهندي صامت، وحرف virama، وصامت آخر. وحرف بعلامتَي تشكيل متراكبتين ثلاث نقاط ترميز. والمطابقة أو القطع في منتصف أي من هذه ينتج جزءًا يُعرَض كبيانات مهملة
يُقيِّد soGraphemeClusters طرفَي كل نتيجة، حرفية كانت أو ببدلاء، بحدود مجموعات أحرف مرئية موسَّعة كاملة. ويُنفِّذ التجزئة القواعد الموسَّعة: إقران CR وLF، والأحرف التحكمية، وأصناف مقاطع الهانغول، وExtend وSpacingMark، وPrepend، وتسلسلات ZWJ للإيموجي، وإقران المؤشر الإقليمي، وفواصل التلاصق الهندي. ولا يُنتَج حد أبدًا داخل زوج بديل، وهذا وحده يُلغي فئة كاملة من النتائج التالفة على أي محتوى يتجاوز المستوى متعدد اللغات الأساسي
ويحكم الخيار أيضًا استهلاك الحرف البديل، وهو الموضع الذي لا يزال فيه التنفيذ الساذج يقطع بشكل خاطئ. يتقدم الحرف البديل المفرد بمجموعة كاملة واحدة بالضبط، ولا يتحرك التراجع لحرف بديل التسلسل إلا بين حدود المجموعات:
// من دون soGraphemeClusters، يمكن لـ "?" استهلاك نصف مجموعة
// وإعادة نتيجة ينتهي نصها بعلامة تركيب معلَّقة
Found := Lib.SearchText('c?té',
[soWildcards, soCanonicalEquivalent, soGraphemeClusters], '', Hits);
// الحدود نفسها تحمي الاستبدال، بحيث لا يُقسِّم التنقيح
// أو إعادة كتابة المحتوى أبدًا رمز إيموجي أو حرفًا مُشكَّلًا
Replaced := Lib.SearchAndReplaceText('naïve', 'plain',
[soCanonicalEquivalent, soGraphemeClusters], '1-20');
اختيار الخيارات لعبء عمل حقيقي
ثلاثة توليفات تُغطّي معظم الحالات. بالنسبة إلى مربع بحث مستندات داخلي، يمنح soCanonicalEquivalent مع soDiacriticInsensitive السلوك المتسامح الذي يتوقعه المستخدمون، بمطابقة صيغتَي الترميز والتهجئتين المُشكَّلة وغير المُشكَّلة. أما بالنسبة إلى البحث القانوني أو بحث الامتثال، حيث للنتيجة الإيجابية الكاذبة تكلفة، فاستخدم soCanonicalEquivalent مع soCaseSensitive وsoWholeWord واترك طيّ علامات التشكيل معطَّلًا، بحيث يكون التكافؤ دقيقًا ومستقلًا عن الترميز
وبالنسبة إلى أي شيء يُعدِّل المستند، أضف soGraphemeClusters من دون استثناء. فبحث يُعيد نطاقًا خاطئًا قليلًا يُضلِّل قارئًا فحسب؛ أما استبدال أو تنقيح يستخدم النطاق الخاطئ نفسه فيكتب الخطأ في الملف. وتُغطّى عواقب الخطأ في نطاقات الإزالة في التنقيح الحقيقي وإزالة المحتوى
حين يهم الإنتاجية، فضِّل نقاط الدخول الدفعية. تُشغِّل SearchTextBatch كل استعلام غير فارغ بينما تكون كتل نص كل صفحة مقيمة، ما يتجنب إعادة استخراج صفحة لكل استعلام ويعيد استخدام التطبيع المخزَّن مؤقتًا، وتُصدر النُسخ المتدفقة النتائج من دون مخزن مؤقت بحجم يحدده المستدعي. ونموذج الاستخراج الكامن موصوف في بحث النص وتعداد عناصر الصفحة
أنظمة الكتابة التي لا يكون فيها هذا اختياريًا
بالنسبة إلى الكورية، التكافؤ القانوني هو الفرق بين إيجاد اسم وعدم إيجاده، لأن المقاطع المُركَّبة مسبقًا والجامو المُفكَّكة شائعان في المستندات الحقيقية. وبالنسبة إلى الفيتنامية، تجعل علامات التشكيل المتراكبة صيغة التركيب معتمدة كليًا على المُنتِج. وبالنسبة إلى أنظمة الكتابة الهندية، تحدد معالجة التلاصق ما إذا كان حد النتيجة يقع في موضع قابل للقراءة. وبالنسبة إلى اليابانية والصينية، جانب البحث بسيط نسبيًا، رغم أن جانب التخطيط ليس كذلك، كما هو موصوف في الكتابة الرأسية لليابانية والصينية
والقاعدة العملية قصيرة: إذا احتوت مجموعة الملفات على أي لغة غير الإنجليزية، فعِّل التكافؤ القانوني وقِس التكلفة قبل أن تقرر أنها باهظة جدًا. وفي معظم مجموعات المستندات ليست كذلك، والبديل ميزة بحث تفشل بصمت بالضبط على الأسماء التي يهتم مستخدموك أكثر بإيجادها
ويشترك البحث الواعي بيونيكود، والاستخراج، والتنقيح، وإعادة كتابة النص في محرك واحد لـ Delphi وC++Builder وFree Pascal؛ وتوجد قائمة الميزات الكاملة على صفحة PDF Library for Delphi