Teknik Makale

PDFium ile Delphi'de PDF Sayfalarından Tablo Çıkarma

PDFium Component, bir PDF sayfasındaki tabloları tespit eder ve bunları satır ve sütun span'leri, başlık satırları ve bir güven değerine sahip bir hücre ızgarası olarak döndürür; tek bir sayfa için ExtractTables ve tüm bir belge için ExtractDocumentTables aracılığıyla. Her tablo tek bir çağrıyla CSV veya JSON'a dönüşür ve bir sayfa sonu boyunca devam eden tablolar bir devamlılık zincirine bağlanabilir

PDF'nin bir tablo nesnesi yoktur. Bir PDF'deki bir tablo, bir insanın onları bir ızgara olarak okuyacağı şekilde konumlandırılmış, bazen etraflarına çizgiler çizilmiş, sıklıkla da çizilmemiş bir metin çalıştırmaları kümesidir. Izgarayı kurtarmak, dosyanın hiçbir zaman kaydetmediği bir niyeti yeniden inşa etmek anlamına gelir; her çıkarma aracının biraz farklı sonuçlar üretmesinin ve size güvenini söyleyen bir aracın söylemeyenden daha kullanışlı olmasının nedeni budur

İki Tablo Türü İçin İki Tespit Modu

Çizgili tespit, çizilen çizgileri kullanır. Her vurgulu yol parçası, sayfa nesnesinin matrisi aracılığıyla sayfa koordinatlarına dönüştürülür, yatay ve dikey çizgiler kesiştirilir ve kesişimler bağlı bileşenler oluşturur. Her bileşen kendi sıralanmış X ve Y konumları ızgarası haline gelir; aynı sayfadaki iki ayrı tablonun tek bir anlamsız ızgaraya birleştirilmesini önleyen şey budur

Boşluk tespiti, çizgiler yerine hizalamayla çizilen tabloları ele alır. Kelime kutuları görsel satırlara gruplandırılır, bir satır içindeki boşluklar onu aday sütunlara böler ve bir tablo yalnızca en az MinRows satır, AlignmentTolerance içinde en az MinColumns sola hizalanmış çapayı tekrarladığında kabul edilir. Satır boşluğu faktörü varsayılan olarak 3'tür; bu, birkaç metin çalıştırması içeren tek bir satırın bir tablo gibi görünmesine izin vermeden 12 puntoluk metne tipik olan yaklaşık 30 puntoluk taban çizgisi aralığını kapsar

Delphi'deki PDFium Component tablo tespit boru hattını gösteren diyagram: çizgili çizgi kesişimleri ve boşlukla hizalanmış kelime satırları, CSV ve JSON dışa aktarımlı tek bir puanlanmış tablo kaydını besler
Çizgili tespit çizilen vuruşları kesiştirirken boşluk tespiti hizalanmış kelime kutusu satırlarını sayar; MinRows ve MinColumns'u geçen adaylar bir güven puanı ve DetectionMode eklenmiş olarak ortaya çıkar
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;                    // 1 tabanlı

    Options := TPdfTableExtractionOptions.Default;
    Options.DetectRuledTables := True;
    Options.DetectWhitespaceTables := True;
    Options.MinConfidence := 0.6;            // varsayılan 0,5'tir
    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;

Birleştirilmiş Hücreler Nasıl Kurtarılır?

Bu, naif çıkarıcıların yanlış yaptığı kısımdır. Birleştirilmiş bir hücre yalnızca global ızgaradan tanımlanamaz, çünkü ızgara sayfadaki tüm çizgilerden türetilir ve birleştirilmiş bir bölge, onu ayıracak olan iç çizgiden basitçe yoksundur

Burada kullanılan kural yereldir: aralarındaki aralığı hiçbir sınır çizgisi kapsamadığında iki bitişik temel hücre birleştirilir. Union-find bunları birleştirir, ortaya çıkan dikdörtgen bileşenler RowSpan ve ColumnSpan değerleri haline gelir ve metin, merkez noktasına göre bir temel hücreye atanır ve ardından o hücreyi birleştirme köküne kadar takip eder. Bunu bu şekilde yapmak, her kelimeyi her hücreye karşı test etmekten elde edilen ikinci dereceden tarama yerine, maliyeti kelimeler artı hücrelerde doğrusal tutar

Delphi için PDFium tablo çıkarmada birleştirilmiş hücre kurtarmayı gösteren diyagram: union-find, aralarındaki aralığı hiçbir sınır çizgisi kapsamadığında bitişik temel hücreleri birleştirir, RowSpan ve ColumnSpan üretir
Union-find, paylaşılan aralığı hiçbir çizilmiş sınır taşımayan komşu temel hücreleri birleştirir, bu yüzden birleştirilmiş bir başlık, boşluklarla çevrili tek bir dolu hücre yerine ColumnSpan ayarlı tek bir hücre olarak döner

Pratik etki, üç sütuna yayılan birleştirilmiş bir "Total" başlığına sahip bir mali tablonun, bir dolu hücre ve iki gizemli boş hücre yerine span üç olan tek bir hücreyle ortaya çıkmasıdır

Sayfalar Arası Devamlılık

