PDFium Component lar en Delphi-applikasjon avgjøre hvilke fontbyte som brukes når en PDF refererer til en font den ikke bygger inn. ConfigureSystemFontProvider installerer en IPdfSystemFontProvider-implementasjon som mottar hver eneste fontkartleggingsforespørsel PDFium gjør, komplett med fontnavn, vekt, kursivflagg, tegnsett og pitch-familie, og svarer med TrueType-, TrueType Collection- eller OpenType-bytene som skal brukes
Dette finnes fordi ikke-innebygde fonter er et gjengivelseslotteri. En PDF som navngir Arial og bygger inn ingenting, gjengis med Arial på en arbeidsstasjon, med en metrikk-kompatibel erstatning på en Linux-server, og med hva enn vertskartleggeren finner på et låst container-image. Den samme fakturaen ser forskjellig ut på hver, linjeskift flytter seg, og en kunde mottar et dokument som ikke stemmer med den arkiverte kopien
Hvorfor ikke bare installere fontene på serveren?
Noen ganger er det svaret, og når det er det, velg det. Men det svikter i tre vanlige situasjoner. Lisensiering kan forby å installere en font på en server for automatisert gjengivelse. Container-images bygges ofte om på nytt, og en font installert for hånd forsvinner ved neste utrulling. Og regulerte arbeidsflyter trenger at gjengivelsesstakken kan reproduseres fra artefakter under versjonskontroll, noe en maskinomfattende fontinstallasjon ikke er
En leverandør adresserer alle tre ved å flytte avgjørelsen inn i applikasjonen din. Fonter leveres som ressurser du kontrollerer, kartleggingspolicyen er kode du kan gjennomgå, og den samme binærfilen gjengir identisk overalt fordi ingenting avhenger av hva som tilfeldigvis er installert
Installere en leverandør
Konfigurasjon må skje før biblioteket lastes inn. PDFium tar imot en systemfont-info-struktur ved initialisering og beholder håndtak den deler ut etterpå, så å bytte leverandør mens dokumenter er åpne, ville ugyldiggjort fonthåndtak PDFium fremdeles holder på; komponenten avviser det rett ut i stedet for å la det korrumpere en gjengivelse:
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
// Deterministisk kartlegging: fontnavn pluss vekt og kursiv avgjør
// hvilken fil vi leverer for denne forespørselen
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); // komplette sfnt- eller TTC-byte
Font.Charset := Request.Charset;
Font.TTCIndex := 0; // indeks inni en samling
end;
var
Policy: TPdfSystemFontPolicy;
begin
Policy := TPdfSystemFontPolicy.Default;
Policy.AllowDefaultFallback := False; // verten avgjør alt
Policy.AllowFaceSubstitution := False; // avvis et annet fontnavn
Policy.MaxFontBytes := 32 * 1024 * 1024;
Policy.MaxCacheEntries := 64;
ConfigureSystemFontProvider(TAppFontProvider.Create, Policy);
// Last biblioteket og åpne dokumenter først nå
end;
Nedrivning kjøres i motsatt rekkefølge: leverandøren kobles fra PDFium først, deretter lastes biblioteket ut. Å hoppe over frakoblingen etterlater native fonthåndtak som peker på Pascal-objekter som er i ferd med å bli frigjort, noe som er den klassiske nedstengnings-access-violation-en i kode som blander referansetalte grensesnitt med et C-bibliotek
Hva policyflaggene egentlig avgjør
AllowDefaultFallback er bryteren mellom to driftsmoduser. Med den av, feiler en forespørsel leverandøren avslår, ganske enkelt, noe som er hva du vil ha mens du beviser at hver font i et korpus er gjort rede for: ethvert hull blir synlig umiddelbart i stedet for å bli tildekket. Med den på, delegeres uløste forespørsler til kartleggeren returnert av FPDF_GetDefaultSystemFontInfo, mens omverdenen fremdeles ser én ensartet håndtak-omslag, med fontnavn, tegnsett, tabelldata og fontsletting korrekt rutet etter opprinnelse
AllowFaceSubstitution styrer om en leverandør kan svare med et annet fontnavn enn det som ble forespurt. Å slå den av gjør substitusjon til en eksplisitt beslutning snarere enn et uhell, noe som betyr noe når et dokument navngir en font hvis metrikk skiller seg nok til å endre paginering
Komponenten validerer hvert leverandørsvar før det når PDFium: tomme data avvises, overdimensjonerte fonter avvises mot MaxFontBytes, TTC-indeksen sjekkes, og individuelle sfnt-tabeller serveres fra fontkatalogen når PDFium spør om en tabell snarere enn om hele filen. Den siste egenskapen betyr at en leverandør kan overlevere en komplett fontfil og la komponenten svare på spørringer på tabellnivå, i stedet for å eksponere rå Pascal-objekter over C-ABI-en
Bufring uten dinglende fontdata
Fontkartleggingsforespørsler gjentar seg konstant under gjengivelse, så svar bufres med en nøkkel som dekker hver fontvalgsparameter, kastet ut etter avgrenset minst-nylig-brukt-rekkefølge. Det subtile er levetid: PDFium leser kanskje fremdeles bytene til en font hvis buffer-oppføring nettopp er kastet ut
Bufferen lagrer referansetalte dynamiske arrayer, og hvert native håndtak har sitt eget øyeblikksbilde, så utkasting fjerner en referanse i stedet for å frigjøre minne i bruk. Slette-tilbakekallet frigjør håndtaket og vedlikeholder en aktiv teller. I praksis betyr dette at MaxCacheEntries kan justeres for minne uten noen risiko for å trekke data ut under en pågående gjengivelse
Kalles leverandøren på min tråd?
Nei, ikke nødvendigvis. PDFium kan kalle kartleggeren fra sine egne arbeidstråder, så en implementasjon må være trådsikker. Delte tellere, bufferen og konfigurasjonsobservasjon er hver beskyttet inni komponenten av sin egen kritiske seksjon, men koden inni ResolveFont er din å gjøre sikker
Den tryggeste formen er en leverandør som ikke rører noen delt mutabel tilstand: les fra en tabell bygget ved oppstart, last byte fra en fil eller en ressurs, returner. Hvis et oppslag trenger en delt cache av din egen, sikre den. Og hold unntak inni implementasjonen din, siden et Pascal-unntak aldri må vikle seg gjennom PDFium-stakken; komponenten fanger opp ved C-ABI-grensen og konverterer til en feil eller en valgfri standard-fallback, men å stole på det som normal kontrollflyt koster ytelse og skjuler feil. Trådingsreglene for resten av komponenten følger de samme prinsippene som de i render-lock-disiplin
Bevise kartleggingen i produksjon
Statistikk gjør fontsubstitusjon om fra gjetning til noe du kan bekrefte. GetSystemFontProviderStatistics rapporterer om en leverandør er konfigurert og installert, hvor mange kartleggingsforespørsler som ble gjort, og hvordan de ble tilfredsstilt, delt inn i buffertreff, leverandørtreff og standard-fallback-treff, sammen med avviste svar, mislykkede forespørsler, aktive håndtak og bufrede fonter:
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]));
// I en samsvarskjøring med fallback deaktivert betyr ethvert fallback-treff
// eller mislykket forespørsel at et dokument refererte til en font vi ikke leverer
if (Stats.DefaultFallbackHits > 0) or (Stats.FailedRequests > 0) then
raise Exception.Create('unmapped font encountered - update the font set');
end;
Et stigende RejectedProviderResponses-antall er signalet om at en leverandør svarer med data policyen avviser, som regel en overdimensjonert fil eller et substituert fontnavn, og det er verdt å varsle på, fordi de forespørslene stille degraderer til fallback eller feil. For å diagnostisere hvilke fonter et dokument faktisk trenger før du bygger kartleggingstabellen, lister inspeksjonsruten i analysere PDF-fontegenskaper opp innebygde og ikke-innebygde fonter per dokument
Fontforsyning, gjengivelse og tekstuthenting deler den samme biblioteksinstansen på tvers av Delphi, C++Builder og Lazarus; distribusjonsdetaljer er beskrevet på PDFium Component for Delphi-siden