A PDFium Component lehetővé teszi, hogy egy Delphi-alkalmazás eldöntse, mely betűtípus-bájtok kerülnek felhasználásra, amikor egy PDF olyan betűtípusra hivatkozik, amelyet nem ágyaz be. A ConfigureSystemFontProvider egy IPdfSystemFontProvider implementációt telepít, amely megkapja a PDFium által kezdeményezett minden betűtípus-leképezési kérést, a betűkép-névvel, súllyal, dőlt jelzővel, karakterkészlettel és lépésközcsaláddal együtt, és a felhasználandó TrueType, TrueType Collection vagy OpenType bájtokkal válaszol
Ez azért létezik, mert a be nem ágyazott betűtípusok renderelési lottó. Egy PDF, amely Arialt nevez meg, és semmit nem ágyaz be, egy munkaállomáson Ariallal renderelődik, egy Linux-szerveren egy metrikakompatibilis helyettesítővel, egy lezárt konténerképen pedig azzal, amit a gazdagép leképezője éppen talál. Ugyanaz a számla mindegyiken másképp néz ki, a sortörések elmozdulnak, egy ügyfél pedig olyan dokumentumot kap, amely nem egyezik az archivált példánnyal
Miért ne csak telepítsük a betűtípusokat a szerverre?
Néha ez a válasz, és amikor az, válaszd ezt. Ám három gyakori helyzetben csődöt mond. A licencelés tilthatja egy betűtípus szerverre telepítését automatizált rendereléshez. A konténerképeket gyakran újraépítik, és egy kézzel telepített betűtípus eltűnik a következő telepítéssel. A szabályozott munkafolyamatoknak pedig arra van szükségük, hogy a renderelési verem verziókövetett artefaktumokból reprodukálható legyen, amit egy gépszintű betűtípus-telepítés nem tud biztosítani
Egy szolgáltató mindhármat kezeli azzal, hogy a döntést átteszi a saját alkalmazásodba. A betűtípusok általad irányított erőforrásokként érkeznek, a leképezési szabályzat átvizsgálható kód, ugyanaz a bináris pedig mindenhol azonosan renderel, mert semmi nem függ attól, mi van éppen telepítve
Egy szolgáltató telepítése
A konfigurálásnak a könyvtár betöltése előtt kell megtörténnie. A PDFium inicializáláskor fogad el egy rendszer-betűtípus-információs struktúrát, és megtartja az utána kiadott leírókat, így egy szolgáltató lecserélése nyitott dokumentumok mellett érvénytelenítené azokat a betűtípus-leírókat, amelyeket a PDFium még tart; a komponens ezt egyenesen elutasítja, ahelyett hogy hagyná egy renderelés sérülését:
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
// Determinisztikus leképezés: a betűkép-név, a súly és a dőlt döntik el,
// melyik fájlt szolgáltatjuk erre a kérésre
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); // teljes sfnt vagy TTC bájtok
Font.Charset := Request.Charset;
Font.TTCIndex := 0; // index egy gyűjteményen belül
end;
var
Policy: TPdfSystemFontPolicy;
begin
Policy := TPdfSystemFontPolicy.Default;
Policy.AllowDefaultFallback := False; // a gazdagép dönt mindenről
Policy.AllowFaceSubstitution := False; // egy eltérő betűkép-név elutasítása
Policy.MaxFontBytes := 32 * 1024 * 1024;
Policy.MaxCacheEntries := 64;
ConfigureSystemFontProvider(TAppFontProvider.Create, Policy);
// Csak most töltsd be a könyvtárat, és nyisd meg a dokumentumokat
end;
A leépítés fordított sorrendben fut: először a szolgáltatót választja le a PDFiumról, majd a könyvtárat tölti ki. A leválasztás kihagyása olyan natív betűtípus-leírókat hagy hátra, amelyek éppen felszabadításra kerülő Pascal-objektumokra mutatnak, ami a klasszikus leállítási hozzáférési hiba az olyan kódban, amely referenciaszámlált interfészeket kever egy C könyvtárral
Mit döntenek el valójában a szabályzat-jelzők?
Az AllowDefaultFallback a kapcsoló két üzemmód között. Kikapcsolva egy olyan kérés, amelyet a szolgáltató elutasít, egyszerűen elbukik, ami akkor kívánatos, ha azt bizonyítod, hogy egy korpusz minden betűtípusa számba van véve: bármilyen hiányosság azonnal láthatóvá válik ahelyett, hogy elsimulna. Bekapcsolva a feloldatlan kérések az FPDF_GetDefaultSystemFontInfo által visszaadott leképezőhöz kerülnek átirányításra, miközben a külvilág továbbra is egyetlen egységes leíró-burkolatot lát, ahol a betűkép-név, a karakterkészlet, a táblázatadatok és a betűtípus-törlés forrás szerint helyesen kerül irányításra
Az AllowFaceSubstitution szabályozza, hogy egy szolgáltató válaszolhat-e a kérttől eltérő betűkép-névvel. Kikapcsolása a helyettesítést explicit döntéssé teszi véletlen helyett, ami akkor számít, amikor egy dokumentum olyan betűtípust nevez meg, amelynek metrikái eléggé eltérnek ahhoz, hogy megváltoztassák a lapozást
A komponens minden szolgáltatói választ ellenőriz, mielőtt az elérné a PDFiumot: az üres adatokat elutasítja, a túlméretezett betűtípusokat elutasítja a MaxFontBytes alapján, ellenőrzi a TTC-indexet, az egyedi sfnt táblákat pedig a betűtípus-könyvtárból szolgálja ki, amikor a PDFium egy táblázatot kér a teljes fájl helyett. Ez az utolsó képesség azt jelenti, hogy egy szolgáltató átadhat egy teljes betűtípusfájlt, és hagyhatja, hogy a komponens válaszoljon a táblázatszintű lekérdezésekre, ahelyett hogy nyers Pascal-objektumokat tenne ki a C ABI-n keresztül
Gyorsítótárazás lógó betűtípusadatok nélkül
A betűtípus-leképezési kérések rendereléskor folyamatosan ismétlődnek, ezért a válaszokat egy minden betűtípus-kiválasztási paramétert lefedő kulccsal gyorsítótárazza, korlátozott legrégebben-használt sorrendben kiürítve. A finomság az élettartam: a PDFium még olvashatja egy olyan betűtípus bájtjait, amelynek gyorsítótár-bejegyzését éppen most ürítették ki
A gyorsítótár referenciaszámlált dinamikus tömböket tárol, és minden natív leíró a saját pillanatképét tartja, így a kiürítés egy hivatkozást ejt el, nem szabadít fel használatban lévő memóriát. A törlési visszahívás felszabadítja a leírót, és fenntart egy aktív számlálót. Gyakorlatilag ez azt jelenti, hogy a MaxCacheEntries memóriára hangolható anélkül, hogy fennállna a veszélye, hogy adatokat húznánk ki egy folyamatban lévő renderelés alól
A saját szálamon hívódik meg a szolgáltató?
Nem, nem feltétlenül. A PDFium a saját munkásszálairól is hívhatja a leképezőt, így egy implementációnak szálbiztosnak kell lennie. A megosztott számlálókat, a gyorsítótárat és a konfiguráció megfigyelését a komponens saját kritikus szekciója védi mindegyiket külön-külön, de a ResolveFont-on belüli kódot neked kell biztonságossá tenned
A legbiztonságosabb forma egy olyan szolgáltató, amely semmilyen módosítható megosztott állapotot nem érint: olvass egy induláskor felépített táblából, tölts be bájtokat egy fájlból vagy erőforrásból, adj vissza. Ha egy keresésnek saját megosztott gyorsítótárra van szüksége, védd azt. És tartsd bent a kivételeket a saját implementációdon belül, mivel egy Pascal-kivétel soha nem tekeredhet vissza a PDFium veremén keresztül; a komponens a C ABI-határon elkapja, és hibává vagy egy opcionális alapértelmezett visszaesésre alakítja, de ha erre normál vezérlésfolyamként támaszkodsz, az teljesítménybe kerül és hibákat rejt el. A komponens többi részének szálkezelési szabályai ugyanazokat az elveket követik, mint amelyeket a renderzár-fegyelem ismertet
A leképezés bizonyítása éles környezetben
A statisztikák a betűtípus-helyettesítést találgatásból olyan dologgá alakítják, amire állításokat tehetsz. A GetSystemFontProviderStatistics jelenti, hogy van-e konfigurálva és telepítve szolgáltató, hány leképezési kérés érkezett, és hogyan lettek kielégítve, gyorsítótár-találatokra, szolgáltatói találatokra és alapértelmezett visszaesési találatokra bontva, az elutasított válaszokkal, a sikertelen kérésekkel, az élő leírókkal és a gyorsítótárazott betűtípusokkal együtt:
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]));
// Egy megfelelőségi futtatásban, ahol a visszaesés le van tiltva, bármilyen
// visszaesési találat vagy sikertelen kérés azt jelenti, hogy egy dokumentum
// olyan betűtípusra hivatkozott, amelyet nem szállítunk
if (Stats.DefaultFallbackHits > 0) or (Stats.FailedRequests > 0) then
raise Exception.Create('unmapped font encountered - update the font set');
end;
Egy emelkedő RejectedProviderResponses szám az a jel, hogy egy szolgáltató olyan adattal válaszol, amelyet a szabályzat elutasít, általában egy túlméretezett fájl vagy egy helyettesített betűkép, és érdemes rá riasztást állítani, mert ezek a kérések csendben visszaesésre vagy hibára degradálódnak. Annak diagnosztizálásához, hogy egy dokumentumnak valójában mely betűtípusokra van szüksége, mielőtt felépítenéd a leképezési táblát, a PDF-betűtípus-tulajdonságok elemzése cikkben található vizsgálati útvonal dokumentumonként felsorolja a beágyazott és be nem ágyazott betűtípusokat
A betűtípus-ellátás, a renderelés és a szövegkinyerés ugyanazt a könyvtárpéldányt osztja meg Delphiben, C++Builderben és Lazarusban; a telepítési részleteket a PDFium Component for Delphi oldala ismerteti