PDFium Component izvlačenje tablica, od verzije 3.117.0, tretira tanki ispunjeni pravokutnik kao liniju okvira tablice. Uz uključen DetectFilledRulings, što je zadano, ispunjeni axis-aligned box koji nije deblji od MaxRulingThickness (3 točke) postaje jedna linija duž svoje duge osi, veći ispunjeni box doprinosi svoja četiri ruba, a svaka koordinata linije poravnava se unutar RulingSnapTolerance (4 točke) prije nego se mreža sastavi. Tablice izvezene iz Worda, Google Docsa i preglednika stoga stižu do detektora crtanih mreža kao potpune mreže, umjesto da padnu na whitespace detekciju kao fragmenti
Raniji članak o detekciji i izvlačenju tablica tvrdio je da detekcija crtanih mreža koristi iscrtane linije i da se svaki segment crtane putanje transformira u koordinate stranice. Ta rečenica bila je točna i nepotpuna. Brojanje path objekata na skupu od 13 stvarnih uzoraka dokumenata pokazalo je da njih 9 ne sadrži nikakvu crtanu putanju, a svaka njihova stranica nosi stotine ispunjenih pravokutnika debljine 0.5 do 1 točke. Detektor koji je vidio samo stroke nije vidio ništa, svaka je stranica pala na whitespace detekciju, a izlaz je bio raspršeni skup malih fragmenata umjesto tablica. Preset kompaktnih stupaca dodan u 3.116.4 ublažio je to na razini fragmenata; korijenski uzrok bio je to što je detektor čitao pogrešan operator iscrtavanja
Zašto tablica izvezena iz Worda nema crtane linije?
Procesor teksta ne misli o obrubu kao o liniji; misli o njemu kao o kutiji s određenom širinom i tu kutiju iscrtava ispunom. ISO 32000-1 §8.5.2.1 definira operator re kao dodavanje podputanje pravokutnika, a §8.5.3 razdvaja operatore iscrtavanja: S crta putanju trenutnom širinom linije, f ispunjava njenu unutrašnjost. Obrub ćelije od 0.5 točaka izlazi kao x y w 0.5 re f, a stroj za stroke, uključujući širinu linije, spojeve i dash pattern, nikad se ne pokrene. Sjenčanje ćelije ista je konstrukcija s većom kutijom. Crtana mreža nacrtana s m, l i S je ono što je izvorni detektor očekivao, i ono što gotovo ništa izvezeno iz uredske aplikacije ne proizvodi:
% jedan obrub ćelije iz izvaza procesora teksta: ispunjena kutija visine 0.5 pt
72 700 468 0.5 re f
% sjenčanje ćelije: ispunjena kutija veličine ćelije
72 676 117 24 re f
% crtana linija mreže za koju je izvorni detektor bio napisan
72 700 m 540 700 l S
Za detektor koji FPDFPath_GetDrawMode pita samo je li zastavica stroke postavljena, obje ispunjene kutije su nevidljive. Riječi unutar ćelija zatim stižu do whitespace detekcije, gdje stupci odvojeni prazninom od 6 točaka sjede ispod zadanog MinColumnGap od 12 točaka, i ono što se vrati je bilo koji podskup redaka koji se slučajno dovoljno dobro poravna da prođe MinRows. To je ponašanje fragmenta, i nikakvo podešavanje parametara ne pretvara ga u mrežu koju je autor nacrtao
Kako PDFium Component pretvara ispunjenu kutiju u liniju?
TableCollectObjectRulings pregledava svaki path objekt podputanju po podputanju. Draw mode dolazi iz FPDFPath_GetDrawMode; putanja se računa kao ispunjena kada je DetectFilledRulings uključen i fill mode nije none. Svaka točka transformira se kroz matricu objekta i prikuplja, do MaxSubpathPoints (8) po podputanji, a svaki segment krivulje označava podputanju kao zakrivljenu. Kada se podputanja zatvori ili započne novi MoveTo, FlushSubpath odlučuje što je bila: zakrivljena podputanja se odbacuje, kao i svaki zatvoreni poligon čije točke ne sjede sve unutar PointTolerance (0.05 točaka) od rubova bounding boxa na barem jednoj osi. Trokut, chevron ili zaobljeni tab nikad ne postaje linija, što i drži dekorativnu grafiku izvan mreže
Ono što preživi je axis-aligned pravokutnik, klasificiran po svom bounding boxu. Širina na razini MaxRulingThickness ili ispod nje s visinom iznad daje jednu okomitu liniju na vodoravnom središtu, premošćujući kutiju odozdo do vrha; zrcalni slučaj daje jednu vodoravnu liniju. Obje dimenzije iznad praga znače sjenčanu ćeliju, i kutija doprinosi četirima linijama, po jednu za svaki rub. Obje dimenzije na pragu ili ispod njega ne doprinose ničemu, pa se kvadrat od 2 točke ne zamijeni za liniju. Crtana putanja ide starijom rutom kroz AddLine, jedna linija po axis-aligned segmentu, pa se mreža nacrtana s S obrađuje točno kao prije, a putanja iscrtana i ispunom i strokeom proizvodi preklapajuće dijelove koje prolaz spajanja sklapa:
uses
PDFium;
var
Pdf: TPdf;
Options: TPdfTableExtractionOptions;
Tables: TPdfTables;
Mode: string;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'itinerary-from-word.pdf';
Pdf.LoadDocument;
Pdf.PageNumber := 1; // od 1
Options := TPdfTableExtractionOptions.Default;
// ovo su zadane vrijednosti 3.117.0, navedene radi jasnoće
Options.DetectFilledRulings := True; // tanke ispunjene kutije postaju linije
Options.MaxRulingThickness := 3.0; // točke; deblje kutije računaju se kao sjenčanje
Options.RulingSnapTolerance := 4.0; // točke; 0 isključuje poravnavanje
Options.IncludeFormXObjects := True;
Tables := Pdf.ExtractTables(Options);
for I := 0 to High(Tables) do
begin
if Tables[I].DetectionMode = ptdmRuled then
Mode := 'ruled'
else
Mode := 'whitespace';
Writeln(Format('%dx%d %s, confidence %.2f',
[Tables[I].RowCount, Tables[I].ColumnCount, Mode,
Tables[I].Confidence]));
end;
finally
Pdf.Free;
end;
end;
Što RulingSnapTolerance radi za tablice sa sjenčanim ćelijama?
RulingSnapTolerance je ono što tablicu sastavljenu samo od sjenčanja spaja u jednu mrežu. Neki izvazi ne crtaju nikakav obrub: svaka ćelija je ispunjena kutija u svojoj boji, a susjedne kutije razdvojene su prazninom od 1 do 3 točke. Svaka kutija daje četiri rubne linije, ali desni rub jedne ćelije i lijevi rub sljedeće sjede 2 točke jedan od drugoga, a test povezanosti koristi RulingTolerance, koji je po defaultu 1 točka. Bez poravnavanja, svaka ćelija tvori vlastitu povezanu komponentu od četiri linije, nijedna komponenta ne dosegne MinRows, i stranica ne prijavljuje ništa. TableSnapRulings prikuplja svaku X koordinatu u igri (položaj svake okomite linije plus početak i kraj svake vodoravne) i svaku Y koordinatu jednako, sortira svaku listu, klasterira je ulančavajući vrijednosti čiji se susjed razlikuje za najviše toleranciju, zamjenjuje svaki klaster njegovom sredinom, a zatim pomiče svaki položaj, početak i kraj na središte najbližeg klastera. Dvije strane praznine postaju ista linija, i povezanost se održava
Poravnavanje se izvršava prije TableMergeRulings, koji sortira linije i spaja kolinearne dijelove koji se dodiruju ili preklapaju unutar RulingTolerance, i oba se izvršavaju prije nego TableDetectRuled uopće vidi podatke, pa je provjera povezanosti po parovima proporcionalna broju linija mreže, a ne broju fragmenata po ćeliji. Na crtanoj mreži prolazi su bezopasni, jer koordinate koje su već bile identične poravnavaju se same sa sobom. Jedno je vrijedno imati na umu: klasteriranje ulančavanjem nema vlastito ograničenje širine, pa se niz koordinata udaljenih po 3 točke sklapa u jedno središte. Uz zadane 4 točke to pogađa samo stupce uže od jednog znaka, ali ako dokument ima stvarne praznine od 3 točke koje moraju ostati odvojene, snizite toleranciju ili je postavite na 0 da isključite poravnavanje:
// Izoliraj strategiju crtanih mreža i usporedi što koja postavka vidi na jednoj stranici
function CountRuledTables(Pdf: TPdf; FilledRulings: Boolean;
SnapTolerance: Double): Integer;
var
Options: TPdfTableExtractionOptions;
begin
Options := TPdfTableExtractionOptions.Default;
Options.DetectWhitespaceTables := False;
Options.DetectFilledRulings := FilledRulings;
Options.RulingSnapTolerance := SnapTolerance;
Result := Length(Pdf.ExtractTables(Options));
end;
// Word izvaz obično prijavljuje 0, N i zatim manje od N:
// samo stroke ne vidi ništa, poravnavanje povezuje sjenčane ćelije,
// a isključivanje poravnavanja ostavlja svaku sjenčanu ćeliju kao vlastiti otok
Writeln(CountRuledTables(Pdf, False, 4.0));
Writeln(CountRuledTables(Pdf, True, 4.0));
Writeln(CountRuledTables(Pdf, True, 0.0));
Linije unutar form XObjecta
Alati za page layout često zamataju tablicu, ili cijelo tijelo stranice, u form XObject i iscrtavaju ga s Do. ISO 32000-1 §8.10.1 propisuje da se matrica forma konkatenira s trenutnom matricom transformacije kada se form iscrtava, pa pravokutnik unutar forma živi u form prostoru i slijeće na stranicu tek nakon dvije ili više transformacija. TableCollectObjectRulings rekurzivno ulazi u form objekte kada je IncludeFormXObjects postavljen: čita matricu objekta, kombinira je s roditeljskom matricom kroz TableMultiplyMatrix, čiji redoslijed argumenata znači "preslikaj kroz prvu matricu, zatim kroz drugu", i nabraja djecu s FPDFFormObj_CountObjects i FPDFFormObj_GetObject, prosljeđujući kombiniranu matricu dalje. Ugnježđivanje dublje od MaxFormDepth (8) tiho se preskače, što je ograda protiv patoloških datoteka, a ne ograničenje kojem se približava ijedan stvarni izvaz. Razlog zašto redoslijed množenja ima značenje isti je onaj obrađen u prepend naspram append kod matrica: zamjena operanda pomiče član translacije, i linija koja bi trebala sletjeti na vrh stranice slijeće u ishodište
Zašto se budžet linija učetverostručio?
Zadani MaxRulingSegments porastao je s 4096 na 16384 u 3.117.0 jer obrubi po ćelijama dolaze u puno većem broju od crtanih linija mreže. Crtana tablica od 30 redaka i 6 stupaca je 38 segmenata linija. Ista tablica izvezena kao ispunjene kutije je do četiri obruba po ćeliji, 720 dijelova prije spajanja, a obrazac sa sjenčanim ćelijama to udvostručuje. Dvije takve tablice na stranici iscrpile bi stari budžet. Budžet se provodi u TableAppendRuling kroz Check, koji diže EPdfError s porukom "Table ruling-segment budget exceeded"; nema degradiranog rezultata, nema djelomične mreže, a ni whitespace prolaz se ne izvršava. Ako za nepouzdan ulaz postavite vlastiti čvršći budžet, uhvatite iznimku i odlučite, umjesto da prazan rezultat čitate kao "nema tablica":
Options := TPdfTableExtractionOptions.Default;
Options.MaxRulingSegments := 2048; // namjerno tijesno za nepouzdan ulaz
try
Tables := Pdf.ExtractTables(Options);
except
on E: EPdfError do
begin
Log(E.Message); // 'Table ruling-segment budget exceeded'
Options.MaxRulingSegments := 16384; // zadano u 3.117.0
Tables := Pdf.ExtractTables(Options);
end;
end;
Izmjereni rezultati i gdje se pristup zaustavlja
Na istih 13 uzoraka, izvlačenje je prešlo s 43 tablice, od kojih 9 crtanih i 34 whitespace fragmenta ili lažnih pozitiva, na 41 crtanu tablicu i nijedan whitespace lažni pozitiv. Dio tog čišćenja pripada dvjema popratnim promjenama u 3.117.0: riječi koje je crtana mreža već prisvojila uklanjaju se prije nego se pokrene whitespace detekcija, pa se tablica nikad ne prijavi dvaput, a granica whitespace stupca sada mora biti koridor bez teksta preko svakog retka koji razdvaja, što je zaustavilo da se obostrano poravnati odlomci boduju kao tablice 5x4. Čitač ispunjenih pravokutnika ono je što je same tablice premjestilo iz stupca fragmenata u stupac crtanih tablica
Granice valja reći otvoreno. Stranica bez tekstualnog sloja i dalje daje kostur mreže, sa svakom ćelijom praznom, jer linije dolaze iz geometrije, a tekst iz tekstualne stranice; skenirane stranice trebaju prvo OCR. Ispunjeni oblici s krivuljama, zaobljenim kutovima ili nepravokutnim obrisima odbacuju se u cijelosti, pa tablica čiji su obrubi nacrtani kao obrisi zaobljenih pravokutnika treba whitespace detekciju kao i prije. Tablica bez obruba i bez sjenčanja nije promijenjena ničim od ovoga i ostaje domena whitespace strategije opisane u članku o izvlačenju tablica; kada ni to nije dovoljno, word boxovi i blokovi iz strukturiranog teksta i redoslijeda čitanja sirovina su za čitač specifičan za domenu. Demo TableExtractionLab koji se isporučuje s komponentom izlaže DetectFilledRulings u svom panelu opcija, što je najbrži način da vidite kako neki izvaz izgleda s tom opcijom i bez nje; puni API opisan je na stranici PDFium Component za Delphi