PDFium Component zazna tabele na strani PDF in jih vrne kot mrežo celic z razponi vrstic in stolpcev, glavnimi vrsticami in vrednostjo zaupanja, prek ExtractTables za eno stran in ExtractDocumentTables za celoten dokument. Vsaka tabela se z enim klicem pretvori v CSV ali JSON, tabele, ki se nadaljujejo čez prelom strani, pa je mogoče povezati v verigo nadaljevanja
PDF nima objekta tabele. Tabela v PDF je nabor besedilnih nizov, postavljenih tako, da jih človek bere kot mrežo, včasih z okoli njih narisanimi črtami, pogosto pa brez njih. Obnavljanje mreže pomeni rekonstrukcijo namena, ki ga datoteka nikoli ni zabeležila, zato vsako orodje za izvlečenje ustvari nekoliko drugačne rezultate in zakaj je orodje, ki vam pove svoje zaupanje, uporabnejše od tistega, ki tega ne stori
Dva načina zaznavanja za dve vrsti tabel
Zaznavanje s črtami uporablja narisane črte. Vsak obrobljen segment poti se prek matrike objekta strani preslika v koordinate strani, vodoravne in navpične črte se sekajo, presečišča pa tvorijo povezane komponente. Vsaka komponenta postane svoja urejena mreža položajev X in Y, kar preprečuje, da bi se dve ločeni tabeli na isti strani združili v eno nesmiselno mrežo
Zaznavanje s presledki obravnava tabele, narisane s poravnavo namesto s črtami. Okvirji besed so združeni v vizualne vrstice, vrzeli znotraj vrstice jo razdelijo v kandidatne stolpce, tabela pa je sprejeta samo takrat, ko se vsaj MinRows vrstic ponovi z vsaj MinColumns levo poravnanimi sidrišči znotraj AlignmentTolerance. Faktor vrzeli med vrsticami je privzeto 3, kar pokrije približno 30-točkovni razmik osnovnih črt, tipičen za 12-točkovno besedilo, ne da bi eni sami vrstici z več besedilnimi nizi dovolil, da se izdaja za tabelo
uses
PDFium;
var
Pdf: TPdf;
Options: TPdfTableExtractionOptions;
Tables: TPdfTables;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'annual-report.pdf';
Pdf.LoadDocument;
Pdf.PageNumber := 12; // od 1 naprej
Options := TPdfTableExtractionOptions.Default;
Options.DetectRuledTables := True;
Options.DetectWhitespaceTables := True;
Options.MinConfidence := 0.6; // privzeto je 0,5
Options.HeaderRowCount := 1;
Tables := Pdf.ExtractTables(Options);
for I := 0 to High(Tables) do
Writeln(Format('table %d: %dx%d cells, confidence %.2f, mode %d',
[I, Tables[I].RowCount, Tables[I].ColumnCount,
Tables[I].Confidence, Ord(Tables[I].DetectionMode)]));
if Length(Tables) > 0 then
SaveText('page12-table0.csv', Tables[0].ToCsv);
finally
Pdf.Free;
end;
end;
Kako se obnovijo združene celice?
To je del, kjer naivni izvlekalniki zgrešijo. Združene celice ni mogoče identificirati samo iz globalne mreže, ker je mreža izpeljana iz vseh črt na strani, združenemu območju pa preprosto manjka notranja črta, ki bi ga ločila
Pravilo, uporabljeno tukaj, je lokalno: dve sosednji osnovni celici sta združeni, kadar nobena mejna črta ne pokriva intervala med njima. Union-find ju poveže, nastali pravokotni komponenti postaneta vrednosti RowSpan in ColumnSpan, besedilo pa je dodeljeno osnovni celici po njeni sredinski točki in nato sledi tej celici do njenega korena združevanja. Tako narejeno tudi ohranja strošek linearen v besedah plus celicah, namesto kvadratnega pregleda, ki bi ga dobili s testiranjem vsake besede proti vsaki celici
Praktičen učinek je, da finančna tabela z združeno glavo "Total", ki obsega tri stolpce, pride ven kot ena celica z razponom tri, namesto ene zapolnjene celice in dveh skrivnostnih praznih
Nadaljevanje čez strani
Dolge tabele se prelomijo čez strani, obravnavanje odlomka vsake strani kot neodvisne tabele pa klicatelja sili, da jih sešije. ExtractDocumentTables jih lahko namesto tega poveže, vendar samo pod strogimi pogoji: odlomek mora biti najnižja tabela na zgodnejši strani, naslednji mora biti najvišja tabela na naslednji strani, številke strani morajo biti sosednje, meje stolpcev pa se morajo ujemati
Vsi štirje pogoji skupaj preprečujejo očitno napako, ki je verižiti vsako štiristolpčno tabelo v dokumentu v eno namišljeno mega-tabelo, ker si naključno delijo število stolpcev. Kadar pogoji držijo, si tabele delijo identifikator skupine nadaljevanja in nosijo metapodatke nadaljevanja; kadar ne držijo, dobite ločene tabele in se lahko odločite sami
Izvlečenje na ravni dokumenta si deli proračuna MaxCells in MaxTables čez strani namesto da bi ju ponastavil za vsako stran, aktivno stran pa obnovi v bloku finally, tako da zagon izvlečenja v pregledovalniku uporabnika pusti gledati stran, na kateri je bil
Izvoz brez poškodovanja podatkov
Oba izvoznika sta premišljena glede ubežnih zaporedij. CSV vedno postavi polja v narekovaje in podvoji notranje narekovaje, kar prepreči klasično napako, kjer celica z vejico tiho postane dva stolpca. Pri združenih celicah je vsebina izdana samo na zgornjem levem sidrišču, tako da krožna pot čez CSV ne podvoji razpenjajoče se glave čez stolpce, ki jih pokriva
JSON ohrani Unicode namesto da bi ga izpustil v ASCII, ubeži kontrolne znake in vključuje metapodatke, ki jih porabnik potrebuje za presojo kakovosti: način zaznavanja, zaupanje, meje, vrednosti razpona, zastavice glave in podatke o nadaljevanju. Če izvlečene tabele vnašate v poznejši sistem, dajte prednost JSON, ker vam vrstica CSV ne more povedati, da je tabela, iz katere prihaja, dosegla zaupanje 0,51:
// Izvlečenje za celoten dokument, obdrži samo tabele, vredne zaupanja
Tables := Pdf.ExtractDocumentTables(Options);
for I := 0 to High(Tables) do
begin
if Tables[I].Confidence < 0.75 then
begin
Log(Format('page %d table needs review (%.2f)',
[Tables[I].PageNumber, Tables[I].Confidence]));
Continue;
end;
if Tables[I].ContinuationGroup > 0 then
AppendToGroup(Tables[I].ContinuationGroup, Tables[I].ToJson)
else
EmitStandalone(Tables[I].ToJson);
end;
Prilagajanje in vedeti, kdaj nehati
Tri nastavitve so pomembnejše od ostalih. MinConfidence je vrata kakovosti, 0,5 pa je namerno prizanesljiva; povečajte jo za avtomatizirano zajemanje in znižajte za pregledni vmesnik, kjer človek potrdi vsak rezultat. MinColumnGap odloči, kaj šteje za mejo stolpca v načinu s presledki, in tesno postavljene tabele v gostih poročilih jo morda potrebujejo zmanjšano od privzete vrednosti 12 točk. MaxRowGapFactor odloči, kdaj navpična razdalja konča tabelo, kar je pomembno pri tabelah z občasnimi praznimi vrsticami
Bodite pošteni glede omejitev. Tabele s črtami se izvlečejo zanesljivo. Čisto poravnane tabele s presledki se izvlečejo dobro. Tabele z zasukanim besedilom, gnezdenimi tabelami ali celicami, katerih vsebina se prelomi v nekaj, kar je videti kot druga vrstica, bodo potrebovale pregled ne glede na to, kako so nastavljeni parametri. Zanje strukturirani besedilni model da surovino za gradnjo bralnika, specifičnega za domeno, opisano v članku strukturirani bloki besedila in vrstni red branja
Ena uporabna kombinacija: kadar skeniran dokument sploh nima besedila, zaznavanje tabel nima s čim delati, dokler ne obstaja besedilna plast. Najprej jo dodajte, kot je opisano v članku dodajanje preiskovalne besedilne plasti skeniranim PDF, nato izvlecite. Okvirji besed, ki jih vrne ponudnik OCR, so natanko tak vhod, kot ga zaznavanje s presledki potrebuje
Izvlečenje tabel, strukturirano besedilo in preoblikovanje toka vsi berejo iz istega modela strani v Delphiju, C++Builderju in Lazarusu; celoten API je opisan na strani PDFium Component for Delphi