HotPDF palauttaa olemassa olevasta PDF:stä taulukot ExtractLoadedTypedTables-metodilla, Delphi-API:lla joka yhdistää asetteluvaiheen tuottamat rivipalaset, rakentaa jokaiselle taulukolle yhden kanonisen sarakeruudukon, jatkaa taulukkoa sivunvaihdon yli kun geometria tukee sitä ja palauttaa jokaisen solun tyypitettynä arvona joka kantaa sivun alkuperän, sarakevälin ja rajat. ExportLoadedTypedTables kirjoittaa saman tuloksen suoraan CSV- tai JSON-muotoon. Käyttötapaus joka tekee tästä rakentamisen arvoisen on tylsä ja erittäin yleinen. Nelikymmensivuinen laskurekisteri, loogisesti yksi taulukko, tulostettuna niin että otsikko toistuu jokaisen sivun yläreunassa. Aja sen yli naiivi lukujärjestyspolku ja saat neljäkymmentä taulukkoa, 39 ylimääräistä otsikkoriviä ja valuuttasarakkeen joka siirtyy yhden paikan vasemmalle jokaisella rivillä jonka keskimmäinen solu sattui olemaan tyhjä. Tämän siivoaminen alavirtaan kutsuvassa sovelluksessa on se kohta johon dokumenttien tuontiprojektit kuolevat
Miksi PDF-sivu antaa sinulle palasia eikä taulukkoa?
Koska PDF-sivulla ei ole lainkaan taulukkosemantiikkaa ellei dokumenttia ole tagitettu. Sisältöstream sisältää tekstiä näyttäviä operaattoreita ja asemointimatriiseja (ISO 32000-1 §9.4.3) eikä muuta; ruudullinen laatikko jonka näet näytöllä on toisiinsa liittymätöntä polkumaalausta jota minkään poimijan ei tarvitse yhdistää tekstiin. Rakenne-elementtien tyypit Table, TR, TH ja TD elävät vain tagitetun PDF:n loogisessa rakennehierarkiassa (ISO 32000-1 §14.8.4), eikä valtaosaa kierrossa olevista liiketoimintadokumenteista ole tagitettu. Kaikki alla kuvattu on geometrista palautusta eikä jäsentämistä, ja tämä kannattaa sanoa ääneen ennen kuin kukaan rakentaa sen päälle täsmäytysraporttia
HotPDF tekee siksi ensin semanttisen asetteluanalyysin poimituista glyfeistä, saman vaiheen joka tukee rakenteen mukaista tekstinpoimintaa ladatusta PDF:stä sekä rakenteisia HTML- ja XML-vientejä. Vaihe ryhmittelee baselinet ajoiksi joiden solut kohdistuvat pystysuunnassa ja jatkaa ajoa vain niin kauan kuin peräkkäisillä riveillä on sama solumäärä. Asettelumoottorille sääntö on oikea ja halpa. Kutsujalle muoto on väärä: yksi rivi jonka sisempi solu on tyhjä jakaa yhden visuaalisen taulukon kahdeksi lähdetaulukoksi. Tyypitetty taulukkokerros on juuri sitä varten asetteluvaiheen yläpuolella, että palaset liitetään takaisin yhteen
Kanoniset sarakeruudukot ja ColumnTolerance-säätö
ExtractLoadedTypedTables yhdistää saman sivun palaset ennen kaikkea muuta ja yhdistää ne rivitekstin sijaan sarakegeometrian perusteella. Kaksi vierekkäistä lähdetaulukkoa samalla sivulla liittyy yhteen, kun molemmissa on vähintään kaksi saraketta, ensimmäisen viimeisen rivin ja toisen ensimmäisen rivin välinen pystyrako pysyy toleranssikaistan sisällä ja niiden sarakkeiden aloituskohdat kohdistuvat. ColumnTolerance-arvon sisällä toisistaan olevat sarakealut sulautetaan yhdeksi kanoniseksi sarakkeeksi ja keskiarvoistetaan yhdistämisen aikana. Oletustoleranssi on 12 user-space-yksikköä, mikä sopii tavalliseen liiketoimintatypografiaan mutta vaatii suuremman arvon leveästi harvennetuissa tai syvästi sisennetyissä asetteluissa
Tyhjän sisemmän arvon sisältävän rivin käsittely on tärkeä kohta. HotPDF napsauttaa jokaisen solun lähimmän kanonisen sarakealun kohdalle ja asettaa ColumnSpan-arvoksi etäisyyden tästä sarakkeesta seuraavaan täytettyyn sarakkeeseen sen sijaan että siirtäisi jäljellä olevia soluja vasemmalle. Viiden sarakkeen ruudukossa oleva kolmen solun rivi säilyttää arvonsa oikeiden otsikoiden alla ja kirjaa täsmälleen missä aukot ovat. Siinä on ero täsmäytettävän taulukon ja sellaisen välillä joka kohdistaa rahat hiljaisesti väärin
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; // user-space-yksiköitä
Options.MinimumTableConfidence := 0.55; // tätä pienemmät taulukot pudotetaan
Options.DateOrder := ttdoDMY; // 03/04/2026 on 3. huhtikuuta
Options.DecimalSeparator := ',';
Options.ThousandsSeparator := '.';
if Pdf.ExtractLoadedTypedTables([0, 1, 2, 3], Options, Tables, Info) then
// Info.TableCount ja Info.SourceTableCount näyttävät kuinka paljon yhdistettiin
ProcessTables(Tables)
else if Info.Status = ttesBudgetExceeded then
Log(string(Info.Diagnostic));
finally
Pdf.Free;
end;
end;
Mitä sivujen yli tapahtuva yhdistäminen oikeastaan takaa?
Se takaa tarkoituksella varovaisuuden. HotPDF yhdistää kaksi taulukkoa sivurajan yli vain kun MergeAcrossPages on käytössä, toinen taulukko alkaa täsmälleen sivuindeksistä joka seuraa ensimmäisen loppusivua, molemmissa on vähintään kaksi saraketta ja vähintään kaksi kanonista sarakealkua kohdistuu ColumnTolerance-arvon sisällä. Peräkkäisten sivujen ehto on kantava. Kutsujat antavat PageIndices-taulukon avoimena taulukkona missä järjestyksessä tahansa, ja ilman tätä tarkistusta pyyntö sivuista 3, 9 ja 14 voisi hitsata kolme toisiinsa liittymätöntä taulukkoa yhdeksi täysin uskottavan näköiseksi tulokseksi. Hinta on se, että aito jatkumo joka ohittaa sivun, lomittuva liite tai kaksipuolinen skannaus jossa kääntöpuoli on tyhjä palautuu kahdeksi taulukoksi eikä mikään optio löysää ehtoa. Niiden yhdistäminen uudelleen on käytäntöpäätös jonka vain kutsuva sovellus voi tehdä, joten API antaa näkyviin arvot FirstPageIndex, LastPageIndex, SourceTableCount ja rivikohtaisen PageIndex-arvon ja jättää päätöksen oikeaan paikkaan
Toistuvat otsikot merkitään eikä poisteta
ExtractLoadedTypedTables ei koskaan poista toistuvaa otsikkoriviä tuloksesta. Kun sivujen yli yhdistäminen löytää, että saapuva taulukko alkaa kertyneen taulukon kanssa tekstiltään identtisellä otsikolla trimmaamisen ja kirjainkoon normalisoinnin jälkeen, se merkitsee rivit arvoilla IsHeader ja IsRepeatedHeader ja liittää ne silti lähdejärjestyksessä mukaan. Poistaminen on tietoa hukkaava ja peruuttamaton valinta, ja eri kuluttajat haluavat eri vastaukset: CSV-tuonti haluaa toistot pois, audit trail haluaa ne sivunumeroineen mukaan ja diff-työkalu haluaa lähdejärjestyksen tavutasolla säilytettynä. Siksi kirjasto raportoi ja kutsuja päättää
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; // pidä vain ensimmäinen otsikkolohko
for C := 0 to High(Row.Cells) do
if Row.Cells[C].ValueKind = ttvkCurrency then
Total := Total + Row.Cells[C].NumberValue;
end;
end;
Tyypitetyt arvot ja erottimet jotka sinun täytyy antaa
Tyypin päättely tapahtuu kiinteässä järjestyksessä joka ratkaisee epäselvyydet ainoaan järkevään suuntaan: ensin boolean, sitten päivämäärä, prosentti, valuutta ja tavallinen luku, ja kaikki muu jää merkkijonoksi. Järjestys estää sarakkeessa olevan arvon 2026 päätymisen numeroparserin ratkaistavaksi ennen kuin päivämääräparseri näkee sen. Valuutta tunnistetaan alussa olevasta merkistä $, £, ¥ tai € tai kolmikirjaimisesta ISO 4217 -koodista jota seuraa välilyönti, ja koodi säilytetään kentässä CurrencyCode. Olennaista on, ettei HotPDF arvaa lokaaliasi. DecimalSeparator, ThousandsSeparator ja DateOrder tulevat optioista, koska 1.234 on joko yksi luku tai tuhat kaksisataa kolmekymmentäneljä riippuen tiedosta jota PDF ei sisällä. Raaka Unicode-Text säilytetään jokaisessa solussa tyypitetyn arvon rinnalla, joten väärä arvaus voidaan aina palauttaa ilman uutta poimintakierrosta
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;
Kaksi vientimuotoa vastaavat eri kysymyksiin eikä niitä ole tarkoituksella tehty samanarvoisiksi. CSV kirjoittaa yhdistetyn välin jatkosarakkeet tyhjinä kenttinä, mitä taulukkolaskenta tai bulk loader odottaa. JSON säilyttää kaiken minkä poiminta tiesi: tyypitetyn arvon oman tyyppinsä alla, columnSpan-arvon, solu- ja rivikohtaisen luottamuksen, solun rajat sekä sivu- ja lähdetaulukkoalkuperän. Molemmat muodot kokoavat koko dokumentin rajattuun muistipuskuriin ja julkaisevat sen kohdestreamiin vasta sen jälkeen, palauttaen alkuperäiset tavut, pituuden ja position jos kirjoitus epäonnistuu kesken, joten epäonnistunut vienti ei jätä puoliksi kirjoitettua tiedostoa. Sivujen, sivun glyfien, taulukoiden, rivien, solujen, merkkien ja tulostavujen budjetit lasketaan kaikki erikseen, ja rivit lasketaan ennen allokointia koska rivikohtainen SetLength muuttuu neliölliseksi kopioinniksi kauan ennen miljoonan rivin oletuskattoa
Missä geometrinen taulukon palautus luovuttaa
Epäonnistumistapojen selkeä nimeäminen on hyödyllisempää kuin ominaisuuslista, koska jokainen näistä on paikka jossa kutsuja tarvitsee oman käytäntönsä paremman optioarvon sijaan
- Pystysuuntaisia yhdistämisiä ei palauteta. HotPDF ilmoittaa vaakasuuntaisista yhdistämisistä
ColumnSpan-arvon ja pitääRowSpan-arvon 1:nä, joten painetun taulukon kolme riviä kattava solu saapuu yhtenä soluna ja kahtena aukkona - Otsikon tunnistus perustuu dataan eikä visuaaliseen ulkoasuun. Otsikkolohko on rivien ajo ennen ensimmäistä riviä joka sisältää ei-merkkijonotyyppisen arvon, joten kokonaan tekstimuotoisen rungon taulukko ilmoittaa
HeaderRowCount-arvoksi nollan riippumatta tyylistä - Alle
MinimumTableConfidence-arvon jäävät taulukot pudotetaan tuloksesta ilman virhettä. Vertaa arvojaInfo.TableCountjaInfo.SourceTableCount, kun haluat tietää että jotakin hylättiin - Ajo tarvitsee vähintään kaksi riviä ja vähintään kaksi saraketta ennen kuin asetteluvaihe kutsuu sitä taulukoksi, joten yhden rivin pseudotaulukko tai kahden sarakkeen pitkäproosainen asettelu ei ole oikein eikä hyödyllisesti taulukko
- Skannatuilla sivuilla ei ole tekstioperaattoreita, joten geometrisesti ei ole mitään palautettavaa ennen kuin sivulla on OCR-tekstikerros
Jos PDF:t syntyvät omasta raportointipinostasi, halvin korjaus kaikkeen tähän on upstreamissa: tuota tagitetut taulukot tai säilytä lähdedata ja käsittele poimintaa vararatkaisuna dokumenteille joita et itse tuottanut. Muussa tapauksessa putki kannattaa opetella tässä järjestyksessä, koska jokainen kerros rakentuu edellisen päälle: aloita tavallisesta tekstinpoiminnasta ladatusta PDF:stä, siirry tyypitettyyn taulukko-API:in kun geometria täytyy säilyttää ja katso datan taulukon renderöintiä uuteen PDF:ään, kun olet tuottamassa puolella ja voit päättää kuinka palautettavaa tulosteesta tulee
ExtractLoadedTypedTables ja ExportLoadedTypedTables toimitetaan natiivin HotPDF Delphi PDF Component -komponentin osana Delphille ja C++Builderille ilman ulkoista DLL:ää tai ajonaikaista riippuvuutta; tuotesivu sisältää tyypitetyn taulukko-API:n täydellisen option-, tila- ja tietueviitteen