مقاله فنی

استخراج متن PDF با ترتیب ساختار در Delphi با HotPDF

هر استخراج‌گر متنی هندسی در حال حدس زدن است. glyphهایی که صفحه می‌کشد را می‌خواند، بر اساس خط پایه و موقعیت افقی مرتب می‌کند، و امیدوار است چیدمان بصری با ترتیبی که یک انسان می‌خواند مطابقت داشته باشد. روی یک گزارش تک‌ستونی آن حدس درست است. روی یک مقاله ژورنالی دومستونی، یک فرم با نوار کناری، یا جدولی که سلول‌هایش ستون‌به‌ستون ساطع شده‌اند، غلط است به شکلی که دیدنش سخت است و کشفش در پایین‌دست پرهزینه. HotPDF به این با ExtractLoadedPageStructureText پاسخ می‌دهد، که هندسه را کلاً نادیده می‌گیرد: درخت ساختار سند را به ترتیب مؤلف طبق تعریف ISO 32000-1 §14.8.4 می‌پیماید، سپس glyphهای صفحه را بر اساس شناسه محتوای نشان‌دارشان بازمونتاژ می‌کند. برای یک PDF برچسب‌خورده این یک هیوریستیک نیست، ترتیبی است که برنامه تولیدکننده اعلام کرده است

تابع وقتی صفحه هیچ درخت ساختار قابل استفاده‌ای ندارد False برمی‌گرداند، که سیگنال بازگشت به استخراج‌گر هندسی است نه شکست. آن طراحی دو-مسیره بیشتر از الگوریتم اهمیت دارد: پذیرش واقعی سند فرم‌های دولتی برچسب‌خورده و خروجی اسکنر را در یک پوشه می‌بیند، و خط لوله‌ای که فقط یکی را هندل کند خط لوله نیست

چرا استخراج هندسی ترتیب خواندن را غلط می‌گیرد؟

چون یک جریان محتوای PDF اصلاً هیچ ترتیب خواندنی حمل نمی‌کند. دنباله‌ای از عملگرهای ترسیم است، و تولیدکننده آزاد است آن‌ها را به هر ترتیبی که موتور چیدمان خودش پسندد ساطع کند. واژه‌پردازها معمولاً به ترتیب جریان ساطع می‌کنند و مرتب‌سازی هندسی خوب به‌نظر می‌رسد. ابزارهای چیدمان، طراحان فرم و مولدهای گزارش اغلب این‌طور نیستند: پانویس صفحه می‌تواند پیش از بدنه ساطع شود، یک جدول می‌تواند ستون‌محور پر شود، و یک صفحه دومستونی می‌تواند سطرهای هر دو ستون را در هم ببافد چون چیدانگر آن‌ها را با هم حل کرده است

مقایسه صفحه PDF دومستونی که استخراج هندسی مرتب‌شده بر اساس خط پایه در حال دوختن ستون‌ها را در برابر استخراج MCID با ترتیب ساختار در HotPDF نشان می‌دهد
مرتب‌سازی glyphها بر اساس خط پایه دو ستون را به یک چرت‌که در هم می‌بافد، در حالی که درخت ساختار ترتیبی که تولیدکننده اعلام کرد را بازپخش می‌کند

حالت شکست بی‌صدا است. یک استخراج‌گر هندسی هرگز خطا گزارش نمی‌کند، فقط نثری برمی‌گرداند که جمله‌هایش از دو ستون دوخته شده. هر چیزی که آن متن را مصرف کند، یک اندیس جستجو، یک نگاشت‌گر فیلد e-invoice، یک خط لوله بازیابی که به یک مدل زبانی تغذیه می‌کند، بدون هشدار آسیب را به ارث می‌برد. HotPDF همچنین استخراج‌گرهای هندسی برای اسناد بارگذاری‌شده را عرضه می‌کند، و آن‌ها برای فایل‌های بدون برچسب همچنان ابزار درست‌اند؛ نکته مسیر ترتیب-ساختار این است که وقتی سند از قبل پاسخ را حمل می‌کند دیگر حدس نزنید

درخت ساختار در واقع چه چیزی ذخیره می‌کند

