Artikel Teknis

Rendering PDF Multi-Engine di Delphi: Bawaan, Cairo, dan PDFium dengan PDF Library for Delphi

Tiga rasterizer bisa membaca PDF yang sama dan berbeda pendapat tentang apa isinya. Mesin bawaan di PDF Library for Delphi adalah yang datang tanpa berkas tambahan dan merender segalanya dengan kompeten, dan karena itulah ia berhak menempati slot default. Cairo membawa pipeline transparansi dan anti-aliasing yang berbeda, dan cenderung menjadi pilihan orang ketika soft mask atau blend mode keluar salah di tempat lain. PDFium membawa kode rendering Chrome, sehingga halaman yang tampak benar di peramban biasanya juga tampak benar di bawah PDFium, dengan ongkos berupa DLL yang lumayan besar dan bitness yang harus dicocokkan. Tak satu pun dari ketiganya benar secara abstrak. Kebenaran itu per dokumen, dan satu-satunya cara jujur untuk mengetahui mesin mana yang menangani suatu korpus adalah menjalankan korpus itu melalui masing-masingnya

Itulah alasan memperlakukan pilihan mesin sebagai keputusan runtime dan bukan keputusan saat build. PDF Library for Delphi, pustaka PDF untuk Delphi dan C++Builder dari losLab, menempatkan ketiganya di balik satu permukaan rendering sehingga keputusan itu hanya berharga satu integer alih-alih sebuah cabang kode. Sisanya berujung pada memilih di antara mereka dengan aman, memastikan mesin apa saja yang sesungguhnya dibawa biner yang sudah terpasang, dan menjaga keadaan rendering agar tidak diam-diam meracuni pekerjaan berikutnya

Tiga rasterizer di balik satu permukaan pemanggilan

Pustaka ini menomori mesin-mesinnya. Mesin 1 adalah perender bawaan, si default, dengan opsi penghalusan GDI+ di Windows. Mesin 2 adalah Cairo dan mesin 3 adalah PDFium, keduanya dipilih saat runtime lewat SelectRenderer. Kedua mesin eksternal itu dimuat dari DLL yang path-nya Anda pasok dengan SetCairoFileName dan SetPDFiumFileName sebelum memilihnya. Mesin mana pun yang aktif, pekerjaannya melewati pemanggilan yang sama: RenderPageToFile, RenderPageToStream, RenderDocumentToFile. Mengganti mesin hanya menggeser satu angka; sisa kode rendering Anda tidak pernah menyadarinya

Model tujuannya menjangkau jauh melampaui bitmap. Kelas perender juga menyasar metafile (WMF, EMF, EMF+), EPS, device context langsung, printer, dan HTML5, dengan Cairo dan PDFium muncul sebagai tujuan tambahan hanya bila keduanya ikut dikompilasi. Keluaran raster adalah tempat ketiga mesin itu paling kentara berbeda, jadi itulah yang dipakai contoh-contoh di sini

Tiga mesin rendering PDF di belakang satu permukaan pemanggilan: SelectRenderer berpindah antara mesin bawaan, Cairo, dan PDFium sementara kode aplikasi tetap memanggil fungsi render yang sama
SelectRenderer menukar satu integer untuk memindahkan pekerjaan antara engine bawaan, Cairo, dan PDFium. Kode aplikasi tetap memanggil RenderPageToFile dan kawan-kawannya apa pun engine yang menghasilkan pikselnya

Jangan pernah mengandaikan sebuah mesin ada: selidiki saat startup

Cairo dan PDFium adalah fitur kompilasi bersyarat, artinya sebuah biner bisa dibangun sepenuhnya tanpa keduanya. Ketika itu terjadi, meminta mesin 2 atau 3 tidak menimbulkan apa pun. SelectRenderer sekadar mengembalikan nilai selain ID yang Anda minta, dan kode yang mengabaikan nilai kembalian itu akan terus merender dengan mesin apa pun yang sudah aktif. Pertahanannya adalah penyelidikan saat startup yang meminta tiap mesin memperkenalkan diri lalu mencatat jawabannya:

function ProbeEngines(PDF: TPDFlib): string;
begin
  Result := 'built-in';                        // mesin 1 selalu ada
  if (PDF.SetCairoFileName('cairo.dll') = 1) and (PDF.SelectRenderer(2) = 2) then
    Result := Result + ', cairo';
  if (PDF.SetPDFiumFileName('pdfium.dll') = 1) and (PDF.SelectRenderer(3) = 3) then
    Result := Result + ', pdfium';
  PDF.SelectRenderer(1);                       // kembalikan default sebelum kerja sungguhan
end;

