Tekninen artikkeli

Verkkofontit PDF:ssä: WOFF2 ja muuttuja-akselit Delphissä

PDF Library for Delphi hyväksyy fontit, jotka web-projekti jo toimittaa. Se tuo WOFF2-säiliöitä, luetteloi muuttujafontin suunnitteluakselit JSON:na, ja instantioi valitun akselikoordinaattijoukon staattiseksi fontiksi, jonka tavallinen upotus-, osittamis- ja poimintareitit käsittelevät tietämättä mitään variaatioista. Sekä TrueType- että CFF2-ääriviivat on katettu

Tilanne, jota tämä käsittelee, on yleinen ja ärsyttävä. Brändin typografia on määritelty suunnittelujärjestelmässä, suunnittelujärjestelmä toimittaa muuttuja-WOFF2-tiedostoja, eikä PDF-generaattori pysty lukemaan säiliötä eikä variaatiodataa. Tavallinen kiertotie — staattisten TTF-instanssien pyytäminen valimolta jokaisesta käytössä olevasta painosta — vie viikkoja ja tuottaa tiedostoja, joita kukaan ei pidä synkronissa

Miksi WOFF2 ei ole vain pakattu fontti?

WOFF2:n käsittely Brotli-kääreenä ja sen purkaminen antaa sinulle tavuja, jotka eivät ole kelvollinen sfnt. Muoto sallii myös muunnettuja ja uudelleenjärjestettyjä taulukoita: glyf, loca ja hmtx voidaan tallentaa uudelleenjärjestetyssä muodossa, joka täytyy kääntää takaisin ennen kuin fontti on jäsennettävissä. Yleiskäyttöinen purkaja ei pysty siihen

Windowsilla alustassa on ollut säiliöanalyysi ja purku Windows 10 -versiosta 1703 lähtien, joten kirjasto käyttää järjestelmätoteutusta sen sijaan, että toimittaisi toisen dekooderin. Tuo päätös pitää käyttöönoton vapaana ylimääräisestä DLL:stä ja tarkoittaa, että muunnoksen kääntäminen hoidetaan samalla koodilla, jota käyttöjärjestelmän oma tekstipino käyttää. Muilla alustoilla WOFF1 kulkee edelleen puhdasta Pascal-reittiä ja WOFF2 raportoi eksplisiittisesti tukemattomaksi, mikä on rehellinen vastaus sen sijaan, että se olisi hiljaisesti turmeltunut fontti

Tuontireitti validoi ennen dekoodausta ja sen jälkeen: 48-tavuinen otsikko, säiliön pituus, varatut kentät, ilmoitettu sfnt-koko ja 512 MiB:n katto tarkistetaan kaikki ensin, ja dekoodatun virran koko verrataan julistukseen jälkikäteen. Verkkofontti on ladattua sisältöä, ja kaksi estämisen arvoista epäonnistumistilaa ovat koon poikkeama ja purkupommi

Tämä putkikaavio näyttää, miten Delphi-PDF-kirjasto tuo WOFF2-verkkofontin otsikkovalidoinnin, järjestelmädekoodauksen, sfnt-muunnoksen kääntämisen ja dekoodauksen jälkeisen kokotarkastuksen kautta
WOFF2-säiliöt saapuvat ladattuna sisältönä, joten tuonti validoi otsikon ja ilmoitetut koot sekä ennen että jälkeen dekoodauksen
uses
  PDFlibrary;

var
  Lib: TPDFlib;
  FontID, InstanceID: Integer;
  AxesJSON: WideString;
begin
  Lib := TPDFlib.Create;
  try
    Lib.NewDocument;
    Lib.SetPageSize('A4');
    Lib.NewPage;

    // WOFF2, WOFF1, TTF, OTF ja TTC tulevat kaikki saman kutsun kautta
    FontID := Lib.AddOpenTypeFontFromFile('Inter.woff2', 0);
    if FontID <= 0 then
      raise Exception.Create('font could not be imported');

    // Mitä tämä fontti oikeasti osaa?
    if Lib.GetFontVariationAxes(FontID, AxesJSON) = 1 then
      Log(AxesJSON);   // tunnisteet, minimi, oletus, maksimi per akseli

    // Leipoi yksi suunnittelusijainti staattiseksi fontiksi
    InstanceID := Lib.CreateOpenTypeFontVariation(FontID,
      'wght=620,wdth=87.5');

    Lib.DrawOpenTypeText(56, 760, InstanceID, 24, 'Quarterly results', 0);
    Lib.SaveToFile('report.pdf');
  finally
    Lib.Free;
  end;