یک PDF برچسب‌خورده توصیف دومی موازی از صفحه حمل می‌کند. کاتالوگ به یک /StructTreeRoot اشاره می‌کند، که فرزندان /K آن یک درخت از عناصر ساختار می‌سازند: /Document، /Sect، /P، /Table، /TR، /TD و غیره. برگ‌های آن درخت ارجاع‌های محتوای نشان‌دارند، عدد صحیح‌هایی که یک بازه از جریان محتوای صفحه را نام می‌برند. در سمت محتوا، آن بازه‌ها با یک عملگر BDC حامل /MCID باز و با EMC بسته می‌شوند. هر عنصر ساختار همچنین یک entry مربوط به /Pg حمل می‌کند که صفحه متعلق به را نام می‌برد، که همان چیزی است که پیمایش به‌ازای هر صفحه را در سندی که درخت ساختارش صدها صفحه را دربرمی‌گیرد ممکن می‌سازد

کالبدشکافی درخت ساختار PDF که عناصر StructTreeRoot مثل Sect و Table و TR و TD را به بازه‌های MCID مربوط به BDC در جریان محتوای صفحه HotPDF پیوند می‌زند
برگ‌های درخت ارجاع‌های محتوای نشان‌دارند، و هر عنصر یک entry مربوط به Pg حمل می‌کند که اجازه می‌دهد پیمایش به صفحه جاری فیلتر شود

HotPDF آن درخت را با سقف عمق ۱۲۸ سطح می‌پیماید و روی /Pg فیلتر می‌کند تا فقط صفحه جاری سهم بگیرد. خروجی پیمایش متن نیست، یک فهرست مرتب از مقادیر MCID است: ترتیب مؤلف بازه‌های محتوای نشان‌دار در این صفحه. بازمونتاژ متن بعد کار بازپخش glyphها در آن ترتیب است

شناسه MCID حین استخراج glyph ثبت می‌شود، نه اینکه بعداً جست‌وجو شود

این همان جزئیات پیاده‌سازی است که ویژگی را ارزان می‌کند. HotPDF از قبل شناسه محتوای نشان‌دار فعال را روی هر glyphی که استخراج می‌کند ثبت می‌کند، در فیلد MCID از THPDFGlyphRecord، چون مفسر جریان محتوا در لحظه پردازش هر عملگر Tj یا TJ می‌داند کدام قلمرو BDC باز است. پس استخراج با ترتیب ساختار به گذر دومی روی جریان محتوا نیاز ندارد. دنباله MCID را از درخت ساختار جمع می‌کند، سپس glyphهای از پیش استخراج‌شده را بر اساس MCID سبدبندی و در آن دنباله ساطع می‌کند

var
  Pdf: THotPDF;
  PageCount, I, Untagged: Integer;
  PageText, AllText: UnicodeString;
  Report: TStrings;   // حوض تشخیصی متعلق به فراخواننده
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('accessible-form.pdf');
    AllText := '';
    for I := 0 to PageCount - 1 do
    begin
      if Pdf.ExtractLoadedPageStructureText(I, PageText, Untagged) then
      begin
        // ترتیب مؤلف مستقیم از درخت ساختار
        if Untagged > 0 then
          Report.Add(Format('page %d: %d glyphs outside the structure tree',
            [I, Untagged]));
      end
      else
        // بدون درخت ساختار قابل استفاده در این صفحه: fallback هندسی
        Pdf.ExtractLoadedPageText(I, PageText);
      AllText := AllText + PageText + #13#10;
    end;
  finally
    Pdf.Free;
  end;
end;

glyphهای بدون برچسب شمرده می‌شوند، هرگز بی‌سروصدا دور ریخته نمی‌شوند

یک صفحه می‌تواند تا حدی برچسب‌خورده باشد. تولیدکنندگان یک خط تزئینی، یک شماره صفحه، یا یک watermark دیرهنگام را بیرون از هر قلمرو BDC اضافه می‌کنند، و آن glyphها به هیچ MCIDای تعلق ندارند. انداختن‌شان پیاده‌سازی مرتب و غلط بود، چون همان شکاف وقتی هم ظاهر می‌شود که تولیدکننده بدنه را برچسب می‌زند اما جدول را فراموش می‌کند، و شما جدول را بدون فهمیدن از دست می‌دادید

HotPDF glyphهای بدون مالک را به‌عنوان یک دنباله هندسی پس از متن مرتب‌شده با ساختار می‌چسباند و تعدادشان را از طریق پارامتر خروجی UntaggedGlyphCount گزارش می‌کند. آن عدد یک سیگنال کیفی است که می‌توانید بر اساسش عمل کنید. مشتی glyph در صفحه‌ای دو هزارتایی وسایل صفحه است و می‌توان نادیده‌اش گرفت. چهل درصد صفحه بیرون از درخت ساختار یعنی برچسب‌زنی تزئینی است و استخراج‌گر هندسی پاسخ صادقانه‌تری برای آن فایل است

