PDFium Component mengubah PDF berlayout tetap menjadi model semantik yang dapat di-reflow, menggunakan BuildReflowDocument, dan mengekspor model tersebut sebagai HTML mandiri melalui ToHtml. Heading tetap menjadi heading, item list tetap menjadi item list, dan table yang terdeteksi pada halaman keluar sebagai markup table sungguhan dengan header cell dan span yang dipertahankan. Tidak ada apa pun dalam output yang merujuk ke skrip atau stylesheet eksternal
Alasan menginginkan ini adalah bahwa halaman PDF merupakan sekumpulan glyph yang diposisikan, yang justru salah untuk layar ponsel, screen reader, atau indeks pencarian. Setiap upaya menyelesaikannya dengan mengekstraksi teks polos kehilangan struktur yang membuat dokumen itu dapat dibaca, dan setiap upaya menyelesaikannya dengan mengonversi halaman menjadi gambar kehilangan teksnya sama sekali. Model reflow mempertahankan keduanya: kata-kata dan hubungan di antaranya
Dari mana informasi semantik berasal?
Semuanya dimulai dari GetStructuredText, satu-satunya sumber teks dan semantik dalam komponen ini. Ketika PDF membawa structure tree, tagged PDF sebagaimana didefinisikan dalam ISO 32000-1 klausul 14.7, model ini mengikuti hierarki logis yang dicatat produser. Ketika tidak, dan sebagian besar PDF di dunia nyata memang tidak, model jatuh kembali ke urutan layout fisik yang sudah dihitung untuk keperluan urutan baca
Pilihan itu menjaga batasan yang tegas: tidak ada parser PDF kedua dan tidak ada rendering engine kedua yang diperkenalkan untuk menjawab pertanyaan yang sudah bisa dijawab oleh yang sudah ada. Mekanisme urutan baca yang mendasarinya dijelaskan dalam blok teks terstruktur dan urutan baca, dan model reflow adalah lapisan semantik di atasnya, bukan penggantinya
Setiap node mencatat dari mana informasinya berasal, sehingga konsumen dapat membedakan heading yang dideklarasikan dokumen dari heading yang disimpulkan heuristik layout. Pipeline yang sensitif terhadap confidence harus membaca field itu alih-alih memperlakukan semua node sebagai sama-sama otoritatif
Pohon datar, dan mengapa itu bukan pohon objek
Model ini adalah pohon pre-order yang diratakan: sebuah array node di mana setiap node membawa ParentIndex dan Depth, alih-alih rekaman rekursif atau object graph dengan kepemilikan. Halaman, heading, paragraf, list, item list, figure, caption, table, baris, dan cell semuanya hidup dalam satu array linear itu
Ada dua manfaat yang mengikutinya. Konsumen dapat men-stream array tersebut secara berurutan tanpa rekursi, yang membuat penerbitan HTML, Markdown, atau tampilan pohon menjadi loop sederhana. Dan layout-nya tetap portabel di Delphi, C++Builder, dan Free Pascal, yang berbeda dalam cara mereka menangani tipe managed rekursif lintas batas ABI. Rekaman rekursif dari dynamic array adalah tepat jenis konstruksi yang berhasil dikompilasi di mana-mana dan berperilaku sedikit berbeda di masing-masing
uses
PDFium;
var
Pdf: TPdf;
Options: TPdfReflowOptions;
Doc: TPdfReflowDocument;
I: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'report.pdf';
Pdf.LoadDocument;
Options := TPdfReflowOptions.Default;
Options.FullDocument := True;
Options.DetectTables := True;
Options.IncludeCss := True; // blok style inline, tanpa berkas eksternal
Options.MaxNodes := 200000; // budget fail-closed
Options.MaxCharacters := 4000000;
Doc := Pdf.BuildReflowDocument(Options);
for I := 0 to High(Doc.Nodes) do
case Doc.Nodes[I].Kind of
prnkHeading:
Writeln(Format('%sH%d: %s', [StringOfChar(' ', Doc.Nodes[I].Depth),
Doc.Nodes[I].HeadingLevel, Doc.Nodes[I].Text]));
prnkParagraph:
Writeln(Format('%sp: %s', [StringOfChar(' ', Doc.Nodes[I].Depth),
Copy(Doc.Nodes[I].Text, 1, 60)]));
prnkTable:
Writeln(Format('table on page %d', [Doc.Nodes[I].PageNumber]));
end;
Writeln(Format('%d node(s), %d table(s), %d character(s)',
[Length(Doc.Nodes), Doc.TableCount, Doc.CharacterCount]));
finally
Pdf.Free;
end;
end;
Bagaimana table dijaga agar tidak muncul dua kali?
Deteksi table berjalan setelah teks terstruktur dikumpulkan untuk sebuah halaman, yang menciptakan bahaya nyata: konten cell yang sama ada baik dalam blok teks maupun dalam table yang terdeteksi. Menerbitkan keduanya menghasilkan HTML di mana setiap table diikuti isinya sendiri lagi sebagai paragraf lepas
Aturan yang menyelesaikannya bersifat geometris. Ketika table yang terdeteksi mencakup lebih dari separuh area sebuah blok teks, node table menggantikan blok tersebut alih-alih bergabung dengannya. Pengindeksan cell di dalam sebuah baris dibangun dengan menghitung ke dalam bucket, sehingga membangun modelnya tetap linear terhadap jumlah cell ditambah baris alih-alih memindai ulang setiap cell untuk setiap baris, yang penting pada dokumen finansial di mana satu halaman dapat membawa ratusan cell
Struktur yang terdeteksi jujur soal statusnya sebagai deteksi. Table dengan garis ruling dikenali lebih andal daripada yang sejajar murni lewat whitespace, dan confidence node mencerminkan itu. Untuk konten di mana table yang salah lebih baik daripada tanpa table, biarkan deteksi tetap aktif; untuk konversi arsip di mana table yang salah lebih buruk, batasi berdasarkan confidence
Mengekspor HTML yang tetap mandiri
ToHtml menelusuri model yang sudah dibangun dan tidak pernah mengunjungi kembali PDFium, sehingga mengekspor dua kali tidak menambah biaya apa pun dan tidak dapat menghasilkan hasil yang berbeda dari model yang sama. Nilai teks dan atribut di-escape secara seragam, level heading dibatasi ke rentang h1 hingga h6 yang benar-benar didefinisikan HTML, dan header cell, RowSpan, serta ColumnSpan diteruskan apa adanya
CSS opsional adalah blok style inline biasa. Tidak ada skrip, tidak ada web font, dan tidak ada resource eksternal apa pun, yang membuat outputnya aman untuk disematkan dalam email, help viewer, atau kontrol browser yang di-sandbox:
var
Html: WideString;
Stream: TFileStream;
Bytes: TBytes;
begin
Options := TPdfReflowOptions.Default;
Options.FullDocument := True;
Options.IncludeCss := True;
Options.IncludePageSections := True; // pertahankan batas halaman tetap terlihat
Options.PreserveLineBreaks := False; // biarkan browser membungkus paragraf
Html := Pdf.BuildReflowDocument(Options).ToHtml;
Bytes := TEncoding.UTF8.GetBytes(string(Html));
Stream := TFileStream.Create('report.html', fmCreate);
try
if Length(Bytes) > 0 then
Stream.WriteBuffer(Bytes[0], Length(Bytes));
finally
Stream.Free;
end;
end;
PreserveLineBreaks adalah opsi yang paling layak dipikirkan. Line break PDF adalah keputusan typesetting yang dibuat untuk lebar halaman tetap, sehingga mempertahankannya pada layar sempit mereproduksi persis masalah yang ingin diselesaikan reflow. Pertahankan break untuk puisi, listing kode, dan alamat; buang untuk prosa
Budget, pembatalan, dan status halaman
Karakter, node, table, dan cell masing-masing memiliki batas atas, dan masing-masing diperiksa sebelum alokasi, bukan sesudahnya, sehingga dokumen yang cacat atau jahat gagal dengan bersih alih-alih menghabiskan memori sampai hal lain terjadi. Token pembatalan diperiksa pada batas halaman, blok, table, baris, dan cell, yang menjaga scan dokumen seribu halaman yang dibatalkan tetap responsif
Satu perilaku penting khusus untuk aplikasi GUI: seluruh scan dokumen berjalan di dalam scope yang memulihkan halaman aktif, sehingga keberhasilan, kegagalan budget, dan pembatalan semuanya meninggalkan halaman saat ini milik pemanggil tetap tidak tersentuh. Penampil yang membiarkan pengguna mengekspor sambil melihat halaman 340 mendapati dirinya masih di halaman 340 sesudahnya
Untuk apa reflow bagus, dan untuk apa ia bukan
Output reflow adalah input yang sangat baik untuk pengindeksan pencarian, tampilan baca yang aksesibel, tampilan mobile, dan migrasi konten. Ini bukan konverter yang mempertahankan fidelitas: posisi absolut, font persis, artwork vektor, dan geometri halaman yang presisi berada di luar tujuannya secara desain. Ketika sebuah pekerjaan memerlukan halaman yang tampak sama, render halamannya; ketika memerlukan halaman yang dapat dibaca di tempat lain, reflow-kan
Untuk teknologi bantu secara khusus, model reflow ini berpasangan dengan fitur pembacaan yang dijelaskan dalam membangun pembaca yang aksesibel, dan dokumen yang membawa structure tree sungguhan menghasilkan model yang jauh lebih baik, yang menjadi argumen kuat untuk memvalidasi tagging di hulu seperti dijelaskan dalam validasi structure tree PDF/UA
Reflow, teks terstruktur, validasi tagging, dan rendering berbagi satu objek dokumen di Delphi, C++Builder, dan Lazarus; API lengkapnya dijelaskan pada halaman PDFium Component untuk Delphi