Techninis straipsnis

Kintami šriftai PDF: statinis instancijavimas Delphi aplinkoje

PDF formatas neturi kintamo šrifto sąvokos. Į PDF failą įterptas šriftas yra fiksuotas kontūrų rinkinys su fiksuota metrika, todėl kintamą šriftą prieš įtraukiant į dokumentą reikia sumažinti iki vieno statinio egzemplioriaus. HotPDF šį instancijavimą atlieka viduje: patikrinate kintamo šrifto ašis, pasirenkate koordinates, pavyzdžiui, storį 620 arba plotį 87,5, ir biblioteka „įkepa“ šias reikšmes į pilną, savarankišką šrifto programą, kurią gali atvaizduoti bet kuris standartus atitinkantis PDF skaitytuvas

Tai svarbu ne teoriškai, o praktiškai. Šriftų liejyklos vis dažniau pateikia vieną kintamą failą vietoj tuzino statinių storių, o dizaino komandos renkasi reikšmes, kurių neturi nė vienas pavadintas egzempliorius. Be instancijavimo ataskaitų generatorius arba pereina prie numatytojo egzemplioriaus, taip atsisakydamas priimto dizaino sprendimo, arba įterpia visą kintamą šriftą ir tikisi, kad peržiūros priemonė pagerbs ašių koordinates, apie kurias ji neturi jokio būdo sužinoti, o to daryti neprivalo joks skaitytuvas

Ką iš tikrųjų reikia perkurti instancijuojant?

OpenType kintamas šriftas kiekvienam glifui saugo vieną numatytąjį kontūrą plius deltų rinkinį, indeksuotą pagal poziciją dizaino erdvėje. Ašies koordinatės pritaikymas nėra tiesiog skaičiaus įrašymas į antraštę; tai reiškia gvar lentelės apėjimą, deltų interpoliavimą prašomai vietai, taškų perkėlimą ir tada visko, kas buvo išvesta iš tų taškų, perskaičiavimą. HotPDF perkuria glifų kontūrus, ilgąją loca lentelę, pilną horizontalią ir vertikalią metriką, bendrą šrifto apribojantį stačiakampį ir sfnt kontrolinės sumos pataisą

Ne mažiau svarbu ir tai, kas pašalinama. Statinis egzempliorius neturi išlaikyti fvar, avar, gvar, HVAR, VVAR, MVAR, STAT ar cvar, taip pat turi būti pašalintas pasenęs DSIG, nes pasirašyti baitai jau nebeegzistuoja. Palikus bent vieną iš jų gaunamas šriftas, kuris teigia esąs kintamas, bet turi jau perkeltus kontūrus, o skaitytuvai, kurie iš tikrųjų taiko variacijas, pritaikys jas dar kartą

Fantominiai taškai ir dvigubo pritaikymo spąstai

Subtiliausia viso proceso taisyklė susijusi su metrika. gvar lentelėje glifo taškų skaičius apima kontūro taškus arba sudėtinio glifo komponentų taškus, plius keturis fantominius taškus, koduojančius kairįjį krašto tarpą, pastūmos plotį ir jų vertikalius atitikmenis. Šie fantominiai taškai patys taip pat priklauso deltoms

Todėl kai šriftas turi gvar lentelę, HotPDF gauna horizontalią ir vertikalią metriką iš interpoliuotų fantominių taškų ir papildomai netaiko HVAR ar VVAR. Pritaikyti abu yra klasikinė klaida: ta pati variacija pritaikoma du kartus, ir kiekviena pastūma išeina šiek tiek per plati, o tai matosi kaip tekstas, palaipsniui slenkantis į dešinę lygiuotoje eilutėje. Tik kai šriftas neturi gvar, biblioteka „įkepa“ metrikos variacijų saugyklą tiesiai į hmtx arba vmtx

Dar du detalės palaiko geometrijos tikslumą. Fantominiai taškai niekada nedalyvauja kontūro interpoliacijoje, todėl paprastam glifui aiškiai neišvardyti taškai išvedami taikant IUP kiekvienam kontūrui, neįtraukiant fantominių taškų. O sudėtiniams glifams deltos pritaikomos komponentų poslinkiams, naudojantiems XY parametrus, po to rekursyviai perskaičiuojamos vaikinių elementų ribos. Ši rekursija turi apribotą gylį ir tikrinama dėl ciklų, nes priešiškas ar tiesiog sugadintas komponentų grafas kitaip galėtų rekursyviai kviestis be galo

Dizaino erdvės patikrinimas prieš pasirenkant

Pirmasis iškvietimas bet kuriame instancijavimo darbo sraute yra InspectVariableFont, kuris praneša ašis ir liejyklos apibrėžtus pavadintus egzempliorius. Ašių įrašai turi keturių baitų žymę, minimalią, numatytąją ir maksimalią reikšmes, vėliavėles ir pavadinimo ID; pavadinti egzemplioriai turi subšeimos pavadinimo ID, vėliavėles, nebūtiną PostScript pavadinimo ID ir po vieną koordinatę kiekvienai ašiai:

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;

