HotPDF, mevcut bir PDF'den tabloları ExtractLoadedTypedTables üzerinden kurtarır; bu Delphi API'si layout geçişinin ürettiği satır parçalarını birleştirir, her tablo için tek bir canonical column grid kurar, geometri desteklediğinde tabloyu sayfa sonu boyunca sürdürür ve her hücreyi sayfa provenance'ı, column span ve bounds taşıyan typed bir değer olarak döndürür. ExportLoadedTypedTables aynı sonucu doğrudan CSV veya JSON'a yazar. Bunu kurmaya değer kılan senaryo sıkıcı ve son derece yaygındır. Tek bir tablo olan kırk sayfalık fatura kayıtları, her sayfanın başında header tekrarlanarak yazdırılır. Naif bir reading-order geçişi çalıştırırsanız kırk tablo, otuz dokuz sahte header satırı ve ortadaki hücre boş kaldığında her satırda bir pozisyon sola kayan bir currency column elde edersiniz. Bunu çağıran uygulamada aşağı akışta temizlemeye çalışmak, document-import projelerinin öldüğü yerdir
PDF sayfası neden size tablo yerine parçalar verir?
Çünkü belge tagged değilse bir PDF sayfası hiç table semantics taşımaz. Content stream, text-showing operator'larını ve positioning matrix'lerini (ISO 32000-1 §9.4.3) taşır, başka hiçbir şey taşımaz; ekranda gördüğünüz çizgili kutu, hiçbir extractor'ın metinle ilişkilendirmek zorunda olmadığı bağımsız path painting'dir. Table, TR, TH ve TD structure element type'ları yalnızca tagged PDF'nin logical structure hierarchy'sinde (ISO 32000-1 §14.8.4) yaşar ve dolaşımdaki business document'ların ezici çoğunluğu tagged değildir. Aşağıda anlatılan her şey parsing değil geometric recovery'dir; herkes üzerine reconciliation report kurmadan önce bunu açıkça söylemek gerekir
HotPDF bu nedenle önce çıkarılan glyph'ler üzerinde semantic layout analysis çalıştırır; bu, yüklenmiş PDF'den structure-order text extraction ile structured HTML ve XML export'larını da besleyen geçiştir. Geçiş, hücreleri dikey hizalanan baseline'ları run'lara gruplar ve yalnızca ardışık satırlar aynı hücre sayısına sahipken bir run'ı sürdürür. Layout engine için bu kural doğru ve ucuzdur. Çağıran için yanlış biçimdir: iç değeri boş tek bir satır, görsel bir tabloyu iki source table'a böler. Typed table layer parçaları tam da bu yüzden yeniden birleştirir
Canonical column grid'ler ve ColumnTolerance düğmesi
ExtractLoadedTypedTables başka bir şey yapmadan önce aynı sayfadaki parçaları birleştirir; metin satırı yerine sütun geometrisi üzerinden birleştirir. Aynı sayfadaki iki bitişik source table, ikisinin de en az iki sütunu varsa, ilk tablonun son satırı ile ikinci tablonun ilk satırı arasındaki dikey boşluk tolerance band içinde kalıyorsa ve sütun başlangıçları hizalanıyorsa birleşir. Birbirinden ColumnTolerance kadar uzakta olan sütun başlangıçları tek bir canonical column'da toplanır ve birleşirken ortalamaları alınır. Varsayılan tolerance, normal business typography'ye uyan 12 user-space unit'tir; geniş aralıklı veya derin girintili layout'larda yükseltilmek ister
İç değeri eksik bir satıra ne olduğu asıl önemli kısımdır. HotPDF her hücreyi en yakın canonical column başlangıcına snap eder ve sonra ColumnSpan'i bu sütundan bir sonraki dolu sütuna olan mesafe olarak ayarlar; kalan hücreleri sola kaydırmaz. Beş sütunlu bir grid'deki üç hücreli satır değerlerini doğru başlıkların altında tutar ve boşlukların tam olarak nerede olduğunu kaydeder. Bu, uzlaştırabileceğiniz bir tablo ile parayı sessizce yanlış ilişkilendiren bir tablo arasındaki farktır
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 birimleri
Options.MinimumTableConfidence := 0.55; // bunun altında tablolar atılır
Options.DateOrder := ttdoDMY; // 03/04/2026, 3 Nisan'dır
Options.DecimalSeparator := ',';
Options.ThousandsSeparator := '.';
if Pdf.ExtractLoadedTypedTables([0, 1, 2, 3], Options, Tables, Info) then
// Info.TableCount ve Info.SourceTableCount ne kadar birleştirildiğini gösterir
ProcessTables(Tables)
else if Info.Status = ttesBudgetExceeded then
Log(string(Info.Diagnostic));
finally
Pdf.Free;
end;
end;
Sayfalar arası birleştirme gerçekte neyi garanti eder?
Bilerek muhafazakârlığı garanti eder. HotPDF iki tabloyu sayfa sınırı üzerinden yalnızca MergeAcrossPages etkinse, ikinci tablo birincinin bittiği sayfa index'inin tam sonraki sayfasında başlıyorsa, ikisi de en az iki sütun taşıyorsa ve en az iki canonical column başlangıcı ColumnTolerance içinde hizalanıyorsa birleştirir. Yük taşıyan koşul ardışık sayfa şartıdır. Çağıranlar PageIndices'i istedikleri sıradaki open array olarak verir ve bu kontrol olmazsa 3, 9 ve 14. sayfalar için istek, birbiriyle ilgisiz üç tabloyu bütünüyle makul görünen tek bir sonuca kaynaklayabilir. Bedeli, sayfa atlayan gerçek bir devamın, araya giren bir appendix'in veya boş verso'su olan duplex scan'in iki tablo olarak dönmesidir; hiçbir seçenek bu sınırı gevşetmez. Bunları yeniden birleştirmek yalnızca çağıran uygulamanın verebileceği bir policy kararıdır; bu nedenle API FirstPageIndex, LastPageIndex, SourceTableCount ve satır başına PageIndex sunar, kararı ait olduğu yerde bırakır
Tekrarlanan header'lar etiketlenir, hiç silinmez
ExtractLoadedTypedTables tekrarlanan header satırını sonuçtan asla kaldırmaz. Sayfalar arası birleştirme gelen tablonun accumulated table ile aynı header metniyle açıldığını, trim ve case folding sonrasında karşılaştırarak bulursa o satırları IsHeader ve IsRepeatedHeader olarak işaretler ve yine source order içinde ekler. Silme kayıplı ve geri döndürülemez bir tercihtir; farklı consumer'lar farklı cevaplar ister: CSV import tekrarları kaldırmak ister, audit trail sayfa numaralarıyla birlikte görmek ister, diffing tool source order'ı byte byte korumak ister. Bu yüzden kütüphane raporlar, kararı çağıran verir
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; // yalnızca ilk header bloğunu tut
for C := 0 to High(Row.Cells) do
if Row.Cells[C].ValueKind = ttvkCurrency then
Total := Total + Row.Cells[C].NumberValue;
end;
end;
Typed değerler ve sizin sağlamanız gereken ayraçlar
Type inference, belirsizlikleri makul olan tek yönde çözen sabit bir sırada çalışır: önce boolean, sonra date, sonra percentage, sonra currency, sonra plain number; hiçbirine uymayan string olarak kalır. Sıra, date column içindeki 2026 değerinin date parser onu görmeden önce number parser tarafından kararlaştırılmasını önler. Currency, baştaki $, £, ¥ veya € işaretinden ya da ardından boşluk gelen üç harfli ISO 4217 kodundan tanınır ve kod CurrencyCode içinde korunur. Kritik nokta, HotPDF'in locale tahmin etmemesidir. DecimalSeparator, ThousandsSeparator ve DateOrder options'tan gelir; çünkü 1.234, PDF'nin taşımadığı bir bilgiye göre tek sayı da olabilir bin iki yüz otuz dört de. Ham Unicode Text her hücrede typed değerle birlikte tutulur; bu nedenle yanlış bir tahmin her zaman ikinci extraction pass olmadan geri alınabilir
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;
İki export formatı farklı sorulara cevap verir ve bilerek eşdeğer değildir. CSV, birleştirilmiş span'in continuation column'larını boş field olarak yazar; spreadsheet veya bulk loader'ın beklediği budur. JSON, extraction'ın bildiği her şeyi korur: typed değer kendi kind'ı altında, columnSpan, hücre ve satır başına confidence, hücre bounds'ları, page ve source-table provenance. Her iki format da bütün belgeyi bounded in-memory buffer'a alır ve ancak sonra hedef stream'e yayımlar; write yarıda başarısız olursa özgün baytları, uzunluğu ve konumu geri yükler, bu yüzden başarısız export yarım yazılmış bir dosya bırakmaz. Page, page başına glyph, table, row, cell, character ve output byte budget'ları ayrı ayrı hesaplanır; bir milyon satırlık varsayılan tavana çok daha önce per-row SetLength quadratic copying'e dönüşeceği için satırlar allocation'dan önce sayılır
Geometrik table recovery nerede vazgeçer?
Başarısızlık kiplerini açıkça belirtmek bir feature list'ten daha yararlıdır; çünkü bunların her biri çağıranın daha iyi bir option value yerine kendi policy'sine ihtiyaç duyduğu bir yerdir
- Dikey birleşmeler kurtarılmaz. HotPDF yatay span'ler için
ColumnSpanbildirir veRowSpan'i 1'de bırakır; yazdırılmış tabloda üç satıra yayılan bir hücre bir hücre ve iki boşluk olarak gelir - Header detection görsel değil data-driven'dır. Header block, typed olmayan ilk non-string değeri taşıyan satırdan önceki satır run'ıdır; gövdesi bütünüyle metin olan bir tablo nasıl stillenmiş olursa olsun
HeaderRowCountdeğerini sıfır bildirir MinimumTableConfidencealtındaki tablolar sonuçtan hatasız biçimde atılır. Bir şeyin atıldığını bilmek istediğinizdeInfo.TableCountileInfo.SourceTableCountdeğerlerini karşılaştırın- Bir run, layout pass'in ona tablo diyebilmesi için en az iki satıra ve en az iki sütuna ihtiyaç duyar; bu yüzden tek satırlık pseudo-table veya uzun prose içeren iki sütunlu bir layout doğru ama yararsız biçimde tablo değildir
- Taranmış sayfalarda text operator yoktur; sayfada bir OCR text layer bulunana kadar geometrik olarak kurtarılacak hiçbir şey yoktur
PDF'leriniz kendi reporting stack'inizden çıkıyorsa bunların hepsinin en ucuz çözümü upstream'dedir: tagged table üretin veya kaynak veriyi tutun ve extraction'ı üretmediğiniz belgeler için fallback kabul edin. Geri kalan her şey için pipeline'ı şu sırayla öğrenmek değerlidir, çünkü her katman altındakinin üzerine kurulur: yüklenmiş PDF'den düz metin çıkarma ile başlayın, geometrinin korunması gerektiğinde typed table API'ye geçin ve üretim tarafındaysanız, çıktının ne kadar kurtarılabilir olacağına karar verebildiğiniz yerde veri tablosunu yeni PDF'e render etmeye bakın
ExtractLoadedTypedTables ve ExportLoadedTypedTables, Delphi ve C++Builder için native HotPDF Delphi PDF Component'in parçasıdır; harici DLL ve runtime dependency yoktur, ürün sayfası typed table API için option, status ve record referansının tamamını taşır