Tekninen artikkeli

Muuttuvat fontit PDF:ssä: staattinen instansiointi Delphissä

PDF:llä ei ole käsitettä muuttuvasta fontista. PDF-tiedostoon upotettu fontti on kiinteä joukko ääriviivoja kiinteillä mitoilla, joten muuttuva fontti täytyy pelkistää yhdeksi staattiseksi instanssiksi ennen kuin se voi päätyä dokumenttiin. HotPDF suorittaa tämän instansioinnin sisäisesti: tarkastellaan muuttuvan fontin akseleita, valitaan koordinaatit, kuten paksuus 620 tai leveys 87,5, ja kirjasto leipoo nämä arvot täydelliseksi, itsenäiseksi fonttiohjelmaksi, jonka mikä tahansa standardinmukainen PDF-lukija pystyy renderöimään

Syy tämän merkitykselle on käytännöllinen, ei teoreettinen. Kirjainvalimot toimittavat yhä useammin yhden muuttuvan tiedoston kymmenen staattisen paksuuden sijaan, ja suunnittelutiimit valitsevat arvoja, joita mikään nimetty instanssi ei tarjoa. Ilman instansiointia raporttigeneraattori joko palaa oletusinstanssiin, mikä hylkää suunnittelupäätöksen, tai upottaa koko muuttuvan fontin ja toivoo, että katseluohjelma kunnioittaa akselikoordinaatteja, joista sillä ei ole mitään tapaa tietää, mihin mikään lukija ei ole velvoitettu

Mitä instansiointi todella joutuu rakentamaan uudelleen?

OpenType-muuttuva fontti tallentaa yhden oletusääriviivan glyyfiä kohden sekä joukon delta-arvoja, jotka on indeksoitu sijainnin mukaan suunnitteluavaruudessa. Akselikoordinaatin soveltaminen ei ole vain luvun kirjoittamista otsikkoon; se tarkoittaa gvar-taulun läpikäymistä, pyydetyn sijainnin delta-arvojen interpolointia, pisteiden siirtämistä ja sitten kaiken näistä pisteistä johdetun uudelleenlaskemista. HotPDF rakentaa uudelleen glyyfien ääriviivat, pitkän loca-taulun, täydelliset vaaka- ja pystymitat, fontin yleisen rajausalueen sekä sfnt-tarkistussumman korjauksen

Yhtä tärkeää on se, mitä poistetaan. Staattinen instanssi ei saa säilyttää tauluja fvar, avar, gvar, HVAR, VVAR, MVAR, STAT tai cvar, ja myös vanhentunut DSIG täytyy poistaa, koska allekirjoitetut tavut eivät enää ole olemassa. Minkä tahansa näistä jättäminen paikalleen tuottaa fontin, joka väittää olevansa muuttuva, vaikka se kantaa jo siirrettyjä ääriviivoja, ja lukijat, jotka todella soveltavat muunnoksia, soveltavat ne silloin toiseen kertaan

Haamupisteet ja kaksinkertaisen soveltamisen ansa

Koko prosessin hienovaraisin sääntö koskee mittoja. gvar-taulussa glyyfin pistemäärä kattaa ääriviivan pisteet, tai yhdistelmäglyyfin komponenttipisteet, sekä neljä haamupistettä, jotka koodaavat vasemman sivureunuksen, etenemäleveyden ja niiden pystysuuntaiset vastineet. Nämä haamupisteet ovat itsekin delta-arvojen kohteena

Kun fontilla on gvar-taulu, HotPDF johtaa vaaka- ja pystymitat interpoloiduista haamupisteistä eikä sovella lisäksi tauluja HVAR tai VVAR. Molempien lisääminen on klassinen virhe: sama muunnos sovelletaan kahdesti ja jokainen etenemäleveys tulee hieman liian leveäksi, mikä näkyy tekstinä, joka ajautuu asteittain oikealle tasatulla rivillä. Vain kun fontilla ei ole gvar-taulua, kirjasto leipoo mittamuunnosvaraston suoraan tauluun hmtx tai vmtx

Kaksi muuta yksityiskohtaa pitävät geometrian täsmällisenä. Haamupisteet eivät koskaan osallistu ääriviivan interpolointiin, joten pisteet, joita ei ole nimenomaisesti listattu yksinkertaiselle glyyfille, päätellään IUP-menetelmällä ääriviivaa kohden, haamupisteet pois lukien. Ja yhdistelmäglyyfien delta-arvot sovelletaan XY-parametreja käyttäviin komponenttisiirtymiin, minkä jälkeen lapsielementtien rajat lasketaan uudelleen rekursiivisesti. Tämä rekursio on syvyydeltään rajoitettu ja syklitarkistettu, koska muutoin vihamielinen tai vain rikkinäinen komponenttigraafi voisi rekursoitua loputtomiin

Suunnitteluavaruuden tarkastelu ennen valintaa

Ensimmäinen kutsu missä tahansa instansiointityönkulussa on InspectVariableFont, joka raportoi akselit ja kirjainvalimon määrittelemät nimetyt instanssit. Akselitietueet kantavat nelitavuisen tunnisteen, minimi-, oletus- ja maksimiarvot, liput ja nimitunnisteen; nimetyt instanssit kantavat alaperheen nimitunnisteen, liput, valinnaisen PostScript-nimitunnisteen ja yhden koordinaatin akselia kohden:

var
  Pdf: THotPDF;
  Axes: THPDFVariableFontAxisArray;
  Instances: THPDFVariableFontNamedInstanceArray;
  I: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.InspectVariableFont('C:\Fonts\Inter.ttf', Axes, Instances) then
    begin
      for I := 0 to High(Axes) do
        Writeln(Format('%s  min=%.1f default=%.1f max=%.1f',
          [string(Axes[I].Tag), Axes[I].MinimumValue,
           Axes[I].DefaultValue, Axes[I].MaximumValue]));
      Writeln(Format('%d named instance(s) defined', [Length(Instances)]));
    end
    else
      Writeln('not a variable font - embed it as an ordinary TrueType face');
  finally
    Pdf.Free;
  end;
end;

Akselin arvoalueen raportoiminen on tärkeää, koska akseliarvot rajataan fontin ilmoittamaan alueeseen, ei käyttöliittymän tarjoamaan alueeseen. Liukusäädin, joka antaa käyttäjän pyytää paksuutta 1000 fontille, jonka wght-akseli päättyy arvoon 900, tulisi korjata käyttöliittymässä, ei hiljaisesti fonttitasolla, tai muuten tulostettu tuloste on ristiriidassa esikatselun kanssa

Koordinaattien valinta ja dokumentin tuottaminen

Akselin valinta on tilallista ja koskee jälkikäteen rekisteröityjä fontteja. SetVariableFontAxis ottaa nelitavuisen tulostettavan ASCII-tunnisteen ja äärellisen arvon, ja hylkää kaiken muun poikkeuksella hiljaisen ohittamisen sijaan. ClearVariableFontAxes nollaa valinnan, ja GetVariableFontAxisSelections raportoi, mikä on parhaillaan voimassa, ja tämä kannattaa kirjata lokiin raporttimoottoreissa, joissa useat koodipolut ovat saattaneet koskettaa samaa dokumenttiobjektia. Itse fonttiperhe valitaan nimellä SetFont-kutsulla, aivan kuten mikä tahansa muu upotettu TrueType-kirjasin:

begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.BeginDoc;

    Pdf.SetVariableFontAxis('wght', 620);   // puolilihava, ei nimetty instanssi
    Pdf.SetVariableFontAxis('wdth', 87.5);  // hieman kavennettu
    Pdf.CurrentPage.SetFont('Inter', [], 11);
    Pdf.CurrentPage.TextOut(72, 720, 0, 'Quarterly results');

    Pdf.ClearVariableFontAxes;              // takaisin oletusinstanssiin
    Pdf.CurrentPage.SetFont('Inter', [], 10);
    Pdf.CurrentPage.TextOut(72, 700, 0, 'Prepared by the finance team');

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

OnVariableFontInstance-tapahtuma laukeaa jokaisen instanssin tuottamisen yhteydessä ja raportoi käytetyt akseliarvot, mikä on halvin tapa todistaa lokissa, mitä tietty PDF todella sisältää. Koska jokainen erillinen koordinaattijoukko tuottaa erillisen fonttiohjelman, kohtele akselin valintoja osana fonttivälimuistin avainta; välimuistin mekaniikka kuvataan artikkelissa pysyvä fonttiosajoukkovälimuisti

Miten instansiointi vaikuttaa osajoukon muodostukseen ja muotoiluun

Instansiointi suoritetaan ennen osajoukon muodostusta, ja tämä järjestys on oikea. Instansoitu fontti on tavallinen staattinen TrueType-kirjasin, joten tavallinen osajoukon muodostaja käsittelee sitä kuten mitä tahansa muuta: se laskee glyyfisulkeuman, säilyttää dokumentin tosiasiassa käyttämät glyyfit ja pudottaa loput. Huomioon otettava vuorovaikutus on se, että saman perheen kaksi eri akselivalintaa ovat kaksi eri fonttiohjelmaa, joten dokumentti, joka sekoittaa paksuutta 400 ja paksuutta 620, upottaa kaksi osajoukkoa, ei yhtä jaettua kirjasinta kahdella instanssilla

Muotoilu ei periaatteessa muutu, mutta se kannattaa tarkistaa käytännössä. Asettelun ominaisuudet asuvat tauluissa GSUB ja GPOS, jotka instansiointi säilyttää, joten ligatuurit ja tyylivaihtoehdot jatkavat toimintaansa kuten artikkelissa OpenType GSUB -tyylivaihtoehdot kuvataan. Se, mikä muuttuu, on sijoittelu: kavennetulla instanssilla on kapeammat etenemät kuin oletuksella, joten mikä tahansa asettelu, joka mittasi tekstin ennen instansiointia, mittasi väärät leveydet. Mittaa samalla akselivalinnalla, jolla renderöit, ja poikkeama katoaa

Vielä yksi puolustava huomio toteutuksesta, hyödyllinen kaikille tätä polkua laajentaville. Fontit ilman pystymittoja arvioivat silti dynaamisten taulukoiden argumentit Delphi-kutsukohdassa, joten jäsennysaikaiset taulukot varataan aina sen sijaan, että luotettaisiin HasVerticalMetrics-tarkistukseen oikosulkemaan tyhjän indeksin ohi. Tämä on juuri sellainen kielitason yksityiskohta, joka muuttaa näennäisesti suojatun haaran pääsyrikkomukseksi juuri niillä fonteilla, joita et testannut

Muuttuvien fonttien tuki sopii samaan fonttiputkeen upottamisen, osajoukon muodostuksen ja glyyfisulkeuman kanssa, joita käsitellään syvemmin artikkelissa fontin osajoukon sulkeuma ja muotoillut glyyfit. Täydellinen typografiaominaisuuksien luettelo Delphille ja C++Builderille on sivulla HotPDF Delphi PDF -komponentin sivulla