Artikel Teknis

Ekstraksi Teks PDF Urutan Struktur di Delphi dengan HotPDF

Setiap ekstraktor teks geometris sedang menebak. Ia membaca glyph yang digambar sebuah halaman, mengurutkannya berdasarkan baseline dan posisi horizontal, dan berharap susunan visualnya cocok dengan urutan yang dibaca manusia. Pada laporan satu kolom tebakan itu benar. Pada artikel jurnal dua kolom, form dengan sidebar, atau tabel yang selnya dipancarkan kolom demi kolom, tebakannya keliru dengan cara yang sulit disadari dan mahal ditemukan di hilir. HotPDF menjawabnya dengan ExtractLoadedPageStructureText, yang mengabaikan geometri sepenuhnya: ia menelusuri pohon struktur dokumen dalam urutan penulisan sebagaimana didefinisikan ISO 32000-1 §14.8.4, lalu menyusun ulang glyph halaman berdasarkan marked-content identifier-nya. Untuk PDF bertag itu bukan heuristik, melainkan urutan yang dideklarasikan aplikasi pembuatnya

Fungsi itu mengembalikan False ketika halaman tidak punya pohon struktur yang dapat dipakai, yang merupakan sinyal untuk jatuh kembali ke ekstraktor geometris alih-alih gagal. Desain dua jalur itu lebih penting daripada algoritmanya: intake dokumen nyata melihat form pemerintahan bertag dan output pemindai di folder yang sama, dan pipeline yang hanya menangani salah satunya bukanlah pipeline

Mengapa ekstraksi geometris mendapat urutan baca yang keliru?

Karena content stream PDF tidak membawa urutan baca sama sekali. Ia adalah rangkaian operator menggambar, dan produsen bebas memancarkannya dalam urutan apa pun yang cocok dengan layout engine-nya sendiri. Word processor biasanya memancarkan dalam urutan aliran dan pengurutan geometris tampak baik. Layout tool, perancang form, dan generator laporan sering kali tidak: footer halaman bisa dipancarkan sebelum badan, tabel bisa diisi column-major, dan halaman dua kolom bisa menyilangkan baris dari kedua kolom karena composer menyelesaikannya bersamaan

Perbandingan halaman PDF dua kolom yang menampilkan ekstraksi geometris terurut baseline menyambung kolom versus ekstraksi MCID urutan struktur di HotPDF
Mengurutkan glyph berdasarkan baseline menyilangkan dua kolom menjadi tak masuk akal, sementara pohon struktur memutar ulang urutan yang dideklarasikan produsen

Mode kegagalannya senyap. Ekstraktor geometris tidak pernah melaporkan error, ia sekadar menyerahkan kembali prosa yang kalimat-kalimatnya disambung dari dua kolom. Apa pun yang mengonsumsi teks itu, indeks pencarian, mapper field e-invoice, pipeline retrieval yang memberi makan model bahasa, mewarisi kerusakannya tanpa peringatan. HotPDF juga mengirim ekstraktor geometris untuk dokumen yang dimuat, dan keduanya tetap menjadi alat yang tepat untuk berkas tanpa tag; inti jalur urutan struktur adalah berhenti menebak ketika dokumen sudah membawa jawabannya

Apa yang sebenarnya disimpan pohon struktur

PDF bertag menyimpan deskripsi kedua yang paralel tentang halaman. Katalog menunjuk ke /StructTreeRoot, yang anak-anak /K-nya membentuk pohon elemen struktur: /Document, /Sect, /P, /Table, /TR, /TD, dan seterusnya. Daun-daun pohon itu adalah referensi marked-content, integer yang menamai satu rentang content stream halaman. Di sisi konten, rentang-rentang itu dibuka dengan operator BDC yang membawa /MCID dan ditutup dengan EMC. Setiap elemen struktur juga membawa entri /Pg yang menamai halaman tempatnya berada, dan itulah yang membuat penelusuran per halaman mungkin pada dokumen yang pohon strukturnya membentang ratusan halaman

Anatomi pohon struktur PDF yang menghubungkan elemen StructTreeRoot seperti Sect, Table, TR, dan TD ke rentang MCID BDC di content stream halaman HotPDF
Daun pohon adalah referensi marked-content, dan setiap elemen membawa entri Pg yang memungkinkan penelusuran menyaring ke halaman saat ini

HotPDF menelusuri pohon itu dengan batas kedalaman 128 level dan menyaring pada /Pg sehingga hanya halaman saat ini yang berkontribusi. Output penelusuran itu bukan teks, melainkan daftar nilai MCID yang terurut: urutan penulisan rentang marked-content pada halaman ini. Menyusun ulang teks kemudian tinggal memutar ulang glyph dalam urutan itu

MCID dicatat saat ekstraksi glyph, bukan dicari setelahnya

Inilah detail implementasi yang membuat fitur ini murah. HotPDF sudah mencatat marked-content identifier aktif pada setiap glyph yang diekstraknya, di field MCID dari THPDFGlyphRecord, karena interpreter content stream tahu scope BDC mana yang terbuka pada saat ia memproses setiap operator Tj atau TJ. Ekstraksi urutan struktur karenanya tidak membutuhkan lintasan kedua atas content stream. Ia mengumpulkan urutan MCID dari pohon struktur, lalu membucketkan glyph yang sudah diekstrak berdasarkan MCID dan memancarkannya dalam urutan itu

var
  Pdf: THotPDF;
  PageCount, I, Untagged: Integer;
  PageText, AllText: UnicodeString;
  Report: TStrings;   // penampung diagnostik milik caller