end;

Mitä instantiointi joutuu rakentamaan uudelleen, ja mitä se poistaa

Muuttujafontti tallentaa yhden oletusääriviivan per glyfi sekä delta-arvot indeksoituna suunnittelutilan positiolla. Staattisen instanssin tuottaminen tarkoittaa noiden deltojen interpolointia pyydetyissä koordinaateissa, pisteiden siirtämistä, ja sitten kaiken niistä johdetun uudelleenlaskemista

TrueType-ääriviivoille se tarkoittaa fvar:n, avar:n, gvar:n, HVAR:n ja MVAR:n jäsentämistä, tuple-deltojen soveltamista jaetulla ja yksityisellä pistenumeroinnilla, ja pisteiden interpolointia, joilla ei ole eksplisiittistä deltaa. Vaakasuuntaiset haamupisteet yhdessä HVAR:n kanssa määrittävät etenemäleveyden ja vasemman sivureunuksen. Yhdistelmäglyfit tarvitsevat komponenttiriippuvuutensa ja pistekuvauksensa ratkaistuna ensin, sitten käännös-, skaalaus-, matriisi- ja kiinnitysoffsetit sovellettuina — syvyysrajoilla ja syklien tunnistuksella, koska pahantahtoinen komponenttigraafi muuten rekursoituisi loputtomiin

Metatieto siirtyy myös, ja tämä on osa, jonka useimmat toteutukset ohittavat. MVAR päivittää globaalit mitat kentissä hhea, OS/2 ja post, joten riviväli, alleviivauksen sijainti ja kursiivikulma täsmäävät instanssia eivätkä oletusta. wght-, wdth-, ital- ja slnt-akselit lisäksi päivittävät painoluokan, leveysluokan, valintaliput, macStyle:n ja kursiivikulman, joten työkalu, joka tarkastaa staattisen fontin, raportoi pyytämäsi tyylin. Fontti, jonka geometria on lihavoitu ja jonka metatieto sanoo normaali, on väärin nimetty kaikkialla jatkokäytössä

Tuloste litistää sitten yhdistelmäääriviivat ja poistaa taulukot, joita ei enää sovelleta: variaatiotaulukot, vihjeet, laitetaulukot ja kaikki allekirjoitukset. Glyfirajat, hhea:n yhteenvetomitat, maxp:n vihjekatot, taulukkokohtaiset tarkistussummat ja koko fontin tarkistussumma kaikki lasketaan uudelleen

Tämä kaavio näyttää muuttujafontin instantioinnin rakentavan uudelleen TrueType-kirjasimen interpoloimalla gvar-deltat, laajentamalla yhdistelmäglyfit, laskemalla etenemät uudelleen ja kirjoittamalla tyylimetatiedon uudelleen ennen variaatiotaulukoiden poistamista
Instantiointi rakentaa uudelleen kaiken, mihin deltat koskettivat, ja poistaa sitten variaatiokoneiston, joten tavallinen upotus näkee pelkän staattisen fontin

CFF2 kulkee eri reittiä samaan paikkaan

CFF2:ta ei voi upottaa suoraan PDF-fonttiohjelmana: sen merkkijonoilla ei ole leveyksiä, ja se nojaa ympäröiviin OpenType-taulukoihin glyfijärjestyksen ja mittojen suhteen. Joten CFF2-fontin instantiointi tuottaa sen sijaan CID-avaimistetun CFF-OpenType-fontin, jota olemassa olevat piirto-, upotus- ja osittamisreitit jo käsittelevät

Muuttujafontin lataaminen tuottaa heti oletuskoordinaattisen staattisen instanssin nykyiselle asiakirjalle, kun taas alkuperäinen CFF2-sfnt säilyy fonttiobjektissa, joten akseliluettelointi ja myöhempi instantiointi näkevät yhä koko suunnittelutilan. Jokainen glyfi de-alirutiinoidaan yksitellen instantioinnin aikana, mikä poistaa variaatiolaskennan kokonaan ja antaa olemassa olevan osittajan toimia muuttumattomana. Kompromissi on, että staattinen instanssi voi olla hieman suurempi kuin sellainen, joka säilytti jaetut alirutiinit — kohtuullinen hinta siitä, ettei tarvitse toista osittamistoteutusta

