I PDFium Component for Delphi førte det å slå på BrotliEnabled eller IsolatePerDocument i TPdfLibraryConfiguration til at det medfølgende Skia-bygget ble byttet til AGG-rendereren uten noen feil, fordi begge alternativene løfter FPDF_LIBRARY_CONFIG til en versjon der PDFium leser m_RendererType bokstavelig. Siden v3.123.0 forblir standard-rendereren DLL-ens eget standardvalg, og siden v3.125.0 reiser en Skia- eller Fontations-forespørsel DLL-en ikke kan innfri, en fangbar EPdfError i stedet for å drepe prosessen
Ingen av buggene annonserte seg selv. Den første produserte sider som så fine ut, bare renderet av en annen rasterizer, med litt annerledes anti-aliasing og tekstkanter enn bygget du sendte ut og testet. Den andre annonserte seg selv, høylytt, ved å ta vertsprosessen ned fra inni native initialisering. Begge kommer fra samme sted: en versjonert C-struktur hvis felt bare teller når versjonstallet sier det, og hvis nullverdier ikke er «uindsatt», men reelle valg
Hvordan avgjør FPDF_LIBRARY_CONFIG hvilken renderer PDFium bruker?
FPDF_InitLibraryWithConfig konsulterer m_RendererType bare når strukturens Version-felt er 4 eller høyere, og fra den versjonen bruker den verdien nøyaktig slik den er skrevet. Under versjon 4 ignorerer PDFium feltet og velger byggets standard, som er Skia i bygg kompilert med PDF_USE_SKIA og AGG overalt ellers
Hvert senere felt følger samme mønster. Strukturen vokste én kapabilitet om gangen, og hver kapabilitet ankom sammen med et nytt versjonstall. PDFium Component bygger den native strukturen i LoadLibrary fra din TPdfLibraryConfiguration og løfter versjonen bare så langt som alternativene du har satt, krever
| Strukturversjon | Feltet den legger til | Satt av |
|---|---|---|
| 2 | m_pIsolate, m_v8EmbedderSlot | Alltid skrevet; V8Isolate, V8EmbedderSlot |
| 3 | m_pPlatform | V8Platform ikke nil |
| 4 | m_RendererType | Renderer annet enn prpDefault |
| 5 | m_FontLibraryType | FontBackend annet enn pfbpDefault |
| 6 | m_BrotliEnabled | BrotliEnabled = True |
| 7 | m_IsolatePerDocument | IsolatePerDocument = True |
Fellen ligger i de to siste radene. Versjoner er kumulative: en versjon 6-struktur er også en versjon 4- og en versjon 5-struktur, så PDFium leser m_RendererType og m_FontLibraryType selv om du bare spurte om Brotli. Hva enn som ligger i de to feltene i det øyeblikket, blir rendereren og font-backend-en, enten du mente å velge dem eller ikke
Hvorfor slo på av Brotli rendereren over til AGG?
Før v3.123.0 skrev PDFium Component FPDF_RENDERERTYPE_AGG inn i m_RendererType for prpDefault, så enhver konfigurasjon som skjøv strukturen til versjon 6 eller 7, tvang AGG på et Skia-bygg. pdfium.dll- og pdfium.v8.dll-kjøretidene som følger med komponenten, er Skia-bygg, så dette rammet standarddistribusjonen, ikke noen eksotisk en
Mappingen så harmløs ut da den ble skrevet. På versjon 2 eller 3 leses feltet aldri, så prpDefault betydde virkelig «hva DLL-en nå gjør». I det øyeblikket BrotliEnabled (versjon 6) eller IsolatePerDocument (versjon 7) kom inn i bildet, gjorde samme kode «ingen preferanse» om til en eksplisitt AGG-forespørsel. Ingenting feilet. PDFium initialiserte normalt, renderet hver side, og returnerte ingen feilkode, fordi fra dens synspunkt hadde kalleren bedt om AGG og fått AGG
En piksel-hash gjør byttet synlig der skjermbilder ikke gjør det. Å rendere første side av samme prøvedokument under tre konfigurasjoner ga:
- Standardkonfigurasjon: hash
502D77C3711B4ACF BrotliEnabled= True medRendererigjen påprpDefault: hashF75B5EB4728ADE87- Eksplisitt
prpAgg: hashF75B5EB4728ADE87, identisk med Brotli-kjøringen
Fiksen i v3.123.0 er den offentlige funksjonen PdfNativeRendererType, som løser en TPdfRendererPreference til verdien skrevet inn i m_RendererType. prpAgg og prpSkia mappes én til én. prpDefault mappes nå til Skia når den lastede DLL-en eksporterer FPDF_RenderPageSkia og til AGG ellers. Den eksporten er kompilert under samme PDF_USE_SKIA-betingelse som Skia-standarden selv, noe som gjør den til den eneste byggeegenskapen du kan observere utenfra DLL-en. Etter fiksen produserer Brotli-konfigurasjonen samme hash som standarden
Font-backend-en hadde aldri samme problem. m_FontLibraryType leses fra versjon 5 og oppover, og nullverdien dens, FPDF_FONTBACKENDTYPE_FREETYPE, er også PDFiums standard når feltet ikke leses i det hele tatt. Å skrive FreeType for pfbpDefault gjenskaper derfor den native standarden nøyaktig. Nullverdier er ikke alltid feil, de er bare aldri automatisk riktige
Med v3.123.0 eller senere gjør oppsettskoden du naturlig ville skrevet, nå det den sier:
uses
PDFium;
procedure ConfigurePdfiumAtStartup;
var
Config: TPdfLibraryConfiguration;
begin
// Må kjøre før noe laster det native biblioteket
Config := TPdfLibraryConfiguration.Default;
Config.BrotliEnabled := True; // løfter FPDF_LIBRARY_CONFIG til versjon 6
// Renderer forblir prpDefault: løses til Skia på bygg som eksporterer
// FPDF_RenderPageSkia og til AGG på AGG-bare bygg
SetLength(Config.UserFontPaths, 1);
Config.UserFontPaths[0] := 'C:\ProgramData\MyApp\Fonts';
ConfigurePdfLibrary(Config);
end;
Husk at BrotliEnabled bare gjør PDF 2.0 /BrotliDecode-strømmer dekodbare når DLL-en selv er bygget med PDF_ENABLE_BROTLI. Flaggget er en forespørsel, og på et bygg uten Brotli-støtte har det ingen effekt. TPdfLibraryConfiguration.Hardened er det samme som Default bortsett fra at AllowMachineTime er False, noe som blokkerer dokument-JavaScript fra å lese den virkelige klokken; det er et fornuftig utgangspunkt for server-side behandling av utruelige filer
Hva skjer når du ber om en backend DLL-en ikke inneholder?
PDFium returnerer ingen feil for en renderer- eller font-backend som mangler i bygget: FPDF_InitLibraryWithConfig feiler en native CHECK, som på Windows kommer til syne som et breakpoint-unntak og, uten en strukturert unntakshåndterer rundt kallet, terminerer prosessen. Hodet sier det selv, med en advarsel om at en ikke-støttet verdi «vil på samme måte feile med en umiddelbar krasj»
De to konkrete tilfellene er et AGG-bare bygg som mottar FPDF_RENDERERTYPE_SKIA, og et bygg uten Fontations som mottar FPDF_FONTBACKENDTYPE_FONTATIONS. Den medfølgende Skia-kjøretiden er i den andre gruppen: den renderer med Skia men bruker FreeType for fonter. Å be om prpSkia sammen med pfbpFontations mot den ga External exception 80000003 på Delphi-siden. Når feilsøkeren eller en unntakshåndterer tilfeldigvis fanger det, er situasjonen fortsatt umulig å redde:
- PDFium er etterlatt halvinitialisert
- Konfigurasjonen på prosessnivå er allerede forseglet, så
ConfigurePdfLibrarynekter en korrigert konfigurasjon - Å prøve igjen med en annen konfigurasjon i samme prosess er ikke lenger mulig
Dette er det motsatte feilbildet av Brotli-buggen. Der holdt feltet en verdi ingen hadde valgt, og PDFium godtok den stille. Her holder feltet en verdi kalleren valgte bevisst, og PDFium diskuterer ingenting om den i det hele tatt. Begge er problemer en wrapper må løse før det native kallet, for etter det finnes det ingenting igjen å fange
Hvordan PDFium Component forhåndssjekker Skia og Fontations
Siden v3.125.0 validerer LoadLibrary konfigurasjonen etter binding av DLL-eksportene og før kallet til FPDF_InitLibraryWithConfig, og gjør en ikke-støttet renderer eller font-backend til en EPdfError med en melding som navngir den klandreverdige innstillingen og alternativene. DLL-en losses, og konfigurasjonen forsegles opp, så kalleren kan velge andre innstillinger og laste igjen
Beslutningen selv bor i den rene funksjonen PdfLibraryConfigurationSupportError, som tar konfigurasjonen pluss to booleans som beskriver bygget og returnerer en tom streng når kombinasjonen er trygg. Etter som den rører ingen native tilstand, kan du kalle den fra dine egne tester med enhver kapabilitetskombinasjon. Inne i LoadLibrary kommer de to booleanene fra ulike typer bevis, og de fortjener ulike tillitsnivåer:
- Skia oppdages fra tilstedeværelsen av
FPDF_RenderPageSkia-eksporten, samme signalPdfNativeRendererTypebruker. Eksporten og Skia-rendereren er kompilert under én betingelse, så sjekken er eksakt - Fontations har ingen egen eksport. Eneste spor det etterlater, er Rust-font-cratene det drar inn i binærfilen, så PDFium Component skanner den lastede bibliotekfilen etter cratenavnene
skrifaogread-fonts(ogsåread_fonts). Skanningen kjører bare nårpfbpFontationser forespurt, og en fil som ikke kan leses, teller som «ingen Fontations»
Fontations-sjekken er en heuristikk, og den kan ta feil i én retning: et Fontations-bygg strippet for hver av de strengene, ville blitt avvist selv om det ville fungert. Den avveiningen ble gjort med vilje. En falsk avvisning koster deg et unntak du kan fange og en fallback til FreeType. En falsk aksept koster deg prosessen
Å forsegles opp igjen betyr like mye som sjekken. LoadLibrary forsegler konfigurasjonen helt i starten av lastingen, så uten tilbakestillingen ville en kapabilitetsavvisning etterlate ConfigurePdfLibrary som svarer ethvert nytt forsøk med EPdfError «PDFium library configuration is already sealed». Avvisningsstien kaller UnloadLibrary først; dens FPDF_DestroyLibrary-kall er trygt på det punktet fordi PDFium ikke er initialisert ennå og returnerer umiddelbart. Andre lastefeil, som en manglende DLL eller en arkitekturmismatch, beholder forseglingen, så en retry-løkke må skille de to fra hverandre:
uses
SysUtils, PDFium;
function StartPdfiumPreferringSkia: TPdfRendererPreference;
var
Config: TPdfLibraryConfiguration;
begin
Config := TPdfLibraryConfiguration.Default;
Config.Renderer := prpSkia;
ConfigurePdfLibrary(Config);
try
PDFium.LoadLibrary; // enhetskvalifisert: Windows.LoadLibrary heter det samme
Result := prpSkia;
except
on E: EPdfError do
begin
// En kapabilitetsavvisning losser DLL-en og forsegler opp konfigurasjonen.
// En DLL som ikke lastet i det hele tatt, forblir forseglet: å prøve igjen hjelper ikke
if PdfLibraryConfigurationSealed then
raise;
Config.Renderer := prpAgg;
ConfigurePdfLibrary(Config);
PDFium.LoadLibrary;
Result := prpAgg;
end;
end;
end;
Merk den eksplisitte PDFium.LoadLibrary. I en enhet som også bruker Windows eller Winapi.Windows, løses et ukvalifisert LoadLibrary til den enheten som kommer sist i uses-klausulen; er det Win32-funksjonen, feiler det parameterløse kallet å kompilere med en argumentantallsfeil som ikke sier noe om PDFium
Validering som skjer enda tidligere
ConfigurePdfLibrary avviser noen kombinasjoner før noen DLL er involvert, alle med EPdfError. En eksplisitt FontBackend, inkludert pfbpFreeType, krever Renderer = prpSkia, fordi PDFium bare konsulterer font-backend-en for Skia-rendereren. IsolatePerDocument krever at V8Isolate er nil, siden PDFium lager sin egen isolate per dokument og feiler en native CHECK hvis du også hånder den én. Tomme strenger i UserFontPaths avvises. Og ethvert kall etter det første lasteforsøket feiler med «PDFium library configuration is already sealed»
Den siste regelen har en praktisk konsekvens: du kan ikke sonde DLL-en først og konfigurere den etterpå. GetSkiaRenderCapabilities, V8FeaturesAvailable, å åpne et dokument, og de fleste andre inngangspunkt kaller LoadLibrary internt, noe som forsegler konfigurasjonen på stedet. Å kalle UnloadLibrary senere åpner den heller ikke igjen. Konfigurer først, last så, og still så spørsmål, noe som er nøyaktig rekkefølgen en diagnostisk rutine bør følge:
uses
SysUtils, PDFium, FPdfView;
function DescribePdfiumState: string;
var
Config: TPdfLibraryConfiguration;
Renderer: string;
begin
Config := GetPdfLibraryConfiguration; // en kopi, trygg å inspisere
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;
// Samme løsning LoadLibrary brukte da den bygde 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;
Å logge den linjen én gang ved oppstart er billig, og det er det første du vil ha i en støttesak som sier «teksten ser annerledes ut på serveren». PDFium.Loaded er kvalifisert av samme grunn som LoadLibrary: inne i en metode på et skjema eller en komponent, binder en bar Loaded til TComponent.Loaded
To måter en versjonert C-konfigurasjonsstruktur går galt på
Enhver versjonert konfigurasjonsstruktur, enten det er FPDF_LIBRARY_CONFIG, en Win32 cbSize-post, eller en plugin-ABI, feiler på to symmetriske måter, og en wrapper må vokte seg mot begge. Den første er å fylle et felt mens versjonen er for lav; den andre er å løfte versjonen mens et felt står på en nullverdi biblioteket leser som et bevisst valg
- Felt satt, versjon for lav. Skriv
m_BrotliEnabled= 1 inn i en versjon 2-struktur, og PDFium ser aldri på den. Kallet lykkes, og Brotli-strømmer forblir udekbare. Forsvaret er å utlede versjonen fra feltene faktisk i bruk, noe som er detLoadLibrarygjør, snarere enn å hardkode én - Versjon høy nok, nullfelt betyr noe. Løft versjonen til 6, og hvert felt opp til versjon 6 er nå levende.
FillCharnullstillerm_RendererTypetilFPDF_RENDERERTYPE_AGG, noe som er en ekte renderer, ikke «uindsatt». Forsvaret er å skrive hvert felt den valgte versjonen dekker, med en bevisst verdi, og å løse «standard» mot det faktiske bygget i stedet for å anta det
En tredje regel følger for verdier som kan krasje den kalte: valider dem mot det binærfilen kan gjøre før kallet, med det sterkeste tilgjengelige beviset, og vær ærlig i koden og dokumentasjonen når det beviset er en heuristikk. Et eksportert symbol er et bevis. Et cratenavn i en strengtabell er en god gjetning
Hurtigreferanse: PDFium Component bibliotekskonfigurasjon
- Kall
ConfigurePdfLibraryén gang, før noe laster DLL-en; enhver kapabilitetsforespørsel eller dokumentlasting forsegler den - Oppgrader til v3.123.0 eller senere hvis du setter
BrotliEnabledellerIsolatePerDocumentog forventer Skia-utdata fra de medfølgende kjøretidene - La
Rendererstå påprpDefaultmed mindre du trenger en spesifikk rasterizer; den løses nå til byggets standard på enhver strukturversjon - Bruk
PdfNativeRendererTypemedGetSkiaRenderCapabilities.PageRenderfor å logge hvilken renderer som faktisk er aktiv - Forvent
EPdfError, ikke en krasj, forprpSkiapå en AGG-bare DLL ellerpfbpFontationspå en ikke-Fontations DLL i v3.125.0 eller senere - Etter en kapabilitetsavvisning er
PdfLibraryConfigurationSealedFalse og du kan konfigurere på nytt; etter en feilet DLL-lasting forblir den True - Behandle Fontations-deteksjonen som heuristisk og ha en FreeType-fallback
- Skriv
PDFium.LoadLibraryogPDFium.Loadedmed enhetsnavnet for å unngå navnekollisjoner med Win32 ogTComponent
Feiler DLL-en før konfigurasjonen i det hele tatt betyr noe, start med diagnose av PDFium DLL-lastefeil i Delphi, og for hvordan komponenten finner riktig binærfil på hver plattform, se lasting av PDFium native bibliotek på ethvert mål. Når rendereren er avgjort, dekker render-cache og smidig zoom-taktikk hvordan du holder siderenderingen rask i en viser
PDFium Component wrapper PDFium-motoren for Delphi og C++Builder med konfigurasjonssjekker som disse, slik at native initialisering feiler som et Pascal-unntak du kan håndtere i stedet for en prosessavslutning. Produktdetaljer og nedlastinger finner du på produktsiden for PDFium Component for Delphi