Artikel Teknis

PDF ke Markdown dan DOCX di Delphi dengan PDFlibPas

PDFlibPas mengonversi konten PDF menjadi dua format yang bisa diedit tanpa Office automation. ExportPageMarkdown dan ExportDocumentMarkdown mengembalikan Markdown semantik dengan heading, ordered dan unordered list, serta pipe table yang diinferensikan, sementara SaveDOCXToFile dan SaveDOCXToStream menulis paket WordprocessingML yang berisi paragraf, heading, penomoran list native, tabel yang terdeteksi, font styling, page break, dan gambar PNG yang diposisikan

Keduanya berjalan sepenuhnya dalam Pascal, di server, tanpa Word terinstal dan tanpa COM. Batasan itulah alasan mengapa fitur ini ada di dalam pustaka PDF, bukan di dalam tool desktop

Mengapa "PDF to Word" sungguh-sungguh sulit?

Karena sebuah halaman PDF tidak berisi paragraf. Ia berisi text-showing operator yang menempatkan run glyph pada koordinat tertentu, dalam urutan apa pun yang dikeluarkan producer-nya, tanpa kewajiban untuk menunjukkan bahwa dua run itu termasuk kalimat yang sama, apalagi item list yang sama. Format ini dirancang untuk mendeskripsikan halaman cetak secara persis, dan ia berhasil melakukan itu dengan cara membuang struktur yang menghasilkan halaman tersebut

Jadi setiap converter harus merekonstruksi apa yang dibuang oleh generator. Pengelompokan baris berasal dari spacing vertikal dan baseline alignment. Batas paragraf berasal dari perubahan spacing dan indentasi. Sebuah heading adalah baris yang font-nya lebih besar atau lebih tebal daripada body dan yang berdiri terpisah dari apa yang mengikutinya. Sebuah list adalah rangkaian paragraf yang diawali karakter bullet atau pola angka. Sebuah tabel adalah grid dari text block yang tepinya sejajar di seluruh baris dan kolom. Setiap hal itu adalah sebuah inferensi, dan inferensi berarti hasil yang bagus pada dokumen yang mengikuti konvensi tipografi biasa dan hasil yang biasa saja pada dokumen yang tidak

PDF tagged adalah pengecualian, dan pengecualian yang besar. Saat dokumen membawa structure tree, peran paragraf, heading, list, dan tabel dicatat, bukan ditebak, yang menjelaskan mengapa pekerjaan aksesibilitas yang dijelaskan di struktur aksesibilitas PDF tagged juga terbayar dalam kualitas konversi. Jika Anda mengendalikan producer-nya, memberi tag pada output Anda adalah satu hal dengan leverage tertinggi yang bisa Anda lakukan untuk siapa pun yang nantinya harus mengonversinya

Ekspor Markdown, satu halaman pada satu waktu

Jalur Markdown adalah yang tepat dipilih saat tujuannya adalah text pipeline: sebuah situs dokumentasi, sebuah search index, atau sebuah retrieval corpus untuk assistant. Opsinya berupa bit mask: PDF_MARKDOWN_INCLUDE_PAGE_MARKERS, PDF_MARKDOWN_DETECT_HEADINGS, PDF_MARKDOWN_PRESERVE_STYLES, dengan PDF_MARKDOWN_DEFAULT menggabungkan ketiganya

var
  Pdf: TPDFlib;
  Md: WideString;
begin
  Pdf := TPDFlib.Create;
  try
    Pdf.LoadFromFile('handbook.pdf', '');

    // Satu halaman, sebagai string
    Md := Pdf.ExportPageMarkdown(1, PDF_MARKDOWN_DEFAULT);

    // Rentang halaman, di-stream ke disk sebagai UTF-8 tanpa BOM
    Pdf.SaveMarkdownToFile('1-40',
      PDF_MARKDOWN_DETECT_HEADINGS or PDF_MARKDOWN_PRESERVE_STYLES,
      'handbook.md');
  finally
    Pdf.Free;
  end;
end;

Page marker terbukti berguna dalam pekerjaan retrieval. Sepotong teks yang membawa halaman asalnya bisa dikutip secara presisi, dan pembaca yang mengikuti kutipan itu mendarat tepat di tempat klaim itu berada. Matikan page marker saat Markdown itu ditujukan untuk dibaca manusia, di mana batas halaman dari layout sumber hanyalah noise

Entry point streaming penting untuk dokumen besar. SaveMarkdownToStream dan SaveMarkdownToFile menulis UTF-8 satu halaman pada satu waktu dan tidak melakukan buffer terhadap seluruh output, sehingga manual 900 halaman tidak lebih dulu menjadi string 900 halaman di memori. Ketiadaan byte-order mark juga disengaja: BOM pada file Markdown membingungkan cukup banyak static site generator dan diff tool