Uzun tablolar sayfalar arasında bölünür ve her sayfanın parçasını bağımsız bir tablo olarak ele almak, çağıranı bunları dikmeye zorlar. ExtractDocumentTables bunun yerine bunları bağlayabilir, ama yalnızca katı koşullar altında: parça, önceki sayfadaki en alttaki tablo olmalıdır, bir sonraki, sonraki sayfadaki en üstteki tablo olmalıdır, sayfa numaraları bitişik olmalıdır ve sütun sınırları eşleşmelidir

Dört koşulun tümü birlikte, bariz hatayı önleyen şeydir; bu hata, bir belgedeki her dört sütunlu tabloyu, yalnızca aynı sütun sayısını paylaştıkları için tek bir hayali mega tabloya zincirlemektir. Koşullar sağlandığında tablolar bir devamlılık grubu tanımlayıcısını paylaşır ve devamlılık meta verisi taşır; sağlanmadığında ayrı tablolar elde edersiniz ve kendiniz karar verebilirsiniz

Delphi'de PDF sayfaları arasında tablo devamlılığını gösteren diyagram: dört katı kapı, bir sayfadaki en alttaki parçanın bir sonrakindeki en üstteki parçaya katılıp katılmayacağına karar verir
Belge düzeyinde çıkarma, parçaları yalnızca dört kapının tümü sağlandığında bağlar; bu, ilgisiz dört sütunlu tabloların tek bir hayali mega tabloya kaynaşmasını önler

Belge düzeyinde çıkarma, MaxCells ve MaxTables bütçelerini sayfa başına sıfırlamak yerine sayfalar arasında paylaşır ve etkin sayfayı bir finally bloğunda geri yükler, bu yüzden bir görüntüleyicide çalıştırılan bir çıkarma, kullanıcıyı bulunduğu sayfaya bakarken bırakır

Veriyi Bozmadan Dışa Aktarma

Her iki dışa aktarıcı da kaçış konusunda kasıtlıdır. CSV her zaman alanları tırnak içine alır ve iç tırnak işaretlerini ikiye katlar; bu, virgül içeren bir hücrenin sessizce iki sütuna dönüştüğü klasik başarısızlığı önler. Birleştirilmiş hücreler için içerik yalnızca sol üst çapada yayılır, bu yüzden bir CSV gidiş-dönüşü, kapladığı sütunlar boyunca yayılan bir başlığı yinelemez

JSON, Unicode'u ASCII'ye kaçırmak yerine korur, kontrol karakterlerini kaçırır ve bir tüketicinin kaliteyi değerlendirmesi için ihtiyaç duyduğu meta veriyi içerir: tespit modu, güven, sınırlar, span değerleri, başlık bayrakları ve devamlılık bilgisi. Çıkarılan tabloları sonraki bir sisteme besliyorsanız JSON'u tercih edin, çünkü bir CSV satırı size geldiği tablonun 0,51 güven puanı aldığını söyleyemez:

// Belge genelinde çıkarma, yalnızca güvenilmeye değer tabloları tutma
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;

Ayarlama ve Ne Zaman Duracağını Bilmek

Üç ayar diğerlerinden daha önemlidir. MinConfidence kalite kapısıdır ve 0,5 kasıtlı olarak izin vericidir; otomatik alım için yükseltin, bir insanın her sonucu onayladığı bir inceleme arayüzü için düşürün. MinColumnGap, boşluk modunda neyin bir sütun sınırı sayıldığına karar verir ve yoğun raporlardaki sıkı ayarlı tablolar, 12 puntoluk varsayılandan düşürülmesini gerektirebilir. MaxRowGapFactor, dikey mesafenin bir tabloyu ne zaman sonlandırdığına karar verir; bu, ara sıra boş satırlara sahip tablolar için önemlidir

Sınırlar konusunda dürüst olun. Çizgili tablolar güvenilir şekilde çıkarılır. Temiz hizalanmış boşluk tabloları iyi çıkarılır. Döndürülmüş metne sahip tablolar, iç içe geçmiş tablolar veya içeriği başka bir satır gibi görünen bir şeye sarılan hücreler, parametreler nasıl ayarlanırsa ayarlansın incelemeye ihtiyaç duyacaktır. Bunlar için yapılandırılmış metin modeli, alana özgü bir okuyucu oluşturmak için ham malzemeyi verir; yapılandırılmış metin blokları ve okuma sırası başlığında anlatılmıştır

Yararlı bir eşleşme: taranmış bir belgenin hiç metni olmadığında tablo tespiti, bir metin katmanı var olana kadar üzerinde çalışacak hiçbir şeye sahip olmaz. Önce bir tane ekleyin, taranmış PDF'lere aranabilir bir metin katmanı ekleme başlığında anlatıldığı gibi, ardından çıkarın. Bir OCR sağlayıcısının döndürdüğü kelime kutuları, tam olarak boşluk tespitinin ihtiyaç duyduğu girdidir

Tablo çıkarma, yapılandırılmış metin ve yeniden akış, Delphi, C++Builder ve Lazarus'ta aynı sayfa modelinden okur; tam API, Delphi için PDFium Component sayfasında açıklanmıştır