PDFiumPas vraća tekst stranice kao strukturu, a ne kao nisku. GetStructuredText proizvodi TPdfStructuredTextPage koji sadrži blokove, od kojih svaki sadrži redove, od kojih svaki sadrži stilizovane raspone, sa granicama u prostoru stranice na svakom nivou, dok su indeksi izvornih karaktera očuvani tako da se svaki fragment može mapirati nazad na osnovnu tekstualnu stranicu
Izvlačenje u obliku ravne niske, sa kojim većina koda počinje, i dalje postoji i ispravno je za svoju svrhu. Prestaje da bude dovoljno u trenutku kada morate da znate koje su reči bile naslov, koje su pripadale levoj koloni, ili gde se na stranici zapravo nalazi poklapanje
Zašto je ravna niska pogrešan izlaz za većinu poslova?
Zato što pitanja koja ljudi postavljaju o izvučenom tekstu gotovo nikada nisu „koji su karakteri na ovoj stranici”. Ona su „šta je naslov”, „da li je ovo tabela”, „da li ovaj pasus pripada odeljku 4”, „gde da nacrtam isticanje”. Jedna niska ne odgovara ni na jedno od njih, a svaki odgovor koji iz nje rekonstruišete jeste heuristika koju sada posedujete
Rasporedi sa dve kolone čine ovu poentu konkretnom. Izvucite dvokolonski članak kao nisku i, u zavisnosti od toga kako je proizvođač napisao tok sadržaja, možete dobiti kolonu jedan praćenu kolonom dva, ili možete dobiti red jedan kolone jedan, red jedan kolone dva, red dva kolone jedan, i tako niz stranicu. Oba izlaze iz usaglašenog PDF-a. Nijedno nije pogrešno na nivou formata, jer PDF opisuje oznake na stranici, a ne kostur dokumenta. Model zasnovan na blokovima omogućava ekstraktoru da eksplicitno donese odluku o redosledu i kaže vam koju je odluku doneo
Redosled sadržaja ili fizički raspored?
TPdfStructuredTextOptions.ReadingOrder bira između roContentOrder i roPhysicalLayout, a ispravan odgovor zavisi od toga čemu više verujete, proizvođaču ili geometriji
Redosled sadržaja vraća tekst u sekvenci u kojoj ga crta tok sadržaja. To je brzo, a za dokumente generisane od strane dobronamernog proizvođača obično je i nameravani redosled čitanja. Fizički raspored ignoriše sekvencu toka i rekonstruiše redosled na osnovu toga gde karakteri zapravo sede, grupišući ih u redove, a zatim u kolone. To je ono što želite za stranice koje su skenirane pa OCR-ovane, za izlaz iz alata koji emituju tekst po redosledu fonta, a ne po redosledu čitanja, i za sve gde je vizuelni rezultat jedino na šta možete da se oslonite
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; // indeksirano od 1
Options := TPdfStructuredTextOptions.Default;
Options.ReadingOrder := roPhysicalLayout;
Options.IncludeFontInfo := True;
Options.IncludeSemantics := True;
Options.MaxCharacters := 200000; // fail-closed budžet
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;
Šta označavanje (tagging) dodaje što geometrija ne može?
Nameru. Kada je IncludeSemantics omogućen, blokovi iz označenog PDF-a nose Kind preuzet iz stabla strukture, tako da je naslov naslov zato što je proizvođač tako rekao, a ne zato što mu je font bio veći od proseka. Vrste pokrivaju oblike koji su bitni za ponovnu upotrebu: cfParagraph, cfHeading sa HeadingLevel, cfListItem, cfTableCell, cfCaption, cfFigure i fallback za neoznačeno cfPlain
Polje Source beleži odakle je svaka klasifikacija potekla, rosStructure za stablo strukture i rosHeuristic za zaključivanje, a to je polje koje treba logovati kada odlučujete koliko duboko da verujete pipeline-u za izvlačenje kroz skup dokumenata. Figure su poseban slučaj koji vredi znati: za blok cfFigure tekst dolazi iz alternativnog opisa, a ne iz bilo kojih glifova, pošto figura nema sopstvene karaktere. Nespojen alternativni tekst se i dalje predstavlja umesto da bude odbačen, što omogućava reviziji pristupačnosti da vidi da opis postoji čak i kada ga ništa na stranici ne crta. Sam model označavanja pokriven je u validaciji stabla strukture PDF/UA
Rasponi nose stilizovanje i poreklo
Svaki TPdfStructuredTextSpan nosi svoj tekst, svoje granice u prostoru stranice, FontName, FontSize, FontWeight i Angle, plus SourceStartIndex i SourceCharacterCount. Rasponi se prekidaju tamo gde se stilizovanje menja, tako da rečenica sa tri podebljane reči postaje tri raspona, a rekonstrukcija isticanja u HTML-u ili Markdown-u postaje pitanje čitanja svojstava, a ne pogađanja na osnovu imena fontova
Ta dva polja izvornog indeksa su ona koja pretvaraju izvlačenje u funkciju, a ne u izveštaj. Ona pokazuju nazad u sekvencu karaktera stranice, što znači da blok koji ste poklopili u pretrazi može da se pretvori u geometriju selekcije na nivou karaktera ili u pravougaonik za isticanje bez drugog, drugačije poređanog prolaska kroz tekst; mehanika je opisana u vizuelnoj selekciji linije teksta pomoću kutija karaktera. Polje Angle je važnije nego što deluje: rotiran tekst u pečatu ili vodenom žigu sleti u isti koordinatni prostor kao i telo teksta, a pipeline koji ignoriše ugao rado će spojiti dijagonalan „DRAFT” usred pasusa
Budžet, i dva brojača kvaliteta
MaxCharacters je fail-closed budžet, a ne podešavanje za skraćivanje: stranica koja ga premaši se zaustavlja umesto da tiho vrati deo sadržaja. Na putanji nepouzdanog prijema to je ponašanje koje želite, jer je stranica sa milion karaktera ili mašinski generisano čudovište ili pokušaj da vaš ekstraktor postane najsporiji deo sistema
Dva brojača na vraćenoj stranici direktno opisuju kvalitet izvlačenja. UnmappedCharacterCount broji karaktere bez upotrebljivog Unicode mapiranja, što je klasičan simptom podskupljenog fonta ugrađenog bez /ToUnicode CMap-a; takav tekst se renderuje savršeno, a izvlači se kao ništa korisno. GeometryFailureCount broji karaktere čija granična kutija nije mogla da se odredi, što degradira redosled fizičkog rasporeda. Logujte oba. Skup dokumenata kod koga su ti brojevi dosledno blizu nule može se indeksirati sa poverenjem, a onaj kod koga nisu govori vam da nekim proizvođačima u vašem pipeline-u treba pažnja pre nego što ijedan nizvodni rezultat postane pouzdan
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;
Performanse na stvarnim stranicama
Izvlačenje sa fizičkim rasporedom je skup režim, a implementacija je izgrađena za stranice koje su zaista velike: redosled karaktera se izvršava u O(n log n) umesto ponovljenim skeniranjem, baferi redova i raspona rastu geometrijski umesto da se realociraju po karakteru, Unicode tekst se gradi u baferima umesto konkatenacijom niski, a pretrage fontova za susedne tekstualne objekte se keširaju. Ta kombinacija je ono što drži gustu stranicu od 5.000 karaktera predvidljivom, a ne kvadratnom
Za posao intenzivan brojem stranica i dalje vredi birati jeftiniji režim gde god možete. Koristite roContentOrder sa uključenom semantikom za označene dokumente kojima verujete, a rezervišite roPhysicalLayout za skeniran i zastareo materijal gde je geometrija jedini signal. Ako vam je potrebna samo obična niska, jednostavniji API opisan u izvlačenju teksta iz PDF dokumenata ostaje brža putanja, a kada treba da pratite tekst nazad do identifikatora obeleženog sadržaja, čitanje i pisanje BDC i MCID obeleženog sadržaja pokriva taj sloj
Model blokova se takođe čisto preslikava na ono što žele pipeline-ovi za pretragu (retrieval): naslov sa svojim pasusima je deo (chunk) sa naslovom, a granice omogućavaju da citat pokazuje na lokaciju na stranici, a ne na dokument. PDFiumPas je Delphi i Lazarus komponenta oko PDFium mehanizma, dokumentovana sa primerima na stranici PDFium Delphi komponente