Di PDFium Component for Delphi, menyalakan BrotliEnabled atau IsolatePerDocument di TPdfLibraryConfiguration dulu menukar build Skia bawaan ke renderer AGG tanpa error apa pun, karena kedua opsi itu menaikkan FPDF_LIBRARY_CONFIG ke versi tempat PDFium membaca m_RendererType secara harfiah. Sejak v3.123.0, renderer default tetap default milik DLL sendiri, dan sejak v3.125.0, permintaan Skia atau Fontations yang tak bisa dipenuhi DLL memicu EPdfError yang bisa ditangkap alih-alih mematikan proses
Tak satu pun dari kedua bug itu mengumumkan dirinya. Yang pertama menghasilkan halaman yang tampak baik, sekadar dirender rasterizer berbeda, dengan anti-aliasing dan tepi teks sedikit berbeda dari build yang Anda kirim dan tes. Yang kedua memang mengumumkan dirinya, dengan keras, dengan merobohkan proses host dari dalam inisialisasi native. Keduanya berasal dari tempat yang sama: struktur C berversi yang field-nya baru dihitung begitu nomor versinya bilang begitu, dan yang nilai nolnya bukan "tak diset" melainkan pilihan sungguhan
Bagaimana FPDF_LIBRARY_CONFIG memutuskan renderer mana yang dipakai PDFium?
FPDF_InitLibraryWithConfig berkonsultasi ke m_RendererType hanya ketika field Version struktur bernilai 4 atau lebih, dan dari versi itu ia memakai nilai apa adanya. Di bawah versi 4, PDFium mengabaikan field itu dan memilih default build, yaitu Skia pada build yang dikompilasi dengan PDF_USE_SKIA dan AGG di tempat lain
Setiap field berikutnya mengikuti pola yang sama. Struktur itu tumbuh satu kapabilitas demi satu kapabilitas, dan tiap kapabilitas datang bersama nomor versi baru. PDFium Component membangun struktur native di LoadLibrary dari TPdfLibraryConfiguration Anda dan menaikkan versi hanya sejauh yang dituntut opsi yang Anda set
| Versi struktur | Field yang ditambahkan | Disetel oleh |
|---|---|---|
| 2 | m_pIsolate, m_v8EmbedderSlot | Selalu ditulis; V8Isolate, V8EmbedderSlot |
| 3 | m_pPlatform | V8Platform bukan nil |
| 4 | m_RendererType | Renderer selain prpDefault |
| 5 | m_FontLibraryType | FontBackend selain pfbpDefault |
| 6 | m_BrotliEnabled | BrotliEnabled = True |
| 7 | m_IsolatePerDocument | IsolatePerDocument = True |
Jebakannya ada di dua baris terakhir. Versi itu kumulatif: struktur versi 6 juga struktur versi 4 dan versi 5, sehingga PDFium membaca m_RendererType dan m_FontLibraryType meski Anda hanya meminta Brotli. Apa pun yang berada di dua field itu pada saat itu menjadi renderer dan font backend, entah Anda bermaksud memilihnya atau tidak
Mengapa menyalakan Brotli menukar renderer ke AGG?
Sebelum v3.123.0, PDFium Component menulis FPDF_RENDERERTYPE_AGG ke m_RendererType untuk prpDefault, sehingga konfigurasi apa pun yang mendorong struktur ke versi 6 atau 7 memaksakan AGG pada build Skia. Runtime pdfium.dll dan pdfium.v8.dll yang dikirim bersama komponen adalah build Skia, jadi ini mengenai deployment default, bukan yang eksotis
Pemetaan itu tampak tak berbahaya saat ditulis. Pada versi 2 atau 3, field itu tak pernah dibaca, jadi prpDefault memang berarti "apa pun yang dilakukan DLL". Begitu BrotliEnabled (versi 6) atau IsolatePerDocument (versi 7) masuk ke dalam persamaan, kode yang sama mengubah "tanpa preferensi" menjadi permintaan AGG eksplisit. Tak ada yang gagal. PDFium terinisialisasi normal, merender setiap halaman, dan tak mengembalikan kode error, karena dari sudut pandangnya caller meminta AGG dan menerima AGG
Pixel hash membuat penukaran itu terlihat di tempat screenshot gagal. Merender halaman pertama dokumen sampel yang sama di bawah tiga konfigurasi menghasilkan:
- Konfigurasi default: hash
502D77C3711B4ACF BrotliEnabled= True denganRendererdibiarkan diprpDefault: hashF75B5EB4728ADE87prpAggeksplisit: hashF75B5EB4728ADE87, identik dengan run Brotli
Perbaikan di v3.123.0 adalah fungsi publik PdfNativeRendererType, yang me-resolve TPdfRendererPreference menjadi nilai yang ditulis ke m_RendererType. prpAgg dan prpSkia terpetakan satu-satu. prpDefault kini dipetakan ke Skia ketika DLL yang termuat mengekspor FPDF_RenderPageSkia dan ke AGG jika tidak. Export itu dikompilasi di bawah kondisi PDF_USE_SKIA yang sama dengan default Skia itu sendiri, yang menjadikannya satu-satunya properti build yang bisa Anda amati dari luar DLL. Setelah perbaikan, konfigurasi Brotli menghasilkan hash yang sama dengan konfigurasi default
Font backend tak pernah mengalami masalah yang sama. m_FontLibraryType dibaca mulai versi 5, dan nilai nolnya, FPDF_FONTBACKENDTYPE_FREETYPE, juga default PDFium ketika field itu sama sekali tak dibaca. Menulis FreeType untuk pfbpDefault karena itu mereproduksi default native secara persis. Nilai nol tak selalu salah, ia sekadar tak pernah otomatis benar
Dengan v3.123.0 atau lebih baru, kode startup yang secara alami akan Anda tulis kini berbuat sesuai ucapannya:
uses
PDFium;
procedure ConfigurePdfiumAtStartup;
var
Config: TPdfLibraryConfiguration;
begin
// Harus berjalan sebelum apa pun memuat library native
Config := TPdfLibraryConfiguration.Default;
Config.BrotliEnabled := True; // menaikkan FPDF_LIBRARY_CONFIG ke versi 6
// Renderer tetap prpDefault: ter-resolve ke Skia pada build yang mengekspor
// FPDF_RenderPageSkia dan ke AGG pada build AGG-saja
SetLength(Config.UserFontPaths, 1);
Config.UserFontPaths[0] := 'C:\ProgramData\MyApp\Fonts';
ConfigurePdfLibrary(Config);
end;
Ingat bahwa BrotliEnabled hanya membuat stream /BrotliDecode PDF 2.0 bisa didekode ketika DLL itu sendiri dibangun dengan PDF_ENABLE_BROTLI. Flag itu adalah permintaan, dan pada build tanpa dukungan Brotli ia tak berefek apa pun. TPdfLibraryConfiguration.Hardened sama dengan Default kecuali AllowMachineTime False, yang memblokir JavaScript dokumen dari membaca jam sesungguhnya; itu titik awal yang masuk akal untuk pemrosesan sisi server atas file yang tak terpercaya
Apa yang terjadi ketika Anda meminta backend yang tak dimuat DLL?
PDFium tak mengembalikan error untuk renderer atau font backend yang tak ada di build: FPDF_InitLibraryWithConfig menggagalkan CHECK native, yang di Windows muncul sebagai breakpoint exception dan, tanpa structured exception handler mengelilingi panggilan itu, mengakhiri proses. Header-nya mengatakan persis begitu, memperingatkan bahwa nilai yang tak didukung "akan gagal serupa dengan crash seketika"
Dua kasus konkretnya adalah build AGG-saja yang menerima FPDF_RENDERERTYPE_SKIA, dan build tanpa Fontations yang menerima FPDF_FONTBACKENDTYPE_FONTATIONS. Runtime Skia bawaan berada di kelompok kedua: ia merender dengan Skia tapi memakai FreeType untuk font. Meminta prpSkia bersama pfbpFontations kepadanya menghasilkan External exception 80000003 di sisi Delphi. Ketika debugger atau exception handler kebetulan menangkapnya, situasinya tetap tak bisa dipulihkan:
- PDFium tertinggal setengah terinisialisasi
- Konfigurasi seluruh proses sudah tersegel, sehingga
ConfigurePdfLibrarymenolak konfigurasi yang sudah diperbaiki - Mencoba ulang dengan konfigurasi berbeda di proses yang sama tak lagi mungkin
Ini kegagalan yang berlawanan dengan bug Brotli. Di sana field memegang nilai yang tak dipilih siapa pun dan PDFium diam-diam menerimanya. Di sini field memegang nilai yang dipilih caller dengan sengaja dan PDFium sama sekali tak menerima diskusi soal itu. Keduanya adalah masalah yang harus diselesaikan wrapper sebelum panggilan native, karena setelahnya tak ada lagi yang bisa ditangkap
Cara PDFium Component melakukan precheck Skia dan Fontations
Sejak v3.125.0, LoadLibrary memvalidasi konfigurasi setelah mengikat export DLL dan sebelum memanggil FPDF_InitLibraryWithConfig, serta mengubah renderer atau font backend yang tak didukung menjadi EPdfError dengan pesan yang menyebut setting bermasalah dan alternatifnya. DLL di-unload dan konfigurasi dibuka segelnya, sehingga caller bisa memilih setting lain dan memuat lagi
Keputusannya sendiri berada di fungsi murni PdfLibraryConfigurationSupportError, yang menerima konfigurasi plus dua Boolean yang mendeskripsikan build dan mengembalikan string kosong ketika kombinasinya aman. Karena tak menyentuh state native sama sekali, Anda bisa memanggilnya dari test Anda sendiri dengan kombinasi kapabilitas apa pun. Di dalam LoadLibrary, kedua Boolean itu datang dari jenis bukti yang berbeda, dan keduanya layak mendapat tingkat kepercayaan berbeda:
- Skia terdeteksi dari hadirnya export
FPDF_RenderPageSkia, sinyal yang sama dengan yang dipakaiPdfNativeRendererType. Export dan renderer Skia dikompilasi di bawah satu kondisi, sehingga pemeriksaannya presisi - Fontations tak punya export sendiri. Jejak satu-satunya adalah Rust font crate yang ia tarik ke dalam binary, sehingga PDFium Component memindai file library termuat mencari nama crate
skrifadanread-fonts(jugaread_fonts). Pemindaian hanya berjalan ketikapfbpFontationsdiminta, dan file yang tak bisa dibaca dihitung "tanpa Fontations"
Pemeriksaan Fontations adalah heuristik, dan ia bisa keliru ke satu arah: build Fontations yang seluruh string itu dihapus darinya akan ditolak meski sebenarnya bisa bekerja. Trade itu diambil dengan sengaja. Penolakan palsu merampas satu exception yang bisa Anda tangkap dan satu fallback ke FreeType. Penerimaan palsu merampas proses Anda
Membuka segel sama pentingnya dengan pemeriksaannya. LoadLibrary menyegel konfigurasi di awal sekali proses pemuatan, sehingga tanpa reset itu, penolakan kapabilitas akan membiarkan ConfigurePdfLibrary menjawab setiap retry dengan EPdfError "konfigurasi library PDFium sudah tersegel". Jalur penolakan memanggil UnloadLibrary lebih dulu; panggilan FPDF_DestroyLibrary-nya aman pada titik itu karena PDFium belum terinisialisasi dan langsung kembali. Kegagalan muat lain, seperti DLL hilang atau ketidakcocokan arsitektur, mempertahankan segelnya, sehingga loop retry harus bisa membedakan keduanya:
uses
SysUtils, PDFium;
function StartPdfiumPreferringSkia: TPdfRendererPreference;
var
Config: TPdfLibraryConfiguration;
begin
Config := TPdfLibraryConfiguration.Default;
Config.Renderer := prpSkia;
ConfigurePdfLibrary(Config);
try
PDFium.LoadLibrary; // berkualifikasi unit: Windows.LoadLibrary bernama sama
Result := prpSkia;
except
on E: EPdfError do
begin
// Penolakan kapabilitas meng-unload DLL dan membuka segel konfigurasi.
// DLL yang sama sekali gagal dimuat tetap tersegel: mencoba ulang tak menolong
if PdfLibraryConfigurationSealed then
raise;
Config.Renderer := prpAgg;
ConfigurePdfLibrary(Config);
PDFium.LoadLibrary;
Result := prpAgg;
end;
end;
end;
Perhatikan PDFium.LoadLibrary yang eksplisit. Di unit yang juga memakai Windows atau Winapi.Windows, LoadLibrary tanpa kualifikasi ter-resolve ke unit mana pun yang muncul terakhir di uses clause; ketika itu fungsi Win32, panggilan tanpa parameter gagal dikompilasi dengan error jumlah argumen yang tak mengatakan apa-apa soal PDFium
Validasi yang terjadi bahkan lebih awal
ConfigurePdfLibrary menolak beberapa kombinasi sebelum DLL mana pun terlibat, semuanya dengan EPdfError. FontBackend eksplisit, termasuk pfbpFreeType, mensyaratkan Renderer = prpSkia, karena PDFium hanya berkonsultasi ke font backend untuk renderer Skia. IsolatePerDocument mensyaratkan V8Isolate nil, karena PDFium menciptakan isolate-nya sendiri per dokumen dan menggagalkan CHECK native kalau Anda juga menyerahkannya satu. String kosong di UserFontPaths ditolak. Dan panggilan apa pun setelah percobaan muat pertama gagal dengan "konfigurasi library PDFium sudah tersegel"
Aturan terakhir itu punya konsekuensi praktis: Anda tak bisa mengintip DLL lebih dulu lalu mengonfigurasinya sesudahnya. GetSkiaRenderCapabilities, V8FeaturesAvailable, membuka dokumen, dan kebanyakan entry point lain memanggil LoadLibrary secara internal, yang menyegel konfigurasi di tempat. Memanggil UnloadLibrary belakangan juga tak membukanya kembali. Konfigurasi dulu, lalu muat, lalu bertanya, persis urutan yang seharusnya diikuti routine diagnostik:
uses
SysUtils, PDFium, FPdfView;
function DescribePdfiumState: string;
var
Config: TPdfLibraryConfiguration;
Renderer: string;
begin
Config := GetPdfLibraryConfiguration; // salinan, aman untuk diperiksa
if not PDFium.Loaded then
begin
if PdfLibraryConfigurationSealed then
Exit('PDFium failed to load; configuration is sealed');
Exit('PDFium not loaded; configuration can still change');
end;
// Resolusi yang sama dengan yang diterapkan LoadLibrary saat membangun FPDF_LIBRARY_CONFIG
if PdfNativeRendererType(Config.Renderer,
GetSkiaRenderCapabilities.PageRender) = FPDF_RENDERERTYPE_SKIA then
Renderer := 'Skia'
else
Renderer := 'AGG';
Result := Format('Renderer=%s Brotli=%s IsolatePerDocument=%s',
[Renderer, BoolToStr(Config.BrotliEnabled, True),
BoolToStr(Config.IsolatePerDocument, True)]);
end;
Mencatat baris itu sekali saat startup murah, dan itu hal pertama yang Anda inginkan di tiket dukungan yang berbunyi "teksnya tampak berbeda di server". PDFium.Loaded berkualifikasi unit karena alasan yang sama dengan LoadLibrary: di dalam method form atau komponen, Loaded polos terikat ke TComponent.Loaded
Dua cara struct konfigurasi C berversi keliru
Setiap struktur konfigurasi berversi, entah FPDF_LIBRARY_CONFIG, record cbSize Win32, atau ABI plugin, gagal dengan dua cara simetris, dan wrapper harus berjaga terhadap keduanya. Yang pertama mengisi field sambil membiarkan versi terlalu rendah; yang kedua menaikkan versi sambil membiarkan field pada nilai nol yang oleh library dibaca sebagai pilihan disengaja
- Field diset, versi terlalu rendah. Tulis
m_BrotliEnabled= 1 ke struktur versi 2 dan PDFium tak pernah melihatnya. Panggilan sukses dan stream Brotli tetap tak terdekode. Pertahanannya adalah menurunkan versi dari field yang benar-benar dipakai, itulah yang dilakukanLoadLibrary, alih-alih meng-hard-code satu angka - Versi cukup tinggi, field nol bermakna sesuatu. Naikkan versi ke 6 dan setiap field sampai versi 6 kini hidup.
FillCharmen-nol-kanm_RendererTypemenjadiFPDF_RENDERERTYPE_AGG, renderer sungguhan, bukan "tak diset". Pertahanannya adalah menulis setiap field yang dicakup versi terpilih dengan nilai yang disengaja, dan me-resolve "default" terhadap build aktual alih-alih mengasumsikannya
Aturan ketiga menyusul untuk nilai yang bisa membuat callee crash: validasilah terhadap apa yang sanggup dilakukan binary sebelum panggilan, memakai bukti terkuat yang tersedia, dan jujurlah di kode maupun dokumentasi ketika bukti itu heuristik. Simbol yang diekspor adalah bukti. Nama crate di string table adalah tebakan yang bagus
Referensi cepat: konfigurasi library PDFium Component
- Panggil
ConfigurePdfLibrarysekali, sebelum apa pun memuat DLL; kueri kapabilitas atau muat dokumen apa pun menyegelnya - Upgrade ke v3.123.0 atau lebih baru jika Anda menyetel
BrotliEnabledatauIsolatePerDocumentdan mengharapkan keluaran Skia dari runtime bawaan - Biarkan
RendererdiprpDefaultkecuali Anda butuh rasterizer tertentu; ia kini ter-resolve ke default build pada setiap versi struktur - Pakai
PdfNativeRendererTypedenganGetSkiaRenderCapabilities.PageRenderuntuk mencatat renderer mana yang benar-benar aktif - Harapkan
EPdfError, bukan crash, untukprpSkiapada DLL AGG-saja ataupfbpFontationspada DLL tanpa Fontations di v3.125.0 atau lebih baru - Setelah penolakan kapabilitas,
PdfLibraryConfigurationSealedFalse dan Anda boleh mengonfigurasi ulang; setelah kegagalan muat DLL ia tetap True - Perlakukan deteksi Fontations sebagai heuristik dan simpan fallback FreeType
- Tulis
PDFium.LoadLibrarydanPDFium.Loadeddengan nama unit untuk menghindari benturan nama Win32 danTComponent
Kalau DLL gagal sebelum konfigurasi sempat berperan, mulailah dari diagnosis kegagalan muat DLL PDFium di Delphi, dan untuk cara komponen menemukan binary yang tepat di tiap platform lihat memuat library native PDFium di target mana pun. Begitu renderer beres, taktik render cache dan zoom mulus membahas cara menjaga rendering halaman tetap cepat di viewer
PDFium Component membungkus engine PDFium untuk Delphi dan C++Builder dengan pemeriksaan konfigurasi semacam ini, sehingga inisialisasi native gagal sebagai exception Pascal yang bisa Anda tangani alih-alih eksekusi proses yang berakhir. Detail produk dan unduhan ada di halaman produk PDFium Component for Delphi