Delphi için PDFium Component'te TPdfLibraryConfiguration içinde BrotliEnabled ya da IsolatePerDocument'i açmak, paketlenmiş Skia derlemesini hatasızca AGG renderer'a geçiriyordu; çünkü iki seçenek de FPDF_LIBRARY_CONFIG'i, PDFium'un m_RendererType'ı harfi harfine okuduğu bir sürüme yükseltiyordu. v3.123.0'dan beri varsayılan renderer, DLL'nin kendi varsayılanı olarak kalır; v3.125.0'dan beri DLL'nin yerine getiremeyeceği bir Skia ya da Fontations isteği, süreci öldürmek yerine yakalanabilir bir EPdfError yükseltir
İki hata da kendini duyurmadı. Birincisi, iyi görünen sayfalar üretti; sadece gönderip test ettiğiniz derlemeden hafifçe farklı anti-aliasing ve metin kenarlarıyla, başka bir rasterizer tarafından render edilmiş. İkincisi kendini duyurdu; gürültüyle, ana süreci yerel başlatmanın içinden indirerek. İkisi de aynı yerden gelir: alanları yalnızca sürüm sayısı dediğinde sayılan, sıfır değerleri "unset" değil gerçek seçim olan sürümlü bir C yapısı
FPDF_LIBRARY_CONFIG, PDFium'un hangi renderer'ı kullanacağına nasıl karar verir?
FPDF_InitLibraryWithConfig, m_RendererType'a yalnızca yapının Version alanı 4 ve üzerindeyken bakar ve o sürümden itibaren değeri yazıldığı gibi kullanır. Sürüm 4'ün altında PDFium alanı yok sayar ve derleme varsayılanını seçer; PDF_USE_SKIA ile derlenmiş derlemelerde Skia, gerisinde AGG
Sonraki her alan aynı örüntüyü izler. Yapı birer yetenekle büyüdü ve her yetenek yeni bir sürüm numarasıyla birlikte geldi. PDFium Component, yerel yapıyı LoadLibrary'de sizin TPdfLibraryConfiguration'ınızdan kurar ve sürümü yalnızca set ettiğiniz seçeneklerin gerektirdiği kadar yükseltir
| Yapı sürümü | Eklediği alan | Kim set eder |
|---|---|---|
| 2 | m_pIsolate, m_v8EmbedderSlot | Daima yazılır; V8Isolate, V8EmbedderSlot |
| 3 | m_pPlatform | V8Platform nil değil |
| 4 | m_RendererType | prpDefault dışında bir Renderer |
| 5 | m_FontLibraryType | pfbpDefault dışında bir FontBackend |
| 6 | m_BrotliEnabled | BrotliEnabled = True |
| 7 | m_IsolatePerDocument | IsolatePerDocument = True |
Tuzak son iki satırda. Sürümler kümülatiftir: sürüm 6 bir yapı aynı zamanda sürüm 4 ve sürüm 5 bir yapıdır; dolayısıyla PDFium, yalnızca Brotli istemiş olsanız bile m_RendererType ile m_FontLibraryType'ı okur. O anda o iki alanda ne duruyorsa renderer ile font backend olur; onları seçmek isteseniz de istemeseniz de
Brotli'yi açmak renderer'ı neden AGG'ye geçirdi?
v3.123.0 öncesinde PDFium Component, prpDefault için m_RendererType'a FPDF_RENDERERTYPE_AGG yazıyordu; dolayısıyla yapıyı sürüm 6'ya ya da 7'ye iten her yapılandırma, bir Skia derlemesinde AGG'yi dayatıyordu. Bileşenle gelen pdfium.dll ile pdfium.v8.dll runtime'ları Skia derlemeleridir; dolayısıyla bu, egzotik bir dağıtımı değil varsayılan dağıtımı vurdu
Eşleme, yazıldığı sırada zararsız görünüyordu. Sürüm 2 ya da 3'te alan hiç okunmaz; dolayısıyla prpDefault gerçekten "DLL ne yaparsa" demekti. BrotliEnabled (sürüm 6) ya da IsolatePerDocument (sürüm 7) resme girdiği anda aynı kod, "tercih yok"u açık bir AGG isteğine çevirdi. Hiçbir şey başarısız olmadı. PDFium normal başlatıldı, her sayfayı render etti ve hiçbir hata kodu döndürmedi; çünkü onun bakış açısından çağıran AGG istemiş ve AGG almıştı
Bir pixel hash, değişimi ekran görüntülerinin göremediği yerde görünür kılar. Aynı örnek belgenin ilk sayfasını üç yapılandırma altında render etmek şunu verdi:
- Varsayılan yapılandırma: hash
502D77C3711B4ACF BrotliEnabled= True veRendererprpDefault'da bırakılmış: hashF75B5EB4728ADE87- Açık
prpAgg: hashF75B5EB4728ADE87, Brotli koşusuyla aynı
v3.123.0'daki düzeltme, TPdfRendererPreference'ı m_RendererType'a yazılan değere çözen PdfNativeRendererType isimli genel fonksiyondur. prpAgg ile prpSkia bire bir eşlenir. prpDefault artık, yüklenen DLL FPDF_RenderPageSkia'yı export ettiğinde Skia'ya, aksi hâlde AGG'ye eşlenir. O export, Skia varsayılanının kendisiyle aynı PDF_USE_SKIA koşulu altında derlenir; bu da onu, DLL'nin dışından gözlemleyebileceğiniz tek derleme özelliği yapar. Düzeltmeden sonra Brotli yapılandırması, varsayılanla aynı hash'i üretir
Font backend hiç aynı sorunu yaşamadı. m_FontLibraryType sürüm 5'ten itibaren okunur ve sıfır değeri olan FPDF_FONTBACKENDTYPE_FREETYPE, alan hiç okunmadığında PDFium'un varsayılanıdır. pfbpDefault için FreeType yazmak bu yüzden yerel varsayılanı aynen üretir. Sıfır değerleri her zaman yanlış değildir; sadece hiçbir zaman otomatik olarak doğru da değildirler
v3.123.0 ve sonrasıyla doğal olarak yazacağınız başlangıç kodu artık dediğini yapar:
uses
PDFium;
procedure ConfigurePdfiumAtStartup;
var
Config: TPdfLibraryConfiguration;
begin
// Herhangi bir şey yerel kütüphaneyi yüklemeden önce koşmalı
Config := TPdfLibraryConfiguration.Default;
Config.BrotliEnabled := True; // FPDF_LIBRARY_CONFIG'i sürüm 6'ya yükseltir
// Renderer prpDefault kalır: FPDF_RenderPageSkia'yı export eden derlemelerde
// Skia'ya, yalnızca-AGG derlemelerde AGG'ye çözülür
SetLength(Config.UserFontPaths, 1);
Config.UserFontPaths[0] := 'C:\ProgramData\MyApp\Fonts';
ConfigurePdfLibrary(Config);
end;
BrotliEnabled'in PDF 2.0 /BrotliDecode stream'lerini yalnızca DLL'nin kendisi PDF_ENABLE_BROTLI ile derlenmişse çözülebilir kıldığını unutmayın. Bayrak bir istektir ve Brotli desteği olmayan bir derlemede etkisizdir. TPdfLibraryConfiguration.Hardened, AllowMachineTime'ın False olması dışında Default ile aynıdır; bu da belge JavaScript'inin gerçek saati okumasını engeller ve güvenilmeyen dosyaların sunucu tarafı işlemesi için makul bir başlangıç noktasıdır
DLL'nin içermediği bir backend istediğinizde ne olur?
PDFium, derlemede eksik bir renderer ya da font backend için hata döndürmez: FPDF_InitLibraryWithConfig yerel bir CHECK'i düşürür; bu, Windows'ta bir breakpoint exception olarak görünür ve çağrının etrafında yapılandırılmış bir exception handler yoksa süreci sonlandırır. Header da aynı şeyi söyler; desteklenmeyen bir değerin "anında çökmeyle benzer biçimde başarısız olacağı" uyarısıyla
İki somut durum: FPDF_RENDERERTYPE_SKIA alan yalnızca-AGG bir derleme ve FPDF_FONTBACKENDTYPE_FONTATIONS alan Fontations'sız bir derleme. Paketlenmiş Skia runtime ikinci gruptadır: Skia ile render eder ama fontlar için FreeType kullanır. Ona karşı prpSkia ile pfbpFontations'ı birlikte istemek, Delphi tarafında External exception 80000003 üretti. Debugger ya da bir exception handler tesadüfen onu yakalasa bile durum hâlâ kurtarılamaz:
- PDFium yarı başlatılmış kalır
- Süreç geneli yapılandırma çoktan mühürlenmiştir; dolayısıyla
ConfigurePdfLibrary, düzeltilmiş bir yapılandırmayı reddeder - Aynı süreçte başka bir yapılandırmayla yeniden denemek artık mümkün değildir
Bu, Brotli hatasının tersi bir başarısızlıktır. Orada alan, kimsenin seçmediği bir değer taşıyordu ve PDFium onu sessizce kabul ediyordu. Burada alan, çağıranın bilerek seçtiği bir değer taşıyor ve PDFium hakkında hiçbir tartışma kabul etmiyor. İkisi de, yerel çağrıdan önce bir wrapper'ın çözmesi gereken sorunlardır; çünkü ondan sonra yakalanacak hiçbir şey kalmaz
PDFium Component Skia ile Fontations'ı nasıl ön-denetler?
v3.125.0'dan beri LoadLibrary, DLL export'larını bağladıktan sonra ve FPDF_InitLibraryWithConfig'i çağırmadan önce yapılandırmayı doğrular ve desteklenmeyen bir renderer ya da font backend'i, suçlu ayarı ile alternatifleri adıyla söyleyen bir EPdfError'a çevirir. DLL boşaltılır ve yapılandırmanın mühürleri açılır; böylece çağıran başka ayarlar seçip yeniden yükleyebilir
Kararın kendisi, yapılandırmayı ve derlemeyi tanımlayan iki Boolean'ı alan, kombinasyon güvenliyken boş string döndüren saf fonksiyon PdfLibraryConfigurationSupportError'da yaşar. Yerel duruma dokunmadığı için kendi testlerinizden herhangi bir yetenek kombinasyonuyla çağırabilirsiniz. LoadLibrary içinde o iki Boolean farklı türden kanıtlardan gelir ve farklı güven düzeylerini hak eder:
- Skia,
FPDF_RenderPageSkiaexport'unun varlığından saptanır;PdfNativeRendererType'ın kullandığı sinyal aynıdır. Export ile Skia renderer tek koşul altında derlenir; dolayısıyla kontrol kesindir - Fontations'ın kendi export'u yoktur. Bıraktığı tek iz, binary'ye çektiği Rust font crate'leridir; dolayısıyla PDFium Component, yüklenen kütüphane dosyasını
skrifaileread-fonts(veread_fonts) crate adları için tarar. Tarama yalnızcapfbpFontationsistendiğinde koşar ve okunamayan bir dosya "Fontations yok" sayılır
Fontations kontrolü bir sezgiseldir ve tek yönde yanılabilir: o string'lerin hepsinden arındırılmış bir Fontations derlemesi, işe yarayabilirdi ama reddedilirdi. O taviz bilinerek yapıldı. Yanlış bir reddin bedeli, yakalayabileceğiniz bir exception ve FreeType'a bir fallback'tir. Yanlış bir kabulün bedeli sürecin kendisidir
Mührü açmak, kontrol kadar önemlidir. LoadLibrary yapılandırmayı yüklemenin en başında mühürler; reset olmadan bir yetenek reddi, ConfigurePdfLibrary'nin her denemeye EPdfError "PDFium library configuration is already sealed" ile karşılık vermesi demek olurdu. Ret yolu önce UnloadLibrary çağırır; onun FPDF_DestroyLibrary çağrısı o noktada güvenlidir, çünkü PDFium henüz başlatılmamıştır ve hemen döner. Eksik bir DLL ya da mimari uyuşmazlığı gibi öteki yükleme başarısızlıkları mührü korur; dolayısıyla bir yeniden deneme döngüsü ikisini ayırt etmek zorundadır:
uses
SysUtils, PDFium;
function StartPdfiumPreferringSkia: TPdfRendererPreference;
var
Config: TPdfLibraryConfiguration;
begin
Config := TPdfLibraryConfiguration.Default;
Config.Renderer := prpSkia;
ConfigurePdfLibrary(Config);
try
PDFium.LoadLibrary; // unit nitelemeli: Windows.LoadLibrary aynı ada sahiptir
Result := prpSkia;
except
on E: EPdfError do
begin
// Yetenek reddi DLL'yi boşaltır ve yapılandırmanın mührünü açar.
// Hiç yükleyemeyen bir DLL mühürlü kalır: yeniden denemek işe yaramaz
if PdfLibraryConfigurationSealed then
raise;
Config.Renderer := prpAgg;
ConfigurePdfLibrary(Config);
PDFium.LoadLibrary;
Result := prpAgg;
end;
end;
end;
Açık PDFium.LoadLibrary'ye dikkat. Aynı zamanda Windows ya da Winapi.Windows kullanan bir unit'te nitelemsiz bir LoadLibrary, uses yan tümcesinde en sonda görünen unit'e çözülür; o Win32 fonksiyonu olduğunda parametresiz çağrı, PDFium hakkında hiçbir şey söylemeyen bir argüman sayısı hatasıyla derlenemez
Daha da erken gerçekleşen doğrulama
ConfigurePdfLibrary, herhangi bir DLL söz konusu olmadan bazı kombinasyonları reddeder; hepsi EPdfError ile. pfbpFreeType dâhil açık bir FontBackend, Renderer = prpSkia ister; çünkü PDFium font backend'ine yalnızca Skia renderer için bakar. IsolatePerDocument, V8Isolate'nin nil olmasını ister; PDFium her belge için kendi isolate'ini kurar ve siz ona bir tane daha uzatırsanız yerel bir CHECK düşürür. UserFontPaths'teki boş string'ler reddedilir. Ve ilk yükleme denemesinden sonraki her çağrı, "PDFium library configuration is already sealed" ile başarısız olur
Son kuralın pratik bir sonucu vardır: önce DLL'yi yoklayıp sonra yapılandıramazsınız. GetSkiaRenderCapabilities, V8FeaturesAvailable, bir belge açmak ve çoğu öteki giriş noktası içeride LoadLibrary çağırır; bu da yapılandırmayı anında mühürler. Sonradan UnloadLibrary çağırmak da onu yeniden açmaz. Önce yapılandırın, sonra yükleyin, sonra sorular sorun; tanısal bir rutinin izlemesi gereken düzen tam olarak budur:
uses
SysUtils, PDFium, FPdfView;
function DescribePdfiumState: string;
var
Config: TPdfLibraryConfiguration;
Renderer: string;
begin
Config := GetPdfLibraryConfiguration; // bir kopya, incelemesi güvenli
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;
// LoadLibrary'nin FPDF_LIBRARY_CONFIG'i kurarken uyguladığı aynı çözüm
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;
O satırı başlangıçta bir kez loglamak ucuzdur ve "metin sunucuda farklı görünüyor" diyen bir destek kaydında isteyeceğiniz ilk şeydir. PDFium.Loaded, LoadLibrary ile aynı nedenle nitelenmiştir: bir form ya da bileşen metodunun içinde çıplak bir Loaded, TComponent.Loaded'a bağlanır
Sürümlü bir C yapılandırma yapısının yanlış gittiği iki yol
Her sürümlü yapılandırma yapısı — FPDF_LIBRARY_CONFIG olsun, bir Win32 cbSize record'u olsun ya da bir plugin ABI — iki simetrik yoldan başarısız olur ve bir wrapper ikisine karşı da korunmalıdır. Birincisi sürümü fazla düşük bırakıp bir alanı doldurmak; ikincisi bir alanı, kütüphanenin kasıtlı seçim diye okuduğu bir sıfır değerde bırakıp sürümü yükseltmektir
- Alan setli, sürüm fazla düşük. Sürüm 2 bir yapıya
m_BrotliEnabled= 1 yazın ve PDFium ona hiç bakmaz. Çağrı başarılı olur ve Brotli stream'leri çözülemez kalır. Savunma, sürümü sabit kodlamak yerine fiilen kullanılan alanlardan türetmektir;LoadLibrary'nin yaptığı budur - Sürüm yeterince yüksek, sıfır alan bir şey ifade eder. Sürümü 6'ya yükseltin; sürüm 6'ya kadarki her alan artık canlıdır.
FillChar,m_RendererType'ı gerçek bir renderer olan, "unset" olmayanFPDF_RENDERERTYPE_AGG'ye sıfırlar. Savunma, seçilen sürümün kapsadığı her alanı kasıtlı bir değerle yazmak ve "default"u varsaymak yerine gerçek derlemeye karşı çözmektir
Çağrılanı çökertebilecek değerler için üçüncü bir kural gelir: onları, çağrıdan önce kullanılabilir en güçlü kanıtla binary'nin yapabildiklerine karşı doğrulayın ve o kanıt bir sezgiselse kodda da dokümantasyonda da dürüst olun. Export edilmiş bir simge kanıttır. String tablosundaki bir crate adı iyi bir tahmindir
Hızlı başvuru: PDFium Component kütüphane yapılandırması
ConfigurePdfLibrary'yi bir kez, herhangi bir şey DLL'yi yüklemeden önce çağırın; her yetenek sorgusu ya da belge yüklemesi onu mühürlerBrotliEnabledya daIsolatePerDocumentset edip paketlenmiş runtime'lardan Skia çıktısı bekliyorsanız v3.123.0 ve sonrasına yükseltin- Belirli bir rasterizer'a gerek duymuyorsanız
Renderer'iprpDefault'da bırakın; artık her yapı sürümünde derleme varsayılanına çözülür - Hangi renderer'ın fiilen aktif olduğunu loglamak için
PdfNativeRendererType'iGetSkiaRenderCapabilities.PageRenderile kullanın - v3.125.0 ve sonrasında yalnızca-AGG bir DLL'de
prpSkiaya da Fontations'sız bir DLL'depfbpFontationsiçin çökme değilEPdfErrorbekleyin - Bir yetenek reddinden sonra
PdfLibraryConfigurationSealedFalse'tur ve yeniden yapılandırabilirsiniz; başarısız bir DLL yüklemesinden sonra True kalır - Fontations saptamasını sezgisel sayın ve bir FreeType fallback tutun
- Win32 ile
TComponentad çakışmalarından kaçınmak içinPDFium.LoadLibraryilePDFium.Loaded'i unit adıyla yazın
DLL, yapılandırma hiç önem kazanmadan başarısız oluyorsa Delphi'de PDFium DLL yükleme başarısızlıklarını teşhis ile başlayın; bileşenin her platformda doğru binary'yi nasıl bulduğu için PDFium yerel kütüphanesini herhangi bir hedefte yükleme yazısına bakın. Renderer oturduktan sonra render cache ve akıcı zoom taktikleri, sayfa render'ını bir görüntüleyicide hızlı tutmayı işler
PDFium Component, PDFium motorunu Delphi ve C++Builder için bu tür yapılandırma kontrolleriyle sarar; böylece yerel başlatma, bir süreç çıkışı yerine işleyebileceğiniz bir Pascal exception olarak başarısız olur. Ürün ayrıntıları ve indirmeler Delphi için PDFium Component ürün sayfasında