Instanssin varmentaminen ennen kuin luotat siihen

Instantiointi on sellainen ominaisuus, joka voi olla hienovaraisesti väärin tavoilla, joita mikään poikkeus ei raportoi. Kolme tarkistusta napsauttaa lähes kaiken

Vie instanssi ja avaa se fonttityökalussa. SaveFontToFile kirjoittaa säilytetyn fonttiohjelman, joten muistissa oleva instanssi voidaan tyhjentää tuottamatta PDF:ää ensin. Viedyn tiedoston pitäisi jäsentyä puhtaasti, sisältää ei variaatiotaulukoita, ja raportoida pyytämäsi tyylimetatieto

Vertaa etenemiä muussa kuin oletussijainnissa. Jos putkesi voi tuottaa saman instanssin toisella tavalla, vertaa etenemäleveyksiä ja käyrärajoja glyfi glyfiltä; vertailutoteutus tässä täsmäsi riippumattoman instantioinnin kanssa 313-glyfisellä kaksiakselisella fontilla, suurimman eron ollessa nolla jokaisella glyfillä

Ja poimi teksti takaisin ulos generoidusta PDF:stä. Instanssi, joka upottuu mutta ei tuota poimittavaa tekstiä, on menettänyt ToUnicode-kuvauksensa, millä on merkitystä haulle ja saavutettavuudelle kauan sen jälkeen kun visuaalinen tarkistus meni läpi. Laajempi poimintareitti on käsitelty artikkelissa tekstin, kuvien ja fonttien poiminta

Tämä kaavio näyttää kolme varmennustarkistusta generoidulle muuttujafonttiinstanssille: viedyn fonttitiedoston tyhjentämisen, glyfietenemien vertaamisen riippumattomaan instantiointiin ja tekstin poimimisen takaisin ulos PDF:stä
Hienovaraisesti väärä instanssi ei heitä poikkeusta, joten viety tiedosto, glyfietenemät ja poimittu teksti kukin varmennetaan vuorollaan
// Vedä generoitu instanssi ulos tarkastelua tai uudelleenkäyttöä varten
Lib.SaveFontToFile('Inter-620-87.ttf');

// Pyydä koordinaatteja ilmoitetun alueen ulkopuolelta, ja ne leikataan,
// joten lue akseli-JSON sen sijaan että olettaisit fontin rajat
// {"axes":[{"tag":"wght","min":100,"default":400,"max":900}, ...]}

Käytännön huomioita brändifonttiputkelle

Välimuistita instanssit koordinaattijoukon mukaan. Jokainen erillinen koordinaattijoukko on erillinen fonttiohjelma, joten asiakirja, joka sekoittaa painoa 400 ja painoa 620, upottaa kaksi osajoukkoa yhden jaetun kirjasimen sijaan. Saman instanssin toistuva generointi on turhaa työtä; kymmenien lähes identtisten painojen generointi on turhaa tiedostokokoa

Mittaa sillä instanssilla, jolla piirrät. Kapealla instanssilla on kapeammat etenemät kuin oletuksella, joten mikä tahansa asettelu, joka mittasi ennen instantiointia, mittasi väärät leveydet. Tämä on yleisin yksittäinen syy tekstille, joka ylittää laatikkonsa vain tuotannossa

Pidä varareitti glyfeille, joita brändifontistasi puuttuu. Muuttuja brändifontti kattaa tyypillisesti latinalaiset merkit ja vähän muuta, ja raportti, joka sisältää kyrillisen asiakasnimen, näyttäisi muuten puuttuvia glyfejä; automaattinen varamekanismi on kuvattu artikkelissa automaattinen fontin varaminen puuttuville glyfeille. Ja kun tavoitteena on pienempi tiedosto tietyn painon sijaan, osittaminen tekee enemmän työtä kuin instantiointi, käsitelty artikkelissa PDF-tiedostokoon optimointi ja fontin osittaminen

Verkkofonttien tuonti, muuttujainstantiointi, osittaminen ja varaus ovat osa samaa fonttiputkea Delphille, C++Builderille ja Free Pascalille; täydellinen ominaisuusluettelo löytyy sivulta PDF Library for Delphi -sivu