DOCX tanpa Office di mesinnya

DOCX writer menghasilkan paketnya sendiri: entri ZIP yang ditulis sebagai raw Deflate dengan CRC check, bagian-bagian WordprocessingML, dan relationship yang mengikatnya. Tidak ada yang memanggil Word, yang berarti konversinya berjalan di server headless, di dalam service account, di dalam container, di semua tempat di mana Office automation entah tidak berlisensi, tidak stabil, atau dilarang

var
  Pdf: TPDFlib;
  Target: TFileStream;
begin
  Pdf := TPDFlib.Create;
  Target := TFileStream.Create('handbook.docx', fmCreate);
  try
    Pdf.LoadFromFile('handbook.pdf', '');
    Pdf.SaveDOCXToStream('1-40',
      PDF_DOCX_INCLUDE_IMAGES or PDF_DOCX_DETECT_HEADINGS or
      PDF_DOCX_PRESERVE_STYLES or PDF_DOCX_PRESERVE_PAGE_BREAKS,
      Target);
  finally
    Target.Free;
    Pdf.Free;
  end;
end;

Data gambar ditulis saat setiap halaman diproses, bukannya dikumpulkan lalu ditambahkan di akhir, sehingga peak memory hanya mengikuti satu halaman, bukan seluruh dokumen. Urutan halaman eksplisit dipertahankan, dan halaman PDF yang terpilih dipulihkan sesudahnya, yang penting saat ekspor ini hanya satu langkah di dalam job yang lebih panjang yang sebelumnya sudah memilih halaman untuk alasan lain

Apa yang didapatkan dari packaging yang deterministik?

Reproduksibilitas byte-for-byte. Dua konversi dari input yang sama dengan opsi yang sama menghasilkan paket yang sama, yang berarti Anda bisa melakukan hash pada output untuk mendeteksi perubahan, mem-diff dua build dari dokumen yang dihasilkan, dan melakukan cache secara agresif tanpa khawatir bahwa input yang identik menghasilkan artefak yang berbeda

Office automation tidak bisa menjanjikan itu. Ia menyematkan timestamp, revision identifier, dan metadata yang bergantung pada mesin, sehingga dokumen yang sama yang dikonversi dua kali berbeda dengan cara yang menggagalkan hashing. Alasan yang sama mendasari deterministic file identifier yang dibahas di deterministic PDF ID untuk reproducible build: saat output bersifat reproducible, verifikasi berubah menjadi sekadar perbandingan alih-alih inspeksi

Di mana output-nya bagus, dan di mana tidak

Jujurlah kepada pengguna Anda soal ini, karena kualitas konversi lebih banyak bergantung pada input daripada pada converter-nya. PDF tagged dan dokumen bisnis yang dihasilkan dengan rapi, invoice, laporan, kontrak, dikonversi dengan baik: heading mendarat sebagai heading, tabel tetap utuh, list dinomori ulang dengan benar di Word. Layout akademis dua kolom dikonversi cukup baik jika geometri kolomnya teratur. Tabel yang melintasi page break dirangkai kembali lewat inferensi dan kadang terpecah. Materi marketing yang dirancang secara berat, di mana teks ditempatkan demi efek visual, bukan urutan baca, dikonversi dengan buruk, dan tidak ada jumlah inferensi apa pun yang bisa memperbaikinya

Dokumen hasil scan adalah kasus yang sama sekali berbeda. Halaman yang berupa satu gambar besar tidak berisi text object, sehingga tidak ada apa pun untuk diekspor sampai text layer ada; jalur OCR yang menghasilkannya adalah prasyarat, bukan opsi. Sebelum menjalankan batch besar, ambil sampel selusin file representatif dan lihat outputnya, dan pertimbangkan untuk mengenumerasi page element terlebih dahulu, seperti dijelaskan di pencarian teks dan enumerasi page element, untuk melihat apa yang sebenarnya dikandung halaman-halaman itu

Untuk pipeline assistant dan retrieval, jalur Markdown biasanya menjadi target yang lebih baik: heading menjadi batas chunk, tabel tetap terbaca sebagai pipe table, dan page marker memberi setiap chunk lokasi yang bisa dikutip. Untuk editing oleh manusia, DOCX adalah jawabannya, karena yang diinginkan pengguna bukan teksnya melainkan kemampuan untuk mengubahnya

PDFlibPas adalah pustaka PDF Delphi, C++Builder, dan Lazarus dengan interface DLL dan ActiveX yang setara, sehingga panggilan ekspor yang sama juga tersedia dari C#, C++, atau scripting host. Dokumentasi lengkap dan build trial ada di halaman pustaka PDF Delphi PDFlibPas