HotPDF obnovi tabele iz obstoječega PDF-ja prek ExtractLoadedTypedTables, API-ja za Delphi, ki združi delce vrstic, ki jih izdela prehod postavitve, za vsako tabelo zgradi eno kanonično mrežo stolpcev, jo nadaljuje čez prelom strani, ko geometrija to podpira, in vrne vsako celico kot tipizirano vrednost z izvorom strani, razponom stolpcev in mejami. ExportLoadedTypedTables isti rezultat zapiše neposredno v CSV ali JSON. Primer, zaradi katerega je to vredno zgraditi, je dolgočasen in zelo pogost. Register računov na štiridesetih straneh je logično ena tabela, natisnjena s ponovljeno glavo na vrhu vsake strani. Nad njim izvedite naivni prehod vrstnega reda branja in dobite štirideset tabel, devetintrideset lažnih vrstic glav ter stolpec valut, ki se pri vsaki vrstici, kjer je srednja celica po naključju prazna, premakne za eno mesto v levo. Čiščenje tega v nadaljevanju, znotraj klicne aplikacije, je kraj, kjer projekti uvoza dokumentov umirajo
Zakaj vam stran PDF izroči delce namesto tabele?
Ker stran PDF nima prav nobene semantike tabel, razen če je dokument označen. Tok vsebine vsebuje operatorje za prikaz besedila in matrike položajev (ISO 32000-1 §9.4.3) in nič več; obrobljeno polje, ki ga vidite na zaslonu, je nepovezano risanje poti, ki ga noben izločevalnik ni dolžan povezati z besedilom. Tipi elementov strukture Table, TR, TH in TD živijo samo v hierarhiji logične strukture označenega PDF-ja (ISO 32000-1 §14.8.4), velika večina poslovnih dokumentov v obtoku pa ni označena. Vse, kar je opisano spodaj, je geometrijska obnova, ne razčlenjevanje, in to je vredno povedati na glas, preden kdo na tem zgradi poročilo usklajevanja
HotPDF zato najprej izvede semantično analizo postavitve nad izločenimi glifi, isti prehod, ki podpira izločanje besedila po vrstnem redu strukture iz naloženega PDF-ja in strukturirane izvoze HTML ter XML. Ta prehod osnovnice združi v poteze, katerih celice se navpično poravnajo, nadaljeval pa bo potezo samo, dokler imajo zaporedne vrstice enako število celic. Za mehanizem postavitve je to pravilo pravilno in poceni. Za klicatelja je napačne oblike: ena vrstica s prazno notranjo celico razdeli eno vizualno tabelo na dve izvorni tabeli. Plast tipiziranih tabel je nad tem prehodom prav zato, da dele sestavi nazaj
Kanonične mreže stolpcev in nastavitev ColumnTolerance
ExtractLoadedTypedTables najprej združi delce na isti strani, še preden naredi karkoli drugega, in jih združi po geometriji stolpcev, ne po besedilu vrstic. Dve sosednji izvorni tabeli na eni strani se združita, ko imata obe vsaj dva stolpca, ko navpična vrzel med zadnjo vrstico prve in prvo vrstico druge ostane znotraj pasu tolerance in ko se začetni položaji stolpcev poravnajo. Začetki stolpcev znotraj ColumnTolerance se zložijo v en kanonični stolpec, ob združevanju pa se povprečijo. Privzeta toleranca je 12 enot uporabniškega prostora, kar ustreza običajni poslovni tipografiji, za široko razmaknjene ali globoko zamaknjene postavitve pa jo je smiselno povečati
Pomemben je del o vrstici, ki nima notranje vrednosti. HotPDF vsako celico pripne na najbližji kanonični začetek stolpca, nato pa ColumnSpan nastavi na razdaljo od tega stolpca do naslednjega zasedenega, namesto da bi preostale celice premaknil v levo. Vrstica s tremi celicami v mreži petih stolpcev ohrani vrednosti pod pravilnimi naslovi in natančno zabeleži, kje so vrzeli. To je razlika med tabelo, ki jo lahko uskladite, in tako, ki tiho pripiše denar napačni postavki
var
Pdf: THotPDF;
Options: THPDFTypedTableExtractionOptions;
Tables: THPDFTypedTables;
Info: THPDFTypedTableExtractionInfo;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('register.pdf', '') <= 0 then
Exit;
Options := THPDFTypedTableExtractionOptions.Default;
Options.ColumnTolerance := 12; // enote uporabniškega prostora
Options.MinimumTableConfidence := 0.55; // pod tem se tabele zavržejo
Options.DateOrder := ttdoDMY; // 03/04/2026 je 3. april
Options.DecimalSeparator := ',';
Options.ThousandsSeparator := '.';
if Pdf.ExtractLoadedTypedTables([0, 1, 2, 3], Options, Tables, Info) then
// Info.TableCount proti Info.SourceTableCount pokaže, koliko je bilo združenega
ProcessTables(Tables)
else if Info.Status = ttesBudgetExceeded then
Log(string(Info.Diagnostic));
finally
Pdf.Free;
end;
end;
Kaj dejansko zagotavlja združevanje čez strani?
Namerno zagotavlja konservativnost. HotPDF združi dve tabeli čez mejo strani samo, ko je MergeAcrossPages omogočen, ko se druga tabela začne natanko na indeksu strani za zadnjo stranjo prve, ko imata obe vsaj dva stolpca in ko se vsaj dva kanonična začetka stolpcev ujemata znotraj ColumnTolerance. Pogoj zaporednih strani nosi največjo težo. Klicatelji posredujejo PageIndices kot odprto polje v poljubnem vrstnem redu, brez tega preverjanja pa bi zahteva za strani 3, 9 in 14 lahko zvarila tri nepovezane tabele v en rezultat, ki je videti povsem verjeten. Cena je, da se resnično nadaljevanje, ki preskoči stran, prepletena priloga ali obojestranski sken s prazno hrbtno stranjo vrne kot dve tabeli in nobena možnost tega ne sprosti. Njihovo ponovno združevanje je stvar politike, o kateri lahko odloči samo klicna aplikacija, zato API izpostavi FirstPageIndex, LastPageIndex, SourceTableCount in PageIndex za vsako vrstico, odločitev pa pusti tam, kamor sodi
Ponavljajoče se glave so označene, nikoli izbrisane
ExtractLoadedTypedTables ponavljajoče se vrstice glave iz rezultata nikoli ne odstrani. Ko združevanje čez strani ugotovi, da se vhodna tabela začne z besedilom glave, ki je enako nabrani tabeli po obrezovanju in spremembi velikosti črk, te vrstice označi z IsHeader in IsRepeatedHeader ter jih vseeno doda v izvornem vrstnem redu. Brisanje je izguba podatkov in nepovratna izbira, različni porabniki pa želijo različne odgovore: uvoz CSV želi ponovitve odstranjene, revizijska sled jih želi z njihovimi številkami strani, orodje za primerjavo želi izvorni vrstni red ohranjen bajt za bajtom. Zato knjižnica poroča, klicatelj pa odloči
var
T, R, C: Integer;
Row: THPDFTypedTableRow;
Total: Double;
begin
Total := 0;
for T := 0 to High(Tables) do
for R := 0 to High(Tables[T].Rows) do
begin
Row := Tables[T].Rows[R];
if Row.IsRepeatedHeader then
Continue; // ohrani samo prvi blok glave
for C := 0 to High(Row.Cells) do
if Row.Cells[C].ValueKind = ttvkCurrency then
Total := Total + Row.Cells[C].NumberValue;
end;
end;
Tipizirane vrednosti in ločila, ki jih morate podati
Sklepanje tipov se izvede v določenem vrstnem redu, ki dvoumnosti razreši v edini razumni smeri: najprej boolean, nato datum, odstotek, valuta in navadno število, vse, kar se ne ujema, pa ostane niz. Vrstni red prepreči, da bi 2026 v stolpcu datumov določil razčlenjevalnik števil, preden ga vidi razčlenjevalnik datumov. Valuta se prepozna po začetnem $, £, ¥ ali € oziroma po tričrkovni kodi ISO 4217, ki ji sledi presledek, koda pa se ohrani v CurrencyCode. Ključno je, da HotPDF ne ugiba vašega okolja. DecimalSeparator, ThousandsSeparator in DateOrder pridejo iz možnosti, saj je 1.234 bodisi eno število bodisi tisoč dvesto štiriintrideset, odvisno od dejstva, ki ga PDF ne vsebuje. Surovi Unicode Text se ohrani pri vsaki celici skupaj s tipizirano vrednostjo, zato je napačno ugibanje vedno mogoče obnoviti brez drugega prehoda izločanja
var
Stream: TFileStream;
Info: THPDFTypedTableExtractionInfo;
begin
Stream := TFileStream.Create('tables.json', fmCreate);
try
if not Pdf.ExportLoadedTypedTables([0, 1, 2], ttefJSON,
Stream, Options, Info) then
case Info.Status of
ttesInvalidOptions: ReportBadConfiguration;
ttesBudgetExceeded: ReportOversizedDocument;
ttesCancelled: ReportUserCancelled;
ttesWriteFailed: ReportDestinationProblem;
else
ReportExtractionFailure;
end;
finally
Stream.Free;
end;
end;
Izvozna zapisa odgovarjata na različni vprašanji in namerno nista enakovredna. CSV nadaljevalne stolpce združenega razpona zapiše kot prazna polja, kar pričakuje preglednica ali množični nalagalnik. JSON ohrani vse, kar je izločanje vedelo: tipizirano vrednost pod lastno vrsto, columnSpan, zaupanje za vsako celico in vrstico, meje celice ter izvor strani in izvorne tabele. Oba zapisa celoten dokument najprej pripravita v omejenem pomnilniškem medpomnilniku in ga šele nato objavita v vaš ciljni tok, pri napaki med pisanjem pa obnovita izvirne bajte, dolžino in položaj, zato neuspešen izvoz nikoli ne pusti napol zapisane datoteke. Proračuni za strani, glife na stran, tabele, vrstice, celice, znake in izhodne bajte se vodijo ločeno, vrstice pa se štejejo pred dodeljevanjem, ker se SetLength na ravni vrstice degenerira v kvadratno kopiranje precej pred privzetim pragom milijona vrstic
Kje se geometrijska obnova tabel umakne
Izrecno opisati načine odpovedi je bolj uporabno kot našteti funkcije, saj je vsako od teh mest prostor, kjer klicatelj potrebuje lastno politiko, ne boljše vrednosti možnosti
- Navpične združitve se ne obnovijo. HotPDF poroča
ColumnSpanza vodoravne razpone inRowSpanpusti pri 1, zato celica, ki v natisnjeni tabeli sega čez tri vrstice, prispe kot ena celica in dve vrzeli - Zaznavanje glave temelji na podatkih, ne na videzu. Blok glave je zaporedje vrstic pred prvo vrstico, ki vsebuje netipizirano vrednost, zato tabela, katere telo je v celoti besedilo, poroča
HeaderRowCountkot nič, ne glede na slog - Tabele pod
MinimumTableConfidencese iz rezultata zavržejo brez napake. Ko morate vedeti, da je bilo nekaj zavrženo, primerjajteInfo.TableCountzInfo.SourceTableCount - Poteza potrebuje vsaj dve vrstici in vsaj dva stolpca, preden jo prehod postavitve sploh poimenuje tabela, zato enovrstična psevdotabela ali dvostolpčna postavitev dolge proze pravilno, čeprav nekoristno, ni tabela
- Skenirane strani nimajo operatorjev besedila, zato geometrijsko ni ničesar za obnoviti, dokler stran nima besedilne plasti OCR
Če vaši PDF-ji prihajajo iz lastnega poročevalskega sklada, je najcenejši popravek za vse to na začetku: oddajajte označene tabele ali ohranite izvorne podatke, izločanje pa obravnavajte kot nadomestno pot za dokumente, ki jih niste izdelali. Za vse drugo se je cevovoda vredno učiti v tem vrstnem redu, saj vsaka plast gradi na spodnji: začnite z navadnim izločanjem besedila iz naloženega PDF-ja, nato preidite na API tipiziranih tabel, ko je treba ohraniti geometrijo, in poglejte izris podatkovne tabele v nov PDF, ko ste na strani izdelave in lahko določite, kako obnovljiv bo izhod
ExtractLoadedTypedTables in ExportLoadedTypedTables sta del izvorne komponente HotPDF Delphi PDF Component za Delphi in C++Builder, brez zunanjega DLL-a in brez odvisnosti od izvajalnega okolja; stran izdelka vsebuje popolno referenco možnosti, statusov in zapisov za API tipiziranih tabel