Ašies diapazono pranešimas svarbus todėl, kad ašies reikšmės apribojamos pagal diapazoną, kurį deklaruoja šriftas, o ne pagal diapazoną, kurį siūlo jūsų sąsaja. Slankiklis, leidžiantis vartotojui prašyti storio 1000 šriftui, kurio wght ašis baigiasi ties 900, turėtų būti ištaisytas sąsajos lygmenyje, o ne tyliai šrifto lygmenyje, kitaip spausdintas rezultatas neatitiks peržiūros

Koordinačių pasirinkimas ir dokumento generavimas

Ašies pasirinkimas yra būsenoje ir taikomas po to registruojamiems šriftams. SetVariableFontAxis priima keturių baitų spausdintiną ASCII žymę ir baigtinę reikšmę, o bet ką kitą atmeta su išimtimi, o ne tyliai ignoruoja. ClearVariableFontAxes atkuria pasirinkimą į numatytąjį, o GetVariableFontAxisSelections praneša, kas šiuo metu laukia pritaikymo – tai verta registruoti ataskaitų varikliuose, kur kelios kodo šakos gali būti palietusios tą patį dokumento objektą. Pati šeima pasirenkama pagal pavadinimą per SetFont, lygiai taip pat, kaip ir bet kuris kitas įterptas TrueType šriftas:

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

    Pdf.SetVariableFontAxis('wght', 620);   // pusjuodis, ne pavadintas egzempliorius
    Pdf.SetVariableFontAxis('wdth', 87.5);  // šiek tiek suspaustas
    Pdf.CurrentPage.SetFont('Inter', [], 11);
    Pdf.CurrentPage.TextOut(72, 720, 0, 'Quarterly results');

    Pdf.ClearVariableFontAxes;              // grąžinama į numatytąjį egzempliorių
    Pdf.CurrentPage.SetFont('Inter', [], 10);
    Pdf.CurrentPage.TextOut(72, 700, 0, 'Prepared by the finance team');

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

Įvykis OnVariableFontInstance suveikia kiekvieną kartą sukūrus egzempliorių ir praneša panaudotas ašių reikšmes – tai pigiausias būdas žurnale įrodyti, kas iš tikrųjų yra konkrečiame PDF faile. Kadangi kiekvienas skirtingas koordinačių rinkinys duoda skirtingą šrifto programą, ašių pasirinkimus verta traktuoti kaip savo šrifto talpyklos rakto dalį; talpyklos mechanika aprašyta straipsnyje nuolatinė šrifto poaibio talpykla

Kaip instancijavimas sąveikauja su poaibio kūrimu ir formavimu

Instancijavimas vyksta prieš poaibio kūrimą, ir ši tvarka yra teisinga. Instancijuotas šriftas yra įprastas statinis TrueType šriftas, todėl paprastasis poaibio kūrėjas jį traktuoja kaip bet kurį kitą: apskaičiuoja glifų uždarinį, palieka glifus, kuriuos dokumentas iš tikrųjų naudoja, ir atmeta likusius. Verta atkreipti dėmesį į tai, kad tos pačios šeimos dvi skirtingos ašių pasirinktys yra dvi skirtingos šrifto programos, todėl dokumentas, kuriame maišomas storis 400 ir storis 620, įterpia du poaibius, o ne vieną bendrą šriftą su dviem egzemplioriais

Formavimas iš principo nekinta, tačiau tai verta patikrinti praktiškai. Maketavimo funkcijos gyvena GSUB ir GPOS lentelėse, kurias instancijavimas išlaiko, todėl ligatūros ir stilistiniai variantai toliau veikia, kaip aprašyta straipsnyje OpenType GSUB stilistiniai variantai. Kas pasikeičia – tai pozicionavimas: suspausto egzemplioriaus pastūmos siauresnės nei numatytojo, todėl bet koks maketas, kuris matavo tekstą prieš instancijavimą, matavo neteisingus pločius. Matuokite su ta pačia ašių pasirinktimi, kurią naudosite atvaizdavimui, ir neatitikimas išnyksta

Paskutinė apsauginė pastaba iš realizacijos, naudinga kiekvienam, kas plečia šį kelią. Šriftai be vertikalios metrikos vis tiek įvertina dinaminių masyvų argumentus Delphi iškvietimo vietoje, todėl analizės metu naudojami masyvai visada paskirstomi, o ne pasikliaujama HasVerticalMetrics patikra, kuri turėtų trumpuoju keliu praleisti tuščią indeksą. Tai kalbos lygmens detalė, kuri iš pažiūros apsaugotą šaką paverčia prieigos pažeidimu būtent tuose šriftuose, su kuriais netestavote

Kintamų šriftų palaikymas įsilieja į tą patį šriftų konvejerį kaip įterpimas, poaibio kūrimas ir glifų uždarinys, kuris išsamiau aprašytas straipsnyje šrifto poaibio uždarinys ir suformuoti glifai. Pilnas tipografijos funkcijų rinkinys Delphi ir C++Builder aplinkoms pateikiamas HotPDF Delphi PDF komponento puslapyje