PDFium Component aplikaciji v Delphiju omogoči, da odloči, kateri bajti pisave se uporabijo, kadar PDF navaja pisavo, ki je ne vdela. ConfigureSystemFontProvider namesti implementacijo IPdfSystemFontProvider, ki prejme vsako zahtevo za preslikavo pisave, ki jo naredi PDFium, popolno z imenom pisave, debelino, zastavico ležečega, naborom znakov in družino koraka, ter odgovori z bajti TrueType, TrueType Collection ali OpenType, ki naj se uporabijo
To obstaja zato, ker so nevdelane pisave upodabljalna loterija. PDF, ki poimenuje Arial in ne vdela ničesar, se na delovni postaji upodobi z Arialom, na strežniku Linux z metrično združljivim nadomestkom, na zaklenjeni sliki vsebnika pa s čimerkoli, kar najde gostiteljski preslikovalnik. Isti račun je na vsakem videti drugače, prelomi vrstic se premaknejo, stranka pa prejme dokument, ki se ne ujema z arhivirano kopijo
Zakaj preprosto ne namestiti pisav na strežnik?
Včasih je to odgovor, in kadar je, ga izberite. A odpove v treh pogostih primerih. Licenciranje lahko prepove namestitev pisave na strežnik za avtomatizirano upodabljanje. Slike vsebnikov se pogosto na novo zgradijo, ročno nameščena pisava pa izgine z naslednjo namestitvijo. Regulirani poteki dela pa zahtevajo, da je upodabljalni sklad ponovljiv iz artefaktov pod nadzorom različic, kar namestitev pisave na ravni celega stroja ni
Ponudnik naslovi vse tri primere tako, da odločitev prenese v vašo aplikacijo. Pisave se dobavijo kot viri, ki jih nadzorujete, pravilnik preslikave je koda, ki jo lahko pregledate, isti dvojiški program pa se povsod upodobi enako, ker nič ni odvisno od tega, kaj je pač nameščeno
Nameščanje ponudnika
Konfiguracija se mora zgoditi, preden se knjižnica naloži. PDFium ob inicializaciji sprejme strukturo informacij o sistemskih pisavah in obdrži ročice, ki jih pozneje razdeli, zato bi zamenjava ponudnika, medtem ko so dokumenti odprti, izničila ročice pisav, ki jih PDFium še vedno drži; komponenta to naravnost zavrne, namesto da bi dovolila, da to pokvari upodabljanje:
uses
PDFium;
type
TAppFontProvider = class(TInterfacedObject, IPdfSystemFontProvider)
public
function ResolveFont(const Request: TPdfSystemFontRequest;
out Font: TPdfSystemFontData): Boolean;
end;
function TAppFontProvider.ResolveFont(const Request: TPdfSystemFontRequest;
out Font: TPdfSystemFontData): Boolean;
var
Path: string;
begin
// Deterministična preslikava: ime pisave plus debelina in ležeče
// odločita, katero datoteko dobavimo za to zahtevo
Path := MapFaceToBundledFile(Request.FaceName, Request.Weight,
Request.Italic, Request.Charset);
Result := Path <> '';
if not Result then
Exit;
Font.FaceName := Request.FaceName;
Font.FontData := LoadFileBytes(Path); // popolni bajti sfnt ali TTC
Font.Charset := Request.Charset;
Font.TTCIndex := 0; // indeks znotraj zbirke
end;
var
Policy: TPdfSystemFontPolicy;
begin
Policy := TPdfSystemFontPolicy.Default;
Policy.AllowDefaultFallback := False; // gostitelj odloči o vsem
Policy.AllowFaceSubstitution := False; // zavrni drugačno ime pisave
Policy.MaxFontBytes := 32 * 1024 * 1024;
Policy.MaxCacheEntries := 64;
ConfigureSystemFontProvider(TAppFontProvider.Create, Policy);
// Šele zdaj naložite knjižnico in odprite dokumente
end;
Razgradnja teče v obratnem vrstnem redu: ponudnik se najprej odklopi od PDFium, nato se knjižnica sname. Če odklop izpustite, ostanejo domorodne ročice pisav kažoč na objekte Pascal, ki jih bo kmalu sprostilo, kar je klasična kršitev dostopa ob zaustavitvi v kodi, ki meša vmesnike s štetjem sklicev s knjižnico C
Kaj zastavice pravilnika dejansko odločajo
AllowDefaultFallback je stikalo med dvema delovnima načinoma. Ko je izklopljeno, zahteva, ki jo ponudnik zavrne, preprosto spodleti, kar je to, kar želite, medtem ko dokazujete, da je vsaka pisava v korpusu zajeta: vsaka vrzel postane takoj vidna, namesto da bi bila prekrita. Ko je vklopljeno, so nerazrešene zahteve prepuščene preslikovalniku, ki ga vrne FPDF_GetDefaultSystemFontInfo, medtem ko zunanji svet še vedno vidi en sam enoten ovoj ročice, z imenom pisave, naborom znakov, podatki tabele in brisanjem pisave, pravilno usmerjenimi po izvoru
AllowFaceSubstitution ureja, ali sme ponudnik odgovoriti z drugačnim imenom pisave, kot je bilo zahtevano. Izklop te možnosti nadomestitev naredi izrecno odločitev in ne naključje, kar je pomembno, kadar dokument poimenuje pisavo, katere metrike se razlikujejo dovolj, da spremenijo paginacijo
Komponenta potrdi vsak odgovor ponudnika, preden doseže PDFium: prazni podatki so zavrnjeni, prevelike pisave so zavrnjene glede na MaxFontBytes, indeks TTC je preverjen, posamezne tabele sfnt pa so postrežene iz imenika pisav, kadar PDFium zahteva tabelo namesto cele datoteke. Ta zadnja zmožnost pomeni, da lahko ponudnik preda popolno datoteko pisave in prepusti komponenti, da odgovarja na poizvedbe na ravni tabele, namesto da bi izpostavil surove objekte Pascal čez ABI jezika C
Predpomnjenje brez visečih podatkov pisave
Zahteve za preslikavo pisav se med upodabljanjem nenehno ponavljajo, zato so odgovori predpomnjeni s ključem, ki zajema vsak parameter izbire pisave, izločeni pa po omejenem vrstnem redu najmanj nedavno uporabljenih. Prefinjenost je v življenjski dobi: PDFium morda še vedno bere bajte pisave, katere vnos v predpomnilniku je bil pravkar izločen
Predpomnilnik shranjuje dinamična polja s štetjem sklicev, vsaka domorodna ročica pa hrani svoj lastni posnetek, tako da izločitev opusti sklic in ne sprosti pomnilnika v uporabi. Povratni klic za brisanje sprosti ročico in vzdržuje aktivno število. Praktično to pomeni, da je MaxCacheEntries mogoče prilagajati zaradi pomnilnika brez kakršnega koli tveganja, da bi podatke izvlekli izpod upodabljanja, ki je še v teku
Ali se ponudnik kliče v mojem nizu?
Ne, ne nujno. PDFium lahko preslikovalnik pokliče iz svojih lastnih delovnih niti, zato mora biti implementacija varna za nite. Skupni števci, predpomnilnik in opazovanje konfiguracije so vsi znotraj komponente zaščiteni s svojim lastnim kritičnim odsekom, vendar je koda znotraj ResolveFont vaša, da jo naredite varno
Najvarnejša oblika je ponudnik, ki se ne dotika nobenega spremenljivega skupnega stanja: bere iz tabele, zgrajene ob zagonu, naloži bajte iz datoteke ali vira, vrne. Če iskanje potrebuje vaš lasten skupni predpomnilnik, ga zaščitite. In izjeme obdržite znotraj svoje implementacije, saj se izjema Pascal nikoli ne sme razviti nazaj skozi sklad PDFium; komponenta jih ujame na meji ABI jezika C in pretvori v neuspeh ali izbirno privzeto rezervno rešitev, vendar zanašanje na to kot na normalen tok nadzora stane zmogljivost in skriva hrošče. Pravila niti za preostanek komponente sledijo istim načelom kot tista v disciplini zaklepanja upodabljanja
Dokazovanje preslikave v produkciji
Statistika nadomeščanje pisav spremeni iz ugibanja v nekaj, na kar se lahko oprete s trditvijo. GetSystemFontProviderStatistics javi, ali je ponudnik konfiguriran in nameščen, koliko zahtev za preslikavo je bilo narejenih in kako so bile izpolnjene, razdeljeno na zadetke predpomnilnika, zadetke ponudnika in zadetke privzete rezervne rešitve, skupaj z zavrnjenimi odgovori, neuspelimi zahtevami, živimi ročicami in predpomnjenimi pisavami:
var
Stats: TPdfSystemFontStatistics;
begin
Stats := GetSystemFontProviderStatistics;
Writeln(Format('requests=%d cache=%d provider=%d fallback=%d',
[Stats.MapRequests, Stats.CacheHits, Stats.ProviderHits,
Stats.DefaultFallbackHits]));
Writeln(Format('rejected=%d failed=%d handles=%d cached=%d',
[Stats.RejectedProviderResponses, Stats.FailedRequests,
Stats.ActiveHandles, Stats.CachedFonts]));
// V teku skladnosti z onemogočeno rezervno rešitvijo vsak zadetek rezervne
// rešitve ali neuspela zahteva pomeni, da je dokument navedel pisavo, ki je ne dobavljamo
if (Stats.DefaultFallbackHits > 0) or (Stats.FailedRequests > 0) then
raise Exception.Create('unmapped font encountered - update the font set');
end;
Naraščajoče število RejectedProviderResponses je signal, da ponudnik odgovarja s podatki, ki jih pravilnik zavrača, običajno prevelika datoteka ali nadomeščena pisava, in vredno je opozarjati nanj, ker se te zahteve tiho degradirajo v rezervno rešitev ali neuspeh. Za diagnosticiranje, katere pisave dokument dejansko potrebuje, preden zgradite preslikovalno tabelo, pot pregleda v analizi lastnosti pisav PDF po dokumentu navede vdelane in nevdelane pisave
Priskrba pisav, upodabljanje in izvlečenje besedila si delijo isto instanco knjižnice v Delphiju, C++Builderju in Lazarusu; podrobnosti razmestitve so opisane na strani PDFium Component za Delphi