Jalankan penyelidikan itu sekali saat startup dan tulis hasilnya ke log berdampingan dengan setiap pekerjaan render. Pertanyaan paling umum ketika pelanggan melaporkan perbedaan rendering adalah mesin apa saja yang sebenarnya dimiliki instalasi mereka, dan jawaban satu baris yang sudah duduk di log menuntaskannya tanpa sesi remote desktop. Satu efek samping yang berguna: bila SetPDFiumFileName sendiri mengembalikan 0, Anda sudah tahu masalahnya ada pada DLL itu (path salah, bitness salah, dependensi hilang) dan bukan pada biner yang dikompilasi tanpa dukungan PDFium, sebab pemanggilan path itu tidak meresolusi apa pun bahkan sebelum SelectRenderer sempat berjalan

Sepuluh format keluaran di balik satu integer Options

Parameter Options pada pemanggilan render memilih pengodean keluaran: 0 adalah BMP, 1 JPEG, 2 WMF, 3 EMF, 4 EPS, 5 PNG, 6 GIF, 7 TIFF, 8 EMF+, dan 9 HTML5. PNG (5) adalah default yang masuk akal untuk pratinjau dan citra halaman arsip. JPEG (1), dipadukan dengan SetJPEGQuality, adalah pilihan lebih baik untuk pindaian fotografis di mana ukuran berkas lebih penting daripada ketajaman tepi

Satu format menyimpan syarat tentang stream tujuannya. Jalur BMP menulis data citra lebih dulu, lalu melompat balik ke offset 0x26 untuk menambal field resolusi di header. Arahkan itu ke stream yang hanya bisa maju, sebuah pembungkus kompresi atau soket jaringan, dan pemanggilannya gagal dengan cara yang terbaca seperti kesalahan mesin padahal bukan. Ketika target yang tak bisa di-seek tidak terhindarkan, render PNG saja, atau tampung BMP-nya lewat memory stream lalu salin maju setelah lengkap

DPI yang Anda oper bukanlah DPI yang Anda dapat

Setiap pemanggilan render menerima argumen DPI, tetapi resolusi yang benar-benar Anda peroleh adalah nilai itu dikalikan skala render global. SetRenderScale bermula di 1.0, dan begitu Anda mengubahnya, faktor baru itu diam-diam berlaku untuk setiap render berikutnya pada instance tersebut:

PDF.SetRenderScale(2.0);                    // setiap render berikutnya digandakan
PDF.RenderPageToFile(150, 1, 5, 'p1.png');  // efektifnya 300 DPI
PDF.SetRenderScale(1.0);                    // reset, atau thumbnail Anda datang raksasa

Kelengketan yang sama berlaku untuk SetRenderCropType dan pengaturan kualitas JPEG. Dalam sebuah layanan yang menghasilkan thumbnail, pratinjau, dan citra resolusi cetak dari satu instance bersama, pengaturan sisa inilah yang sebenarnya berdiri di balik tiket "thumbnail tiba-tiba 40 MB" yang sesekali muncul. Dua jalan keluar yang bersih: reset keadaan yang relevan di awal setiap operasi, atau dedikasikan instance terpisah untuk tiap profil keluaran sehingga tidak ada yang bocor di antara mereka

PDF Library for Delphi: Flowchart probe mesin saat startup: setiap renderer mengonfirmasi path DLL-nya dan jawaban SelectRenderer-nya sebelum ringkasan ketersediaan dicatat di samping setiap pekerjaan render
Panggilan path yang gagal menuduh DLL, sementara hasil SelectRenderer yang tak cocok berarti binary tidak pernah mengompilasi engine tersebut. Probe berjalan sekali dan ringkasan satu barisnya menuntaskan sebagian besar pertanyaan rendering pelanggan

Menyetel mesin default sebelum menjangkau yang lain

Porsi yang mengejutkan dari permintaan "kami butuh mesin lain" ternyata adalah masalah pengaturan yang sedang menyamar. Perender bawaan memaparkan perilaku penghalusannya lewat SetGDIPlusOptions dan keluarga SetRenderOptions yang lebih luas, dan SetGDIPlusFileName memungkinkan Anda mengarahkannya ke runtime GDI+ tertentu ketika lingkungan penerapan membawa versi yang tidak biasa. Garis gambar bergerigi pada DPI rendah, teks kabur di thumbnail, banding pada gradien: semuanya menanggapi tombol-tombol itu, dan memutarnya tidak menambah beban apa pun di installer. Menambahkan Cairo atau PDFium, sebaliknya, berarti mengirim lebih banyak DLL, melacak varian bitness kedua atau ketiga, dan memikul kewajiban memperbaruinya

