Tehnični članak

Strukturirano besedilo PDF v Delphiju s PDFium VCL

PDFiumPas vrne besedilo strani kot strukturo namesto niza. GetStructuredText ustvari TPdfStructuredTextPage, ki vsebuje bloke, od katerih vsak hrani vrstice, od katerih vsaka hrani oblikovane razpone, z mejami v prostoru strani na vsaki ravni in ohranjenimi indeksi izvornih znakov, tako da lahko vsak fragment preslikate nazaj na osnovno stran besedila

Izvleček v obliki ravnega niza, s katerim začne večina kode, je še vedno prisoten in še vedno pravilen za svoj namen. Prenehati je dovolj v trenutku, ko morate vedeti, katere besede so bile naslov, katere so pripadale levemu stolpcu ali kje na strani ujemanje dejansko leži

Zakaj je ravni niz napačen izhod za večino nalog?

Ker so vprašanja, ki jih ljudje postavljajo izvlečenemu besedilu, skoraj nikoli "kateri znaki so na tej strani". So "kaj je naslov", "je to tabela", "ali ta odstavek spada v poglavje 4", "kam narišem poudarek". En sam niz ne odgovori na nobeno od njih, vsak odgovor, ki ga iz njega rekonstruirate, pa je hevristika, ki jo zdaj posedujete vi

Dvostolpčne postavitve to naredijo konkretno. Izvlecite dvostolpčni članek kot niz in, odvisno od tega, kako je izdelovalec zapisal vsebinski tok, morda dobite stolpec ena, ki mu sledi stolpec dva, ali pa dobite vrstico ena stolpca ena, vrstico ena stolpca dva, vrstico dve stolpca ena in tako naprej po strani navzdol. Oboje izide iz skladnega PDF. Nobeno ni napačno na ravni formata, ker PDF opisuje sledi na strani in ne orisa dokumenta. Model na osnovi blokov omogoči, da izvlekalnik odločitev o vrstnem redu sprejme izrecno in vam pove, katero odločitev je sprejel

Vrstni red vsebine ali fizična postavitev?

TPdfStructuredTextOptions.ReadingOrder izbira med roContentOrder in roPhysicalLayout, pravilen odgovor pa je odvisen od tega, komu bolj zaupate, izdelovalcu ali geometriji

Vrstni red vsebine vrne besedilo v zaporedju, v katerem ga izriše vsebinski tok. To je hitro, za dokumente, ustvarjene z dobro obnašanim izdelovalcem, pa običajno tudi nameravan vrstni red branja. Fizična postavitev ignorira zaporedje toka in obnovi vrstni red iz tega, kje znaki dejansko sedijo, jih grozdi v vrstice in nato v stolpce. To je tisto, kar želite za skenirane in nato OCR-ane strani, za izhod orodij, ki besedilo izdajo v vrstnem redu pisave in ne branja, in za vse, kjer je vizualni rezultat edini signal, na katerega se lahko zanesete

uses
  PDFium;

var
  Pdf: TPdf;
  Options: TPdfStructuredTextOptions;
  Page: TPdfStructuredTextPage;
  B, L: Integer;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'article.pdf';
    Pdf.LoadDocument;
    Pdf.PageNumber := 1;                     // od 1 naprej

    Options := TPdfStructuredTextOptions.Default;
    Options.ReadingOrder := roPhysicalLayout;
    Options.IncludeFontInfo := True;
    Options.IncludeSemantics := True;
    Options.MaxCharacters := 200000;         // proračun, ki ob preseganju zapre dostop

    Page := Pdf.GetStructuredText(Options);

    for B := 0 to High(Page.Blocks) do
    begin
      if Page.Blocks[B].Kind = cfHeading then
        Emit(Format('H%d: %s',
          [Page.Blocks[B].HeadingLevel, Page.Blocks[B].Text]))
      else
        for L := 0 to High(Page.Blocks[B].Lines) do
          Emit(Page.Blocks[B].Lines[L].Text);
    end;
  finally
    Pdf.Free;
  end;
end;

Kaj doda označevanje, česar geometrija ne zmore?

Namen. Z omogočenim IncludeSemantics bloki iz označenega PDF nosijo Kind, potegnjen iz drevesa strukture, tako da je naslov naslov, ker je to povedal izdelovalec, ne ker je bila njegova pisava večja od povprečja. Vrste pokrivajo oblike, ki so pomembne za ponovno uporabo: cfParagraph, cfHeading s HeadingLevel, cfListItem, cfTableCell, cfCaption, cfFigure in neoznačeno rezervno možnost cfPlain

Polje Source zabeleži, od kod je prišla vsaka klasifikacija, rosStructure za drevo strukture in rosHeuristic za sklepanje, kar je polje, ki ga velja beležiti, ko se odločate, kako daleč zaupati cevovodu za izvleček čez nabor dokumentov. Slike so poseben primer, ki ga velja poznati: za blok cfFigure besedilo prihaja iz nadomestnega opisa in ne iz kakršnih koli glifov, ker slika nima lastnih znakov. Neujemajoče nadomestno besedilo je še vedno predstavljeno namesto izpuščeno, kar je tisto, kar reviziji dostopnosti omogoči videti, da opis obstaja, tudi kadar na strani nič ne izriše ničesar. Sam model označevanja je obravnavan v preverjanju drevesa strukture PDF/UA

