يبدو سحب النص والصور والخطوط من ملف PDF موجود وكأنه مشكلة محلولة حتى تقوم بتشغيل مجموعة حقيقية من خلالها. قم بتوجيه مفهرس بحث إلى أربعين ألف ملف عميل وسيتم فرز الكسر إلى بضعة أكوام يمكن التعرف عليها. تتداخل الكلمات معًا لأنه لم يخبر أحد المستخرج بمدى الفجوة التي تعتبر مسافة. تعود الصفحات الأخرى كثرثرة لأن الخط الفرعي لا يحمل أي خريطة من رموز الحروف الرسومية الخاصة به إلى أحرف فعلية. ويتضح أن "شعار الشركة" عبارة عن تسعة كائنات صور منفصلة مكدسة خلف قناع ناعم. لا شيء من هذا يعد خطأ في المكتبة. إنه الفرق بين استدعاء وظيفة الاستخراج وفهم ما يمكن وما لا يمكن للوظيفة استرداده من البايتات على القرص
تمنح مكتبة losLab PDF Library، إصدار Pascal، كود Delphi و C++Builder أكثر من طريقة واحدة لقراءة كل من هذه التدفقات الثلاثة، وتختلف المستويات فيما تضمنه. الحيلة هي مطابقة المستوى للوظيفة: فهرس بحث، ومراجع تنقيح (redaction)، وتمريرة فحص مبدئي لـ PDF/A جميعهم يريدون أشياء مختلفة من نفس الصفحة، والوصول إلى الاستدعاء الخاطئ يهدر الجهد أو ينتج مخرجات لا يمكنك الوثوق بها
مستويات استخراج النص وما يعد به كل منها
يأخذ GetPageText قيمة خيارات من 0 إلى 8، ويختار هذا الرقم محركًا بدلاً من التنسيق. تعمل القيم من 0 إلى 2 على تشغيل تمرير خفيف الوزن يعد مناسبًا للمعاينة السريعة. التوجيه من 3 إلى 8 من خلال المحرك المدرك للتخطيط (layout-aware)، والذي يعيد بناء الخطوط والتباعد من حيث تجلس الحروف الرسومية (glyphs) بالفعل على الصفحة. ضمن هذا النطاق، تهم الاختلافات: 4 و 6 تقسم المخرجات إلى كلمات، و 5 و 6 تصدر عروض لكل حرف رسومي، ويعيد 7 نصًا عاديًا مع إسقاط الخط واللون والبيانات الوصفية للكتلة عمدًا. الخيار 7 هو الخيار المناسب لتغذية فهرس البحث، لأن الفهرس يريد الكلمات ولا شيء غير ذلك
لا يوجد إعداد خيار يمكنه إنقاذ مستند لم يحمل المعلومات في المقام الأول. يعيّن PDF رموز الأحرف إلى أشكال الحروف الرسومية، والشيء الوحيد الذي يعيّن تلك الرموز مرة أخرى إلى نص مقروء هو CMap ToUnicode الخاص بالخط (ISO 32000-1 §9.10). عندما يتم شحن خط مقطوع (subsetted) بدونه، يتعطل كل مستخرج. هذه المكتبة، النسخ واللصق في العارض، مجموعة أدوات منافسة: يتم تقليصها جميعًا إلى التخمين من أسماء الحروف الرسومية أو عدم إرجاع أي شيء. الاستجابة العملية هي الاكتشاف، وليس البطولة. سجل الصفحة على أنها منخفضة الثقة وأرسلها إلى التعرف الضوئي على الحروف (OCR)، لأن فهرسة القمامة بصمت أسوأ من الاعتراف بأنك لا تستطيع قراءتها
للحالات التي لا تغطيها الخيارات المسطحة، والترميز المخصص، والطب الشرعي لتدفق المحتوى، وقمع نصي مبني وفقًا لقواعدك الخاصة، يتوفر جهاز فك التشفير طبقة واحدة لأسفل. يتم إنشاء TPDFExtractor فوق قاموس موارد الصفحة ومجموعة الخطوط. تعمل طريقة ExtractTextW الخاصة بها على تشغيل عمليات نص تدفق المحتوى الخام مرة أخرى من خلال نفس آلية الخط لاستعادة Unicode، ويسلمك حدث OnFindObject كل كائن أثناء مروره. لا يحتاج معظم الكود أبدًا للوصول إلى هذا العمق. التطبيقات التي تفعل ذلك هي تلك التي تسعد بأن الطبقة عامة وليست مدفونة
الكتل الموضعية: وحدة نتائج البحث ومراجعة التنقيح
يخبرك النص العادي بما تقوله الصفحة. عاجلاً أم آجلاً، يحتاج المنتج أيضًا إلى معرفة أين يقول ذلك، حتى يتمكن من إبراز نتيجة بحث، أو رسم مربع حول مرشح تنقيح (redaction candidate)، أو ربط تعليق توضيحي بالبقعة الصحيحة. يُرجع ExtractPageTextBlocks مقبضًا لقائمة من عمليات تشغيل النص، وكل عملية تشغيل تحمل نصها، والمربع المحيط بها، واسم الخط والحجم الذي تم تعيينه فيه:
var
Pdf: TPDFlib;
Blocks, I: Integer;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile('contract.pdf', '') <> 1 then
raise Exception.Create('load failed');
Pdf.SelectPage(1);
Blocks := Pdf.ExtractPageTextBlocks(0);
for I := 0 to Pdf.GetTextBlockCount(Blocks) - 1 do
Writeln(Format('%s [%s %.1f pt at %.0f,%.0f]',
[Pdf.GetTextBlockText(Blocks, I),
Pdf.GetTextBlockFontName(Blocks, I),
Pdf.GetTextBlockFontSize(Blocks, I),
Pdf.GetTextBlockBound(Blocks, I, 0),
Pdf.GetTextBlockBound(Blocks, I, 1)]));
Pdf.ReleaseTextBlocks(Blocks);
finally
Pdf.Free;
end;
end;
هناك تفصيل واحد في هذا المجال يعرقل عمليات الدمج أكثر من أي شيء آخر. تعد SetTextExtractionArea، و SetTextExtractionWordGap، و SetTextExtractionOptions حالة على مستوى المستند تستمر، وليست وسيطات تقوم بتمريرها لكل استدعاء. قم بتكوين تقييد منطقة لميزة واحدة، على سبيل المثال، قراءة شريط الرأس فقط لتصنيف مستند، فإنه يقطع بصمت كل استخراج يتبع في نفس المقبض، بما في ذلك مستويات GetPageText المدركة للتخطيط التي تصل إليها لاحقًا. إما إعادة تعيين حالة الاستخراج بين المهام المنطقية أو إعطاء كل مهمة مقبض المستند الخاص بها
عتبة فجوة الكلمات هي الرافعة لذلك الكومة الأولى من الفشل، الكلمات التي تتداخل معًا. يخبر SetTextExtractionWordGap محرك التخطيط بمقدار المسافة الأفقية، المقاسة مقابل تباعد الحروف الرسومية للصفحة، التي تفصل كلمة عن الكلمة التي تليها. يريد الجدول الكثيف فجوة أصغر من صفحة تسويقية محددة بشكل فضفاض، وبالتالي فإن العتبة المضبوطة لكل فئة مستند تتفوق على ثابت عالمي واحد. يستمر على المستند مثل بقية حالة الاستخراج، لذلك خطط لتعيينه بشكل متعمد بدلاً من مرة واحدة ونسيانه
الصور: تدفقات أصلية، وليست لقطات شاشة
الطريقة الخاطئة للحصول على الصور من ملف PDF هي عرض الصفحة واقتصاصها. يؤدي ذلك إلى إعادة تشكيل البكسلات، ويخبز في أي دوران، ويتخلص من أي شيء كان عليه الأصل. يقوم GetPageImageList بدلاً من ذلك بتعداد موارد الصور الفعلية التي تشير إليها الصفحة، وكل عنصر يعيد خصائصه وبياناته الأصلية غير المضطربة:
var
ImgList, I: Integer;
begin
Pdf.SelectPage(1);
ImgList := Pdf.GetPageImageList(0);
for I := 0 to Pdf.GetImageListCount(ImgList) - 1 do
begin
Writeln(Pdf.GetImageListItemFormatDesc(ImgList, I, 0));
Pdf.SaveImageListItemDataToFile(ImgList, I, 0,
Format('page1-img%.2d.bin', [I]));
end;
Pdf.ReleaseImageList(ImgList);
end;
تحقق من GetImageListItemFormatDesc قبل أن تفترض أي شيء حول عنصر ما، لأن ما تشير إليه الصفحة نادرًا ما يكون صورة واحدة مرتبة لكل صورة مرئية. يظهر القناع الناعم كإدخال منفصل خاص به. غالبًا ما يتكرر نفس كائن XObject عبر العديد من الصفحات، لذلك قم بإلغاء التكرار عن طريق تجزئة المحتوى (content hash) قبل أرشفة تصدير "جميع الصور"، أو ستكتب نفس الشعار مائة مرة. تحتاج ملفات CMYK JPEGs إلى تطبيق إدارة الألوان في المصب (downstream)، أو يتم عرضها معكوسة في العارضات التي تأخذ القنوات بقيمتها الاسمية. عندما تريد جردًا على مستوى المستند بدلاً من صفحة في كل مرة، يقوم FindImages مع SetFindImagesMode بمسح الملف بأكمله في تمريرة واحدة
هناك حد واحد يستحق الرفع مع أصحاب المصلحة قبل أن يكتب أي شخص معايير القبول: يُرجع استخراج الصورة الموارد النقطية (raster resources) فقط. الشعار أو المخطط المرسوم كمسارات متجهة ليس صورة بالمعنى المرجعي ولن يظهر أبدًا في أي قائمة صور، بغض النظر عن مدى وضوح قراءته كصورة على الشاشة. عندما يكون المطلب في الحقيقة هو تقديم ذلك المخطط كملف، فإن النهج الصادق هو عرض منطقة الصفحة في صورة نقطية (bitmap)، وهي عملية مختلفة بدقة مختلفة. لا ينتمي نوعا الإخراج إلى نفس مجلد التصدير دون ملصق يوضح أيهما هو
الخطوط: سطح تدقيق، وليس ميزة تصدير
تُجيب واجهة برمجة تطبيقات الخطوط على الأسئلة المتعلقة بالخطوط. لا تسلمك ملفات الخطوط نفسها، وهذا التمييز يشكل كل شيء يمكنك بناءه عليها. بعد أن يقوم FindFonts بمسح المستند، يسير التعداد في الخطوط حسب المعرف (ID)، وتقوم استدعاءات الخصائص بالإبلاغ عن أي خط محدد حاليًا:
var
I: Integer;
begin
Pdf.FindFonts;
for I := 1 to Pdf.FontCount do // font indexes start at 1, not 0
if Pdf.SelectFont(Pdf.GetFontID(I)) = 1 then
Writeln(Format('%s type=%d embedded=%d subset=%d',
[Pdf.FontName, Pdf.FontType,
Pdf.GetFontIsEmbedded, Pdf.GetFontIsSubsetted]));
end;
شاهد حدود الحلقة. تعمل فهارس الخطوط من 1 إلى FontCount، بينما تستند فهارس كتلة النص وقائمة الصور إلى بضع فقرات لأعلى إلى الصفر. احمل اصطلاحًا واحدًا إلى الآخر وستحصل على خطأ بواحد (off-by-one) إما أن يتخطى الخط الأول أو ينفد من النهاية، وسيجتاز الاختبار العرضي لأن معظم المستندات تحتوي على عدة خطوط ولا يزال الخطأ يبدو معقولاً. كن واضحًا بشأن النطاق أيضًا. واجهة برمجة التطبيقات هذه لا تحتوي على تصدير خط بمستوى البايت. لا يوجد استدعاء يُرجع برنامج الخط المضمن كملف TTF أو OTF، والتعداد بالإضافة إلى فحص البيانات الوصفية هو النموذج المقصود بالكامل. لا يزال هذا النموذج يغطي ما يطلبه عمل الإنتاج في الواقع من الخطوط: اكتشاف المجموعة الفرعية حسب نمط الاسم، وعمليات تدقيق التضمين قبل التحويل الأرشيفي (الخط غير المضمن هو مانع قوي لـ PDF/A، كما يتناول ذلك الفحص المبدئي لـ PDF/A و PDF/UA في Delphi)، وتشخيصات التشفير لمتى تنخفض ثقة الاستخراج. هناك أيضًا سبب ترخيص لوجود الحد هنا. يعد برنامج الخط الفرعي مادة مرخصة، ومفقود معظم الحروف الرسومية الخاصة به، غير مفيد كخط قابل للتثبيت على أي حال. معالجته كبيانات وصفية للتدقيق بدلاً من كونه أصلًا قابلًا للاستخراج هو الموقف الذي يمكنك الدفاع عنه
الاستدعاء الأخير يسحب وزنه في الفرز. قم بتشغيل GetFontEncoding على كل خط، واقرأه جنبًا إلى جنب مع علامة المجموعة الفرعية (subset flag)، ويمكنك التنبؤ بجودة الاستخراج قبل سحب حرف واحد. الصفحة التي يتم تجميع جميع خطوطها الفرعية بتشفيرات غير قياسية هي مرشحة لـ OCR عند الفحص وحده، مما يتيح لخط أنابيب الدُفعات توجيهها بشكل صحيح دون إهدار تمريرة استخراج فاشلة عليها أولاً
استخراج على نطاق واسع بدون تحميل مستندات
في خط أنابيب الدُفعات، فإن تحميل مستند كامل فقط لقراءة صفحة واحدة هو إدخال/إخراج مهدر، ويتراكم بسرعة عبر المجموعة. تأخذ متغيرات الاستدعاء الواحد، ExtractFilePageText و ExtractFilePageTextBlocks، اسم الملف وكلمة المرور ورقم الصفحة مباشرة وتتخطى التحميل الكامل. بالنسبة لملفات بحجم جيجابايت، يوجد ترس أقل لا يزال متاحًا. يفتح مسار الوصول المباشر ملفًا من خلال تدفق قراءات xref، لذلك فإن DAOpenFileReadOnly متبوعًا بـ DAExtractPageText لا يمس سوى الكائنات التي تحتاجها صفحة واحدة في الواقع. يأتي ذلك مع تحول في الاصطلاح يستحق الالتزام بالذاكرة: تقوم وظائف DA بمعالجة الصفحات بواسطة PageRef، وهو مقبض مرجعي لكائن تحصل عليه من DAFindPage، وليس أبدًا بواسطة رقم صفحة أولي. قم بتمرير الرقم حيث ينتمي المقبض وسيعمل الاستدعاء على الكائن الخطأ دون إثارة خطأ، وهو أسوأ نوع من الأخطاء يمكن تصحيحه. يتم وضع بقية مجموعة أدوات الوصول المباشر في دمج وتقسيم ملفات PDF الكبيرة والوصول المباشر
إذا كانت هناك عادة واحدة تفصل بين كود الاستخراج الذي ينجو من مجموعة حقيقية عن الكود الذي يعرج، فهي معاملة الصفحة كإدخال غير موثوق به بدلاً من مصدر بيانات نظيف. النص الذي لا يوافق ما يعرضه المشاهد هو دائمًا مشكلة تشفير تقريبًا، حيث ينهار حرف ربط (ligature) إلى حرف رسومي واحد أو خط فرعي يفتقد إلى إدخالات ToUnicode الخاصة به، والحل هو قياس الثقة وتحويل الصفحات السيئة إلى التعرف الضوئي على الحروف (OCR)، وليس لمحاربة البايتات. لن تنتج واجهة برمجة تطبيقات الخطوط (Font API) أبدًا TTF أو OTF، حسب التصميم، لذا قم ببناء سير عمل الخطوط حول أسئلة التدقيق. وحالة الاستخراج المستمرة، مربع المنطقة أكثر من أي شيء آخر، هو إعداد تمتلكه طوال حياة مقبض المستند، وليس معلمة تنساها بعد استدعاء واحد. احصل على ردود الفعل الثلاثة هذه بشكل صحيح وسوف تتصرف بقية واجهة برمجة التطبيقات بشكل جيد
إصدارات التقييم والمشاريع التجريبية والمرجع الكامل لواجهة برمجة تطبيقات الاستخراج موجودة على صفحة منتج losLab PDF Library for Delphi