PDFium Component antaa Delphi-sovelluksen päättää, mitä fonttitavuja käytetään, kun PDF viittaa fonttiin, jota se ei upota. ConfigureSystemFontProvider asentaa IPdfSystemFontProvider-toteutuksen, joka vastaanottaa jokaisen PDFiumin tekemän fonttikuvauspyynnön, mukaan lukien kirjasimen nimen, paksuuden, kursiivilipun, merkistön ja tasavälisyysperheen, ja vastaa käytettävillä TrueType-, TrueType-kokoelma- tai OpenType-tavuilla
Tämä on olemassa, koska upottamattomat fontit ovat renderöintiarpajaiset. PDF, joka nimeää Arialin eikä upota mitään, renderöityy Arialilla työasemalla, mittayhteensopivalla korvaajalla Linux-palvelimella ja millä tahansa, mitä isäntäkoneen kuvain löytää lukitulta säiliökuvalta. Sama lasku näyttää erilaiselta jokaisella, rivinvaihdot siirtyvät, ja asiakas saa dokumentin, joka ei täsmää arkistoituun kopioon
Miksei fontteja vain asenneta palvelimelle?
Joskus se on vastaus, ja silloin kannattaa käyttää sitä. Mutta se epäonnistuu kolmessa yleisessä tilanteessa. Lisensointi saattaa kieltää fontin asentamisen palvelimelle automatisoitua renderöintiä varten. Säiliökuvat rakennetaan usein uudelleen, ja käsin asennettu fontti katoaa seuraavan käyttöönoton myötä. Ja säännellyt työnkulut vaativat renderöintipinon olevan toistettavissa versionhallinnassa olevista artefakteista, mitä koko koneen laajuinen fonttiasennus ei ole
Tarjoaja ratkaisee kaikki kolme siirtämällä päätöksen sovellukseesi. Fontit toimitetaan resursseina, joita hallitset, kuvauskäytäntö on koodia, jota voit tarkastella, ja sama binääri renderöi identtisesti kaikkialla, koska mikään ei riipu siitä, mitä sattuu olemaan asennettuna
Tarjoajan asentaminen
Konfiguroinnin täytyy tapahtua ennen kirjaston lataamista. PDFium hyväksyy järjestelmän fonttitietorakenteen alustuksen yhteydessä ja säilyttää kahvat, jotka se antaa jälkikäteen, joten tarjoajan vaihtaminen dokumenttien ollessa auki mitätöisi fonttikahvat, joita PDFium yhä pitää hallussaan; komponentti hylkää tämän suoralta kädeltä sen sijaan, että antaisi sen turmella renderöinnin:
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
// Deterministinen kuvaus: kirjasimen nimi sekä paksuus ja kursiivi määräävät
// minkä tiedoston toimitamme tälle pyynnölle
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); // täydelliset sfnt- tai TTC-tavut
Font.Charset := Request.Charset;
Font.TTCIndex := 0; // indeksi kokoelman sisällä
end;
var
Policy: TPdfSystemFontPolicy;
begin
Policy := TPdfSystemFontPolicy.Default;
Policy.AllowDefaultFallback := False; // isäntä päättää kaiken
Policy.AllowFaceSubstitution := False; // hylkää eri kirjasimen nimi
Policy.MaxFontBytes := 32 * 1024 * 1024;
Policy.MaxCacheEntries := 64;
ConfigureSystemFontProvider(TAppFontProvider.Create, Policy);
// Vasta nyt ladataan kirjasto ja avataan dokumentit
end;
Purkaminen tapahtuu vastakkaisessa järjestyksessä: tarjoaja irrotetaan ensin PDFiumista, sitten kirjasto puretaan. Irrotuksen ohittaminen jättää natiivit fonttikahvat osoittamaan Pascal-objekteihin, jotka ollaan juuri vapauttamassa, mikä on klassinen sammutuksen pääsyrikkomus koodissa, joka sekoittaa viiteytettyjä rajapintoja C-kirjastoon
Mitä käytäntöliput todella päättävät
AllowDefaultFallback on kytkin kahden toimintatilan välillä. Kun se on pois päältä, pyyntö, jonka tarjoaja hylkää, yksinkertaisesti epäonnistuu, mikä on haluttua, kun todistetaan, että jokainen aineiston fontti on huomioitu: mikä tahansa aukko tulee näkyväksi välittömästi sen sijaan, että se peiteltäisiin. Kun se on päällä, ratkaisemattomat pyynnöt delegoidaan kuvaimelle, jonka palauttaa FPDF_GetDefaultSystemFontInfo, samalla kun ulkomaailma näkee edelleen yhden yhtenäisen kahvakäärimen, jossa kirjasimen nimi, merkistö, taulutiedot ja fontin poisto reititetään oikein alkuperän mukaan
AllowFaceSubstitution hallitsee, saako tarjoaja vastata eri kirjasimen nimellä kuin pyydetty. Sen pois kytkeminen tekee korvaamisesta selkeän päätöksen vahingon sijaan, millä on merkitystä, kun dokumentti nimeää fontin, jonka mitat eroavat tarpeeksi muuttaakseen sivutuksen
Komponentti validoi jokaisen tarjoajavastauksen, ennen kuin se saavuttaa PDFiumin: tyhjä data hylätään, ylikokoiset fontit hylätään arvoa MaxFontBytes vasten, TTC-indeksi tarkistetaan, ja yksittäiset sfnt-taulut tarjoillaan fonttihakemistosta, kun PDFium pyytää taulua koko tiedoston sijaan. Tämä viimeinen kyky tarkoittaa, että tarjoaja voi luovuttaa täydellisen fonttitiedoston ja antaa komponentin vastata taulutason kyselyihin sen sijaan, että se paljastaisi raakoja Pascal-objekteja C-ABI:n yli
Välimuisti ilman roikkuvaa fonttidataa
Fonttikuvauspyynnöt toistuvat jatkuvasti renderöinnin aikana, joten vastaukset välimuistitetaan avaimella, joka kattaa jokaisen fontin valintaparametrin, ja häädetään rajoitetun vähiten-äskettäin-käytetty-järjestyksen mukaan. Hienovaraisuus on elinaika: PDFium saattaa yhä lukea fontin tavuja, jonka välimuistimerkintä on juuri häädetty
Välimuisti tallentaa viiteytettyjä dynaamisia taulukoita, ja jokainen natiivi kahva pitää oman tilannekuvansa, joten häätö pudottaa viitteen sen sijaan, että se vapauttaisi käytössä olevaa muistia. Poistotakaisinkutsu vapauttaa kahvan ja ylläpitää aktiivista laskuria. Käytännössä tämä tarkoittaa, että MaxCacheEntries-arvoa voi säätää muistin mukaan ilman minkäänlaista riskiä datan vetämisestä pois käynnissä olevan renderöinnin alta
Kutsutaanko tarjoajaa omassa säikeessäni?
Ei, ei välttämättä. PDFium saattaa kutsua kuvainta omista työsäikeistään, joten toteutuksen täytyy olla säieturvallinen. Jaetut laskurit, välimuisti ja konfiguraation tarkkailu on kukin suojattu komponentin sisällä omalla kriittisellä osiollaan, mutta ResolveFont-metodin sisällä oleva koodi on sinun tehtäväsi tehdä turvalliseksi
Turvallisin muoto on tarjoaja, joka ei koske mihinkään muuttuvaan jaettuun tilaan: lue käynnistyksessä rakennetusta taulusta, lataa tavuja tiedostosta tai resurssista, palauta. Jos haku tarvitsee oman jaetun välimuistisi, suojaa se. Ja pidä poikkeukset toteutuksesi sisällä, koska Pascal-poikkeus ei koskaan saa purkautua PDFium-pinon läpi; komponentti nappaa sen C-ABI-rajalla ja muuntaa sen virheeksi tai valinnaiseksi oletuskorvaukseksi, mutta tähän luottaminen normaalina ohjausvuona maksaa suorituskykyä ja piilottaa virheitä. Loppukomponentin säikeistyssäännöt noudattavat samoja periaatteita kuin artikkelissa renderöintilukon kuri
Kuvauksen todistaminen tuotannossa
Tilastot muuttavat fonttien korvaamisen arvailusta jonkin, jota vastaan voi tehdä väitteitä. GetSystemFontProviderStatistics raportoi, onko tarjoaja konfiguroitu ja asennettu, kuinka monta kuvauspyyntöä tehtiin ja miten ne täytettiin, jaoteltuna välimuistiosumiin, tarjoajaosumiin ja oletuskorvausosumiin, sekä hylätyt vastaukset, epäonnistuneet pyynnöt, elävät kahvat ja välimuistitetut fontit:
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]));
// Vaatimustenmukaisuusajossa korvaus poissa käytöstä, mikä tahansa korvausosuma tai
// epäonnistunut pyyntö tarkoittaa, että dokumentti viittasi fonttiin, jota emme toimita
if (Stats.DefaultFallbackHits > 0) or (Stats.FailedRequests > 0) then
raise Exception.Create('unmapped font encountered - update the font set');
end;
Nouseva RejectedProviderResponses-määrä on signaali siitä, että tarjoaja vastaa datalla, jonka käytäntö hylkää, yleensä ylikokoisella tiedostolla tai korvatulla kirjasimella, ja siitä kannattaa hälyttää, koska nuo pyynnöt heikentyvät hiljaa korvaukseksi tai epäonnistumiseksi. Sen diagnosoimiseksi, mitä fontteja dokumentti todella tarvitsee ennen kuvaustaulun rakentamista, tarkastusreitti artikkelissa PDF-fonttien ominaisuuksien analysointi listaa upotetut ja upottamattomat fontit dokumenttia kohden
Fonttien tarjonta, renderöinti ja tekstin poiminta jakavat saman kirjastoinstanssin Delphin, C++Builderin ja Lazaruksen välillä; käyttöönoton yksityiskohdat kuvataan sivulla PDFium Component for Delphi -sivulla