Razponi nosijo oblikovanje in izvor

Vsak TPdfStructuredTextSpan nosi svoje besedilo, svoje meje v prostoru strani, FontName, FontSize, FontWeight in Angle, poleg SourceStartIndex in SourceCharacterCount. Razponi se lomijo tam, kjer se spremeni oblikovanje, zato stavek s tremi krepkimi besedami postane trije razponi, obnavljanje poudarka v HTML ali Markdown pa je stvar branja lastnosti in ne ugibanja iz imen pisav

Dve polji izvornega indeksa sta tisti, ki izvleček spremenita v funkcijo in ne le poročilo. Kažeta nazaj v zaporedje znakov strani, kar pomeni, da lahko blok, ki ste ga ujeli v iskanju, pretvorite v geometrijo izbire na ravni znaka ali pravokotnik za poudarek brez drugega, drugače urejenega prehoda čez besedilo; mehanika je opisana v izbiri vizualne besedilne vrstice z okvirji znakov. Polje Angle je pomembnejše, kot izgleda: zasukano besedilo v žigu ali vodnem žigu pristane v istem koordinatnem prostoru kot telo besedila, cevovod, ki ignorira kot, pa bo diagonalno "OSNUTEK" veselo zlil sredi odstavka

Proračun in dva števca kakovosti

MaxCharacters je proračun, ki ob preseganju zapre dostop, ne nastavitev za obrezovanje: stran, ki ga preseže, se ustavi namesto da bi tiho vrnila del vsebine. Na nezaupljivi poti sprejema je to vedenje, ki ga želite, ker je stran z milijonom znakov bodisi strojno ustvarjena pošast bodisi poskus, da vaš izvlekalnik postane najpočasnejši del sistema

Dva števca na vrnjeni strani neposredno opisujeta kakovost izvleka. UnmappedCharacterCount šteje znake brez uporabne preslikave Unicode, kar je klasičen simptom podnabora pisave, vdelanega brez CMap /ToUnicode; tako besedilo se izriše popolnoma in izvleče kot nič uporabnega. GeometryFailureCount šteje znake, katerih omejevalnega okvirja ni bilo mogoče določiti, kar poslabša urejanje po fizični postavitvi. Beležite oba. Nabor dokumentov, kjer sta ti številki dosledno blizu nič, je mogoče indeksirati z zaupanjem, tisti, kjer nista, pa vam pove, da nekateri izdelovalci v vašem cevovodu potrebujejo pozornost, preden je kateri koli nadaljnji rezultat vreden zaupanja

var
  Page: TPdfStructuredTextPage;
  B, S, L: Integer;
  Emphasised: Boolean;
begin
  Page := Pdf.GetStructuredText(Options);

  if Page.UnmappedCharacterCount > 0 then
    Log(Format('page %d: %d characters without a Unicode mapping',
      [Page.PageNumber, Page.UnmappedCharacterCount]));
  if Page.GeometryFailureCount > 0 then
    Log(Format('page %d: %d characters without geometry',
      [Page.PageNumber, Page.GeometryFailureCount]));

  for B := 0 to High(Page.Blocks) do
    for L := 0 to High(Page.Blocks[B].Lines) do
      for S := 0 to High(Page.Blocks[B].Lines[L].Spans) do
      begin
        Emphasised := Page.Blocks[B].Lines[L].Spans[S].FontWeight >= 600;
        AppendRun(Page.Blocks[B].Lines[L].Spans[S].Text, Emphasised,
          Page.Blocks[B].Lines[L].Spans[S].SourceStartIndex);
      end;
end;

Zmogljivost na resničnih straneh

Izvleček s fizično postavitvijo je drag način, izvedba pa je zgrajena za strani, ki so dejansko velike: urejanje znakov teče v O(n log n) in ne s ponavljajočim se pregledovanjem, medpomnilniki vrstic in razponov rastejo geometrijsko namesto ponovnega dodeljevanja na znak, besedilo Unicode je zgrajeno v medpomnilnikih in ne z veriženjem nizov, iskanja pisav za sosednje besedilne objekte pa so predpomnjena. Ta kombinacija je tisto, kar gosto stran s 5.000 znaki ohrani predvidljivo namesto kvadratno

Za opravilo, obremenjeno s številom strani, se še vedno splača izbrati cenejši način, kjer je to mogoče. Za označene dokumente, ki jim zaupate, uporabite roContentOrder z omogočeno semantiko, roPhysicalLayout pa prihranite za skenirano in podedovano gradivo, kjer je geometrija edini signal. Če potrebujete le navaden niz, ostaja preprostejši API, opisan v izvlečku besedila iz dokumentov PDF, hitrejša pot, kadar pa morate besedilo izslediti nazaj do identifikatorjev označene vsebine, branje in pisanje označene vsebine BDC in MCID pokriva to plast

Model blokov se tudi čisto preslika na to, kar želijo cevovodi za priklic: naslov s svojimi odstavki je kos z naslovom, meje pa omogočijo, da navedba kaže na lokacijo na strani in ne na dokument. PDFiumPas je komponenta za Delphi in Lazarus okoli pogona PDFium, dokumentirana s primeri na strani izdelka PDFium