Maka keluhan kualitas punya urutan langkah yang alami. Reproduksi dulu pada DPI dan skala persis milik pelanggan, sebab separuh waktunya perbedaan itu menguap begitu keduanya cocok. Berikutnya, coba opsi penghalusan mesin bawaan. Baru setelah itu sandingkan halamannya antar mesin dengan semua variabel lain ditahan tetap: render ke PNG lewat mesin 1, 2, dan 3 pada DPI identik lalu lampirkan ketiganya. Biasanya dua dari tiga sepakat, dan mayoritas itu memberi tahu Anda apakah si pencilan adalah dokumen yang ditafsirkan berbeda atau justru ekspektasi dasar Anda sendiri yang meleset. Tiga citra konkret menuntaskan sengketa "rendernya salah" jauh lebih cepat daripada satu paragraf penuh kata sifat

Rantai fallback yang menjelaskan dirinya sendiri

Begitu penyelidikan dan disiplin keadaan sudah di tempatnya, rantai fallback-nya sendiri pendek. Mendeteksi kegagalan bersandar pada LastRenderError, yang memuat teks pesan dari mesin itu sendiri untuk render terakhir dan kosong ketika render berhasil:

procedure RenderPageWithFallback(PDF: TPDFlib; Page: Integer; const OutFile: string);
begin
  PDF.SelectRenderer(1);                            // bawaan lebih dulu
  PDF.RenderPageToFile(200, Page, 5, OutFile);      // 5 = PNG
  if PDF.LastRenderError = '' then Exit;
  LogEngineFailure('built-in', Page, PDF.LastRenderError);
  if PDF.SelectRenderer(3) = 3 then                 // PDFium sebagai fallback berat
  begin
    PDF.RenderPageToFile(200, Page, 5, OutFile);
    if PDF.LastRenderError = '' then Exit;
    LogEngineFailure('pdfium', Page, PDF.LastRenderError);
  end;
  raise Exception.CreateFmt('Page %d failed on all available engines', [Page]);
end;

Dua poin desain memikul bobot di sini. Rantai itu mencatat mengapa setiap peralihan terjadi, sebab baris log yang berbunyi "halaman ini jatuh ke PDFium sejak rilis 3.7" adalah sinyal regresi yang sebaiknya tampak sebagai tren di pemantauan alih-alih hilang. Urutan fallback-nya sendiri adalah kebijakan yang layak dipilih per beban kerja. Mesin bawaan terpasang tanpa DLL tambahan, dan itu menjadikannya percobaan pertama yang tepat di sebagian besar instalasi, sementara dokumen yang sarat grup transparansi atau shading tak lazim adalah alasan lazim sebuah tim memasang mesin alternatif sejak awal. Tidak ada mesin yang paling cepat secara umum, dan itulah seluruh alasan memilih per pemanggilan: uji tolok masing-masing terhadap sampel dokumen nyata Anda pada DPI nyata Anda, dan tinjau ulang pengukuran itu setiap kali DLL mesin atau campuran dokumennya berubah. Korpuslah yang selalu memenangkan perdebatan

Rantai fallback render PDF: mesin bawaan mencoba lebih dulu, kegagalan dicatat, PDFium mencoba ulang, dan exception yang dilempar melaporkan ketika semua mesin yang tersedia gagal pada suatu halaman
Setiap percobaan memeriksa LastRenderError dan mencatat alasannya sebelum berpindah engine. Hanya ketika setiap engine yang terpasang telah gagal, rantai itu melempar exception, dengan sebab-sebab yang terkumpul sudah berada di log

Melampaui halaman tunggal: batch TIFF dan device context langsung

Dua tetangga dari pemanggilan per halaman melengkapi kotak perkakas. RenderAsMultipageTIFFToFile merender sebuah ekspresi rentang halaman langsung menjadi TIFF multi-halaman, bentuk alami untuk serah terima arsip ke sistem manajemen dokumen yang lebih tua daripada PDF. RenderPageToDC melukis langsung ke device context Windows untuk kontrol pratinjau, diatur oleh trio pengaturan lengketnya sendiri (SetRenderDCOffset, SetRenderDCErasePage, ditambah tipe crop) yang menuntut disiplin reset yang sama seperti faktor skala. Pratinjau layar dan rendering jalur cetak membawa cukup banyak jebakan sendiri sehingga pantas mendapat artikel khusus, tertaut di bawah

Ke mana selanjutnya

Satu kebiasaan yang layak dibawa terus: karena SelectRenderer berlaku untuk setiap pemanggilan berikutnya pada instance itu, satu halaman keras kepala dapat dicoba ulang di mesin lain sementara sisa dokumennya tetap di default. Untuk pelukisan pratinjau, pemilihan printer, dan penanganan DevMode, lanjutkan dengan artikel pratinjau cetak dan device context. Ketika render memasok pipeline bervolume tinggi di atas berkas yang sangat besar, pendekatan berbasis handle di panduan direct-access berpasangan secara alami dengan rendering per halaman lewat DARenderPageToFile

Pengemasan mesin, format yang didukung, dan build uji coba diuraikan di halaman produk PDF Library for Delphi