begin
  Pdf := THotPDF.Create(nil);
  try
    PageCount := Pdf.LoadFromFile('accessible-form.pdf');
    AllText := '';
    for I := 0 to PageCount - 1 do
    begin
      if Pdf.ExtractLoadedPageStructureText(I, PageText, Untagged) then
      begin
        // Urutan penulisan langsung dari pohon struktur
        if Untagged > 0 then
          Report.Add(Format('page %d: %d glyphs outside the structure tree',
            [I, Untagged]));
      end
      else
        // Tidak ada pohon struktur yang dapat dipakai di halaman ini: fallback geometris
        Pdf.ExtractLoadedPageText(I, PageText);
      AllText := AllText + PageText + #13#10;
    end;
  finally
    Pdf.Free;
  end;
end;

Glyph tanpa tag dihitung, tidak pernah dibuang diam-diam

Sebuah halaman bisa bertag sebagian. Produsen menambahkan garis dekoratif, nomor halaman, atau watermark tahap akhir di luar scope BDC mana pun, dan glyph-glyph itu tidak milik MCID mana pun. Membuangnya adalah implementasi yang rapi dan yang keliru, karena celah yang sama juga muncul ketika produsen menandai badan tetapi lupa tabelnya, dan Anda akan kehilangan tabelnya tanpa menyadarinya

HotPDF menambahkan glyph tak terklaim sebagai ekor geometris setelah teks terurut struktur dan melaporkan jumlahnya melalui parameter output UntaggedGlyphCount. Angka itu adalah sinyal kualitas yang bisa Anda tindaklanjuti. Segelintir glyph pada halaman berisi dua ribu adalah furnitur halaman dan bisa diabaikan. Empat puluh persen halaman di luar pohon struktur berarti penandaiannya dekoratif dan ekstraktor geometris adalah jawaban yang lebih jujur untuk berkas itu

Alur keputusan ekstraksi teks struktur HotPDF dengan fallback geometris ketika halaman tidak punya pohon struktur yang dapat dipakai atau penandaan dekoratif
True berarti urutan struktur dengan ekor tanpa tag ditambahkan, dan False mengarahkan halaman ke ekstraktor geometris alih-alih gagal
function ExtractPageBestEffort(Pdf: THotPDF; PageIndex: Integer;
  out AText: UnicodeString; out UsedStructure: Boolean): Boolean;
var
  Untagged, TotalGlyphs: Integer;
  Glyphs: THPDFGlyphArray;
begin
  UsedStructure := False;
  if Pdf.ExtractLoadedPageStructureText(PageIndex, AText, Untagged) then
  begin
    TotalGlyphs := 0;
    if Pdf.ExtractLoadedPageGlyphs(PageIndex, Glyphs) then
      TotalGlyphs := Length(Glyphs);
    // Percayai pohon struktur hanya ketika ia mengklaim sebagian besar halaman
    if (TotalGlyphs = 0) or (Untagged * 4 <= TotalGlyphs) then
    begin
      UsedStructure := True;
      Result := True;
      Exit;
    end;
  end;
  Result := Pdf.ExtractLoadedPageText(PageIndex, AText);
end;

Apa yang membuat fungsi mengembalikan False

Tiga kasus, dan ketiganya layak dibedakan karena hanya satu yang merupakan defek pada dokumen. Yang pertama adalah PDF tanpa tag biasa: tanpa /StructTreeRoot, tidak ada yang ditelusuri, dan False sekadar kebenarannya. Yang kedua adalah halaman hasil pindai yang teksnya datang dari lapisan OCR yang tidak pernah ditandai. Yang ketiga adalah yang menarik: konten yang membawa operator BDC dengan nilai /MCID tetapi halamannya tidak punya entri /StructParents dan pohon strukturnya tidak pernah merujuk identifier-identifier itu. Marked content-nya ada, sisi strukturnya tidak, dan tidak ada urutan untuk dipulihkan. HotPDF melaporkan False alih-alih mengarang satu

Kasus terakhir itu muncul di berkas yang disunting tangan dan di output dari tool yang memancarkan marked content untuk keperluan optional-content atau artifact tanpa membangun pohon struktur. Jika Anda sendiri memproduksi PDF bertag, asimetri yang sama adalah yang diperiksa oleh validasi PDF/UA, dan padanan sisi penulisnya dibahas dalam layout DOM yang memancarkan output bertag berhalaman

Di mana urutan struktur membayar dirinya sendiri

Audit aksesibilitas adalah yang paling jelas: jika Anda mensertifikasi dokumen terhadap PDF/UA, urutan baca yang akan diumumkan screen reader persis adalah urutan struktur, sehingga mengekstraknya adalah cara Anda meninjaunya tanpa screen reader. Penangkapan data adalah kasus komersial yang lebih besar. Form pemerintahan bertag, pengungkapan yang diatur, dan lampiran e-invoice membawa label dan nilai field dalam urutan yang dideklarasikan, dan membacanya dalam urutan itu menghapus satu kelas utuh bug pemetaan yang diciptakan ekstraksi geometris pada layout multi-kolom

Konsumen terbaru adalah retrieval untuk model bahasa. Chunking dokumen untuk embedding hanya sebaik urutan teksnya, dan chunk yang menyambung dua kolom menghasilkan kalimat yang tidak pernah ada. Ekstraksi urutan struktur adalah perbaikan termurah yang tersedia untuk itu, karena untuk dokumen bertag urutan yang benar sudah ada di berkas dan hanya perlu dibaca

HotPDF adalah komponen VCL native untuk Delphi dan C++Builder, sehingga penelusuran pohon struktur dan pemutaran ulang glyph sama-sama berjalan in-process terhadap dokumen yang dimuat tanpa renderer eksternal yang terlibat. Detail API lengkap untuk keluarga ekstraksi dokumen yang dimuat ada di halaman produk HotPDF Delphi PDF component