PDFiumPas vraća tekst stranice kao strukturu, a ne kao niz znakova. GetStructuredText proizvodi TPdfStructuredTextPage koji sadrži blokove, svaki od kojih sadrži retke, svaki od kojih sadrži stilizirane tekstualne odsječke, s granicama u prostoru stranice na svakoj razini i sačuvanim indeksima izvornih znakova, tako da se svaki fragment može mapirati natrag na temeljnu tekstualnu stranicu
Izdvajanje u obliku ravnog niza znakova, s kojim većina koda počinje, i dalje postoji te je i dalje ispravno za svoju namjenu. Prestaje biti dovoljno u trenutku kada trebate znati koje su riječi bile naslov, koje su pripadale lijevom stupcu, ili gdje se na stranici zapravo nalazi podudaranje
Zašto je ravni niz znakova pogrešan izlaz za većinu zadataka?
Zato što su pitanja koja ljudi postavljaju o izdvojenom tekstu gotovo nikad „koji su znakovi na ovoj stranici”. Ona su „koji je naslov”, „je li ovo tablica”, „pripada li ovaj odlomak poglavlju 4”, „gdje trebam nacrtati istaknuti dio”. Jedan niz znakova ne odgovara ni na jedno od toga, a svaki odgovor koji iz njega rekonstruirate heuristika je koju sada posjedujete
Dvostupčani rasporedi to čine konkretnim. Izdvojite dvostupčani članak kao niz znakova i, ovisno o tome kako je proizvođač napisao tok sadržaja, možete dobiti prvi stupac praćen drugim stupcem, ili možete dobiti prvi redak prvog stupca, prvi redak drugog stupca, drugi redak prvog stupca, i tako redom niz stranicu. Oboje proizlazi iz sukladnog PDF-a. Nijedno nije pogrešno na razini formata, jer PDF opisuje oznake na stranici, a ne strukturu dokumenta. Model temeljen na blokovima omogućuje ekstraktoru da eksplicitno donese odluku o redoslijedu i kaže vam koju je odluku donio
Redoslijed sadržaja ili fizički raspored?
TPdfStructuredTextOptions.ReadingOrder bira između roContentOrder i roPhysicalLayout, a ispravan odgovor ovisi o tome čemu više vjerujete, proizvođaču ili geometriji
Redoslijed sadržaja vraća tekst redoslijedom kojim ga tok sadržaja crta. To je brzo, a za dokumente koje generira pristojan proizvođač, obično i namijenjeni redoslijed čitanja. Fizički raspored ignorira redoslijed toka i rekonstruira redoslijed iz stvarnog položaja znakova, grupirajući ih u retke, a zatim u stupce. To je ono što želite za skenirane pa OCR-om obrađene stranice, za izlaz alata koji emitiraju tekst redoslijedom fonta, a ne redoslijedom čitanja, i za sve gdje je vizualni rezultat jedino na što se možete osloniti
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 proračun
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;
Što označavanje dodaje, a geometrija ne može?
Namjeru. Kada je IncludeSemantics uključen, blokovi iz označenog PDF-a nose Kind izveden iz stabla strukture, pa je naslov naslov zato što je to proizvođač tako rekao, a ne zato što mu je font bio veći od prosjeka. Vrste pokrivaju oblike koji su bitni za ponovnu upotrebu: cfParagraph, cfHeading s HeadingLevel, cfListItem, cfTableCell, cfCaption, cfFigure i fallback za neoznačeno cfPlain
Polje Source bilježi odakle je svaka klasifikacija došla, rosStructure za stablo strukture i rosHeuristic za zaključivanje, a to je polje koje treba bilježiti kada odlučujete koliko vjerovati pipelineu za izdvajanje kroz skup dokumenata. Slike su poseban slučaj vrijedan poznavanja: za blok cfFigure tekst dolazi iz alternativnog opisa, a ne iz bilo kojih glifova, budući da slika nema vlastitih znakova. Nepovezan alternativni tekst i dalje je prikazan umjesto da bude odbačen, što reviziji pristupačnosti omogućuje da vidi da opis postoji čak i kada ga ništa na stranici ne crta. Sam model označavanja obrađen je u članku validacija stabla strukture PDF/UA
Odsječci nose stiliziranje i porijeklo
Svaki TPdfStructuredTextSpan sadrži svoj tekst, svoje granice u prostoru stranice, FontName, FontSize, FontWeight i Angle, plus SourceStartIndex i SourceCharacterCount. Odsječci se prekidaju ondje gdje se stiliziranje mijenja, pa rečenica s tri podebljane riječi postaje tri odsječka, a rekonstrukcija naglaska u HTML-u ili Markdownu postaje pitanje čitanja svojstava, a ne pogađanja iz imena fontova
Ta su dva polja izvornog indeksa ono što izdvajanje pretvara u značajku, a ne u izvještaj. Pokazuju natrag u niz znakova stranice, što znači da se blok koji ste pronašli u pretrazi može pretvoriti u geometriju odabira na razini znaka ili pravokutnik isticanja bez drugog, drukčije uređenog prolaska kroz tekst; mehanika je opisana u članku vizualni odabir teksta u retku pomoću okvira znakova. Polje Angle znači više nego što izgleda: rotirani tekst u pečatu ili vodenom žigu slijeće u isti koordinatni prostor kao i osnovni tekst, a pipeline koji ignorira kut sasvim će spremno spojiti dijagonalni „DRAFT” usred odlomka
Proračun, i dva brojača kvalitete
MaxCharacters je fail-closed proračun, a ne postavka skraćivanja: stranica koja ga premaši zaustavlja se umjesto da tiho vrati dio sadržaja. Na putanji prihvata nepouzdanih datoteka to je ponašanje koje želite, jer je stranica s milijun znakova ili strojno generirano čudovište ili pokušaj da vaš ekstraktor postane najsporiji dio sustava
Dva brojača na vraćenoj stranici izravno opisuju kvalitetu izdvajanja. UnmappedCharacterCount broji znakove bez upotrebljivog Unicode mapiranja, što je klasičan simptom podskupa fonta ugrađenog bez /ToUnicode CMap-a; takav se tekst savršeno renderira, a izdvaja se kao potpuno beskorisno. GeometryFailureCount broji znakove čiji se granični okvir nije mogao odrediti, što narušava redoslijed fizičkog rasporeda. Bilježite oba. Skup dokumenata kod kojeg su ti brojevi dosljedno blizu nule može se indeksirati s povjerenjem, a onaj kod kojeg nisu govori vam da nekim proizvođačima u vašem pipelineu treba pažnja prije nego što ijedan rezultat nizvodno 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
Izdvajanje s fizičkim rasporedom skuplji je način rada, a implementacija je izgrađena za stranice koje su stvarno velike: uređivanje znakova izvodi se u O(n log n) umjesto ponovljenim skeniranjem, međuspremnici redaka i odsječaka rastu geometrijski umjesto da se realociraju po svakom znaku, Unicode tekst gradi se u međuspremnicima umjesto konkatenacijom nizova, a pretrage fontova za susjedne tekstualne objekte predmemoriraju se. Ta kombinacija drži gustu stranicu od 5000 znakova predvidljivom, a ne kvadratnom
Za zadatak s velikim brojem stranica i dalje se isplati birati jeftiniji način rada gdje god možete. Koristite roContentOrder uz uključenu semantiku za označene dokumente kojima vjerujete, a roPhysicalLayout rezervirajte za skenirani i naslijeđeni materijal gdje je geometrija jedini signal. Ako vam treba samo obični niz znakova, jednostavniji API opisan u članku izdvajanje teksta iz PDF dokumenata ostaje brži put, a kada trebate pratiti tekst natrag do identifikatora označenog sadržaja, čitanje i pisanje BDC i MCID označenog sadržaja pokriva taj sloj
Model blokova također se čisto preslikava na ono što pipelineovi za dohvat žele: naslov sa svojim odlomcima jest komad teksta s naslovom, a granice omogućuju da citat pokazuje na lokaciju na stranici, a ne na cijeli dokument. PDFiumPas je Delphi i Lazarus komponenta izgrađena oko PDFium engine-a, dokumentirana s primjerima na stranici PDFium Delphi komponente