V komponenti PDFium Component za Delphi je vklop BrotliEnabled ali IsolatePerDocument v TPdfLibraryConfiguration preklopil zbirno gradnjo Skia na izrisovalnik AGG brez vsakršne napake, ker obe možnosti dvigneta FPDF_LIBRARY_CONFIG na različico, pri kateri PDFium bere m_RendererType dobesedno. Od v3.123.0 privzeti izrisovalnik ostane lastna privzetost DLL, od v3.125.0 pa zahteva po Skia ali Fontations, ki je DLL ne more izpolniti, sproži ulovljiv EPdfError namesto da bi ubil proces
Noben od hroščev se ni naznanil. Prvi je izdelal strani, ki so izgledale v redu, le izrisane z drugim rasterizatorjem, s rahlo drugačnim glajenjem robov in robovi črk kot gradnja, ki ste jo razposlali in testirali. Drugi se je naznanil — glasno — tako, da je spravil gostiteljski proces dol od znotraj izvorne inicializacije. Oba prihajata z istega mesta: verzionirana struktura C, katere polja štejejo šele, ko to pove številka različice, in katere ničelne vrednosti niso »nenastavljeno«, ampak prave izbire
Kako FPDF_LIBRARY_CONFIG odloči, kateri izrisovalnik PDFium uporabi?
FPDF_InitLibraryWithConfig posvetuje m_RendererType samo, kadar je polje Version strukture 4 ali višje, od te različice naprej pa uporabi vrednost točno tako, kot je zapisana. Pod različico 4 PDFium polje ignorira in izbere privzetost gradnje, kar je Skia v gradnjah, prevedenih s PDF_USE_SKIA, drugje pa AGG
Vsako poznejše polje sledi istemu vzorcu. Struktura je rasla po eni zmožnosti naenkrat in vsaka zmožnost je prispela skupaj z novo številko različice. Komponenta PDFium Component zgradi izvorno strukturo v LoadLibrary iz vaše TPdfLibraryConfiguration in dvigne različico samo toliko, kot zahtevajo nastavljene možnosti
| Različica strukture | Polje, ki ga doda | Nastavi |
|---|---|---|
| 2 | m_pIsolate, m_v8EmbedderSlot | Vedno zapisano; V8Isolate, V8EmbedderSlot |
| 3 | m_pPlatform | V8Platform ni nil |
| 4 | m_RendererType | Renderer drugačen od prpDefault |
| 5 | m_FontLibraryType | FontBackend drugačen od pfbpDefault |
| 6 | m_BrotliEnabled | BrotliEnabled = True |
| 7 | m_IsolatePerDocument | IsolatePerDocument = True |
Past je v zadnjih dveh vrsticah. Različice so kumulativne: struktura različice 6 je tudi struktura različice 4 in različice 5, zato PDFium bere m_RendererType in m_FontLibraryType, čeprav ste zahtevali samo Brotli. Karkoli sedi v teh dveh poljih v tistem trenutku postane izrisovalnik in zaledje pisav, ne glede na to, ali ste jih nameravali izbrati
Zakaj je vklop Brotli preklopil izrisovalnik na AGG?
Pred v3.123.0 je komponenta PDFium Component zapisala FPDF_RENDERERTYPE_AGG v m_RendererType za prpDefault, tako da je vsaka konfiguracija, ki je strukturo potisnila na različico 6 ali 7, vsilila AGG na gradnji Skia. Pogona pdfium.dll in pdfium.v8.dll, ki jih dostavlja komponenta, sta gradnji Skia, zato je to zadelo privzeto razporeditev, ne kako eksotično
Preslikava se je ob pisanju zdelo neškodljiva. Pri različici 2 ali 3 polje ni nikoli brano, zato je prpDefault res pomenil »karkoli dela DLL«. Trenutek, ko sta v sliko vstopila BrotliEnabled (različica 6) ali IsolatePerDocument (različica 7), je ista koda spremenila »brez preferenc« v izrecno zahtevo po AGG. Nič ni spodletelo. PDFium se je inicializiral normalno, izrisal vsako stran in vrnil nobeno kodo napake, ker je iz njegovega zornega kota klicatelj zahteval AGG in dobil AGG
Pikseljski hash naredi zamenjavo vidno tam, kjer je posnetki zaslonov ne pokažejo. Izris prve strani istega vzorčnega dokumenta pod tremi konfiguracijami je dal:
- Privzeta konfiguracija: hash
502D77C3711B4ACF BrotliEnabled= True zRendererpuščenim naprpDefault: hashF75B5EB4728ADE87- Izrecen
prpAgg: hashF75B5EB4728ADE87, identičen pognanemu Brotli
Popravek v v3.123.0 je javna funkcija PdfNativeRendererType, ki razreši TPdfRendererPreference v vrednost, zapisano v m_RendererType. prpAgg in prpSkia se preslikata ena-v-enega. prpDefault se zdaj preslika v Skia, kadar naloženi DLL izvaža FPDF_RenderPageSkia, sicer pa v AGG. Ta izvoz je preveden pod istim pogojem PDF_USE_SKIA kot sama privzetost Skia, kar ga naredi edino lastnost gradnje, ki jo lahko opazujete od zunaj DLL. Po popravku konfiguracija Brotli izda isti hash kot privzeta
Zaledje pisav nikoli ni imelo iste težave. m_FontLibraryType se bere od različice 5 naprej, njegova ničelna vrednost, FPDF_FONTBACKENDTYPE_FREETYPE, pa je tudi privzetost PDFiuma, kadar polja sploh ne bere. Zapis FreeType za pfbpDefault torej točno ponovi izvorno privzetost. Ničelne vrednosti niso vedno narobe, samo nikoli niso samodejno prave
Z v3.123.0 ali novejšim koda ob zagonu, ki bi jo naravno napisali, zdaj dela, kar pravi:
uses
PDFium;
procedure ConfigurePdfiumAtStartup;
var
Config: TPdfLibraryConfiguration;
begin
// Zaženi se pred tem, kar koli naloži izvorno knjižnico
Config := TPdfLibraryConfiguration.Default;
Config.BrotliEnabled := True; // dvigne FPDF_LIBRARY_CONFIG na različico 6
// Renderer ostane prpDefault: razreši se v Skia na gradnjah, ki izvažajo
// FPDF_RenderPageSkia, in v AGG na gradnjah samo-AGG
SetLength(Config.UserFontPaths, 1);
Config.UserFontPaths[0] := 'C:\ProgramData\MyApp\Fonts';
ConfigurePdfLibrary(Config);
end;
Upoštevajte, da BrotliEnabled tokove /BrotliDecode PDF 2.0 naredi dekodirljive samo, kadar je bil sam DLL zgrajen s PDF_ENABLE_BROTLI. Zastavica je zahteva in na gradnji brez podpore Brotli nima učinka. TPdfLibraryConfiguration.Hardened je isto kot Default, le da je AllowMachineTime False, kar dokumentnemu JavaScriptu blokira branje prave ure; razumna izhodiščna točka za obdelavo nezaupanja vrednih datotek na strani strežnika
Kaj se zgodi, kadar zahtevate zaledje, ki ga DLL ne vsebuje?
PDFium ne vrne napake za izrisovalnik ali zaledje pisav, ki v gradnji manjkata: FPDF_InitLibraryWithConfig spodleti pri izvornem CHECK, ki se na Windows pokaže kot izjema prekinitvene točke in brez upravljavca strukturiranih izjem okoli klica konča proces. Glava tudi sama opozarja, da bo nepodprta vrednost »podobno spodletela z neposrednim sesutjem«
Dva konkretna primera sta gradnja samo-AGG, ki dobi FPDF_RENDERERTYPE_SKIA, in gradnja brez Fontations, ki dobi FPDF_FONTBACKENDTYPE_FONTATIONS. Zbirni pogon Skia je v drugi skupini: izrisuje s Skia, za pisave pa uporablja FreeType. Zahteva prpSkia skupaj s pfbpFontations proti njemu je na strani Delphi izdelala External exception 80000003. Ko jo vnosnik ali upravljavec izjem po naključju ujame, je stanje še vedno neozdravljivo:
- PDFium ostane pol inicializiran
- Konfiguracija po vsem procesu je že zapečatena, zato
ConfigurePdfLibraryzavrne popravljeno konfiguracijo - Ponovni poskus z drugačno konfiguracijo v istem procesu ni več mogoč
To je nasproten spodlet od hrošča Brotli. Tam je polje nosilo vrednost, ki je ni izbral nihče, PDFium pa jo je tiho sprejel. Tu polje nosi vrednost, ki jo je klicatelj izbral namenoma, PDFium pa o njej ne sprejme nikakršne razprave. Oboje so težave, ki jih mora ovijalec rešiti pred izvornim klicem, ker za njim ni več ničesar, kar bi lahko ujel
Kako komponenta PDFium Component vnaprej preveri Skia in Fontations
Od v3.125.0 LoadLibrary preveri konfiguracijo po vezavi izvozov DLL in pred klicem FPDF_InitLibraryWithConfig ter spremeni nepodprtega izrisovalnika ali zaledja pisav v EPdfError s sporočilom, ki poimenuje krivo nastavitev in alternative. DLL se razloži in konfiguracija se odpri, tako da klicatelj lahko izbere druge nastavitve in naloži znova
Odločitev sama živi v čisti funkciji PdfLibraryConfigurationSupportError, ki sprejme konfiguracijo plus dve logični vrednosti, ki opisujeta gradnjo, in vrne prazen niz, kadar je kombinacija varna. Ker se ne dotika nobenega izvornega stanja, jo lahko pokličete iz svojih preskusov s katero koli kombinacijo zmožnosti. Znotraj LoadLibrary prihajata ti logični vrednosti iz različnih vrst dokazov in zaslužita različne stopnje zaupanja:
- Skia se zazna iz navzočnosti izvoza
FPDF_RenderPageSkia, istega signala, ki ga uporabljaPdfNativeRendererType. Izvoz in izrisovalnik Skia sta prevedena pod enim pogojem, zato je preizkus točen - Fontations nima lastnega izvoza. Edini sled, ki ga pusti, so Rust okvari pisav, ki jih vleče v binarno datoteko, zato komponenta PDFium Component preišče naloženo datoteko knjižnice po imenih okvari
skrifainread-fonts(tudiread_fonts). Pregled teče samo, kadar je zahtevanpfbpFontations, datoteka, ki je ni mogoče brati, pa šteje kot »brez Fontations«
Preizkus Fontations je hevristika in lahko zgreši v eno smer: gradnja Fontations, olupljena vseh teh nizov, bi bila zavrnjena, čeprav bi lahko delovala. Ta izmenjava je bila sklenjena namenoma. Napačna zavrnitev vas stane izjeme, ki jo lahko ujamete, in rezerve na FreeType. Napačno sprejetje vas stane procesa
Odpiranje zapečatke šteje enako kot preizkus. LoadLibrary zapečati konfiguracijo na samem začetku nalaganja, zato bi zavrnitev zmožnosti brez ponastavitve pustila ConfigurePdfLibrary, da vsak ponovni poskus odgovori z EPdfError »PDFium library configuration is already sealed«. Pot zavrnitve najprej pokliče UnloadLibrary; njen klic FPDF_DestroyLibrary je na tej točki varen, ker PDFium še ni bil inicializiran in se vrne takoj. Druge spodletitve nalaganja, kot je manjkajoča DLL ali neskladje arhitektur, zapečatko obdržijo, zato mora zanka ponavljanja vedeti, kako ju ločiti:
uses
SysUtils, PDFium;
function StartPdfiumPreferringSkia: TPdfRendererPreference;
var
Config: TPdfLibraryConfiguration;
begin
Config := TPdfLibraryConfiguration.Default;
Config.Renderer := prpSkia;
ConfigurePdfLibrary(Config);
try
PDFium.LoadLibrary; // kvalificirano z enoto: Windows.LoadLibrary ima isto ime
Result := prpSkia;
except
on E: EPdfError do
begin
// Zavrnitev zmožnosti razloži DLL in odpri konfiguracijo.
// DLL, ki sploh ni naložil, ostane zapečaten: ponavljanje ne pomaga
if PdfLibraryConfigurationSealed then
raise;
Config.Renderer := prpAgg;
ConfigurePdfLibrary(Config);
PDFium.LoadLibrary;
Result := prpAgg;
end;
end;
end;
Upoštevajte izrecni PDFium.LoadLibrary. V enoti, ki uporablja tudi Windows ali Winapi.Windows, se nekvalificiran LoadLibrary razreši v enoto, ki se pojavi zadnja v klavzuli uses; kadar je to funkcija Win32, klic brez parametrov ne prevede s napako o številu argumentov, ki o PDFium ne pove nič
Preverjanje, ki se zgodi še prej
ConfigurePdfLibrary zavrne nekatere kombinacije, preden je vpletena katera koli DLL, vse z EPdfError. Izrecen FontBackend, vključno s pfbpFreeType, zahteva Renderer = prpSkia, ker PDFium zaledje pisav posvetuje samo za izrisovalnik Skia. IsolatePerDocument zahteva, da je V8Isolate nil, ker PDFium ustvari svoj izolat na dokument in spodleti pri izvornem CHECK, če mu ročno podate še enega. Prazni nizi v UserFontPaths so zavrnjeni. Vsak klic po prvem poskusu nalaganja pa spodleti z »PDFium library configuration is already sealed«
Zadnje pravilo ima praktično posledico: DLL ne morete najprej povohati in ga šele nato nastaviti. GetSkiaRenderCapabilities, V8FeaturesAvailable, odpiranje dokumenta in večina drugih vstopnih točk interni kličejo LoadLibrary, ki konfiguracijo zapečati na mestu. Poznejši klic UnloadLibrary je tudi ne ponovno odpre. Najprej nastavite, nato naložite, šele nato vprašajte — točno ta vrstni red naj sledi diagnostična rutina:
uses
SysUtils, PDFium, FPdfView;
function DescribePdfiumState: string;
var
Config: TPdfLibraryConfiguration;
Renderer: string;
begin
Config := GetPdfLibraryConfiguration; // kopija, varna za pregled
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;
// Ista razrešitev, ki jo je LoadLibrary uporabil, ko je zgradil 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;
Ta vrstica v dnevnik enkrat ob zagonu je poceni in to je prva stvar, ki jo želite v podpornem listku, ki pravi »besedilo na strežniku izgleda drugače«. PDFium.Loaded je kvalificiran iz istega razloga kot LoadLibrary: znotraj metode obrazca ali komponente se gol Loaded veže na TComponent.Loaded
Dva načina, kako verzionirana struktura konfiguracije C zgreši
Vsaka verzionirana struktura konfiguracije, naj bo to FPDF_LIBRARY_CONFIG, zapis Win32 cbSize ali ABI vtičnika, spodleti na dva simetrična načina in jih mora ovijalec pokriti oba. Prvi je polnjenje polja, medtem ko različica ostane prenizka; drugi je dvig različice, medtem ko polje ostane na ničelni vrednosti, ki jo knjižnica bere kot namerno izbiro
- Polje nastavljeno, različica prenizka. Zapišite
m_BrotliEnabled= 1 v strukturo različice 2 in PDFium se nanjo nikoli ne pogleda. Klic uspe in tokovi Brotli ostanejo nedekodirljivi. Obramba je izpeljati različico iz polj, ki so dejansko v rabi, kar delaLoadLibrary, namesto da bi jo trdo kodiral - Različica dovolj visoka, ničelno polje nekaj pomeni. Dvignite različico na 6 in vsako polje do različice 6 je zdaj živo.
FillCharničeliram_RendererTypenaFPDF_RENDERERTYPE_AGG, kar je pravi izrisovalnik, ne »nenastavljeno«. Obramba je zapisati vsako polje, ki ga izbrana različica pokriva, z namerno vrednostjo in razrešiti »privzeto« proti dejanski gradnji, namesto da bi ga domnevali
Za vrednosti, ki lahko sesujejo klicanega, sledi tretje pravilo: preverite jih proti temu, kar binarna datoteka zmore, pred klicem, z najmočnejšim dokazom, ki je na voljo, in bodite iskreni v kodi in dokumentaciji, kadar je ta dokaz hevristika. Izvožen simbol je dokaz. Ime okvari v tabeli nizov je dober ugib
Hiter pregled: konfiguracija knjižnice PDFium Component
- Pokličite
ConfigurePdfLibraryenkrat, preden kar koli naloži DLL; vsaka poizvedba po zmožnostih ali nalaganje dokumenta jo zapečati - Nadgradite na v3.123.0 ali novejši, če nastavljate
BrotliEnabledaliIsolatePerDocumentin od zbirnih pogonov pričakujete izris Skia - Pustite
RenderernaprpDefault, razen če potrebujete konkreten rasterizator; zdaj se razreši v privzetost gradnje pri vsaki različici strukture - Uporabite
PdfNativeRendererTypesGetSkiaRenderCapabilities.PageRender, da zapišete v dnevnik, kateri izrisovalnik je dejansko aktiven - Pričakujte
EPdfError, ne sesutja, zaprpSkiana DLL samo-AGG alipfbpFontationsna DLL brez Fontations v v3.125.0 ali novejšem - Po zavrnitvi zmožnosti je
PdfLibraryConfigurationSealedFalse in smešte se znova nastaviti; po spodleteli naložitvi DLL ostane True - Zaznavo Fontations obravnavajte kot hevristiko in obdržite rezervo na FreeType
- Zapišite
PDFium.LoadLibraryinPDFium.Loadedz imenom enote, da se izognete trkom imen z Win32 inTComponent
Če DLL spodleti, še preden konfiguracija sploh pride poštev, začnite pri diagnosticiranju spodletelih naložitev DLL PDFium v Delphiju, kako komponenta najde pravo binarno datoteko na vsaki platformi pa je opisano v nalaganju izvorne knjižnice PDFium na kateri koli cilj. Ko je izrisovalnik urejen, predpomnilnik izrisa in taktike tekočega povečevanja pokrije, kako ostane izrisovanje strani v pregledovalniku hitro
Komponenta PDFium Component ovije pogon PDFium za Delphi in C++Builder s takšnimi preverjanji konfiguracije, tako da izvorna inicializacija spodleti kot Pascalova izjema, ki jo lahko obravnavate, namesto kot izhod procesa. Podrobnosti o izdelku in prenosi so na strani izdelka PDFium Component za Delphi