جریان تصمیم برای استخراج متن ساختاری HotPDF با fallback هندسی وقتی صفحه درخت ساختار قابل استفاده یا برچسب‌زنی تزئینی دارد
True یعنی ترتیب ساختار با دنباله بدون‌برچسب چسبیده، و False صفحه را به‌جای شکست به استخراج‌گر هندسی می‌فرستد
function ExtractPageBestEffort(Pdf: THotPDF; PageIndex: Integer;
  out AText: UnicodeString; out UsedStructure: Boolean): Boolean;
var
  Untagged, TotalGlyphs: Integer;
  Glyphs: THPDFGlyphArray;
begin
  UsedStructure := False;
  if Pdf.ExtractLoadedPageStructureText(PageIndex, AText, Untagged) then
  begin
    TotalGlyphs := 0;
    if Pdf.ExtractLoadedPageGlyphs(PageIndex, Glyphs) then
      TotalGlyphs := Length(Glyphs);
    // به درخت ساختار فقط وقتی اعتماد کن که بیشتر صفحه را مدعی است
    if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
    begin
      UsedStructure := True;
      Result := True;
      Exit;
    end;
  end;
  Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;

چه چیزی تابع را به بازگرداندن False می‌راند

سه حالت، و ارزش تفکیک دارند چون فقط یکی از آن‌ها عیبی در خود سند است. اولی یک PDF معمولی بدون برچسب است: بدون /StructTreeRoot، چیزی برای پیمودن نیست، و False صرفاً حقیقت است. دومی صفحه اسکن‌شده‌ای است که متنش از لایه OCR می‌آید که هرگز برچسب نخورده. سومی جذاب است: محتوایی که عملگرهای BDC با مقادیر /MCID حمل می‌کند اما صفحه‌اش هیچ entry مربوط به /StructParents ندارد و درخت ساختارش هرگز به آن شناسه‌ها ارجاع نمی‌دهد. محتوای نشان‌دار وجود دارد، سمت ساختار نه، و ترتیبی برای بازیابی نیست. HotPDF به‌جای اختراعش False گزارش می‌کند

آن مورد آخر در فایل‌های ویرایش‌شده با دست و در خروجی ابزارهایی ظاهر می‌شود که محتوای نشان‌دار را برای مقاصد optional-content یا artifact ساطع می‌کنند بدون ساختن درخت ساختار. اگر خودتان PDF برچسب‌خورده تولید می‌کنید، همان عدم‌تقارن است که اعتبارسنجی PDF/UA دنبالش می‌گردد، و قرینه سمت نویسنده در DOM چیدمانی که خروجی برچسب‌خورده و صفحه‌بندی‌شده ساطع می‌کند پوشش داده شده است

کجا ترتیب ساختار خرجش را درمی‌آورد

ممیزی دسترس‌پذیری بدیهی‌ترین است: اگر در حال گواهی‌کردن یک سند در برابر PDF/UA هستید، ترتیب خواندنی که یک صفحه‌خوان اعلام خواهد کرد دقیقاً ترتیب ساختار است، پس استخراجش همان راه بازبینی‌اش بدون صفحه‌خوان است. برداشت داده مورد تجاری بزرگ‌تر است. فرم‌های دولتی برچسب‌خورده، افشاهای مقرره‌ای و پیوست‌های e-invoice برچسب‌ها و مقادیر فیلد را به ترتیب اعلام‌شده حمل می‌کنند، و خواندن‌شان در آن ترتیب یک رده کامل از باگ‌های نگاشت را حذف می‌کند که استخراج هندسی روی چیدمان‌های چندستونی می‌سازد

جدیدترین مصرف‌کننده بازیابی برای مدل‌های زبانی است. تکه‌تکه‌کردن یک سند برای embedding فقط به خوبی ترتیب متن است، و تکه‌ای که دو ستون را می‌دوزد جمله‌هایی تولید می‌کند که هرگز وجود نداشته‌اند. استخراج با ترتیب ساختار ارزان‌ترین fix موجود برای آن است، چون برای اسناد برچسب‌خورده ترتیب درست از قبل در فایل هست و فقط باید خوانده شود

HotPDF یک مؤلفه VCL بومی برای Delphi و C++Builder است، پس پیمایش درخت ساختار و بازپخش glyph هر دو درون-فرایندی روی یک سند بارگذاری‌شده اجرا می‌شوند بدون دخالت هیچ رندرکننده خارجی. جزئیات کامل API برای خانواده استخراج اسناد بارگذاری‌شده در صفحه محصول HotPDF Delphi PDF component است