1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
import io
from pathlib import Path
from typing import Mapping, Optional
import fontTools.designspaceLib
import fontTools.ttLib
import fontTools.ttLib.tables._n_a_m_e
import fontTools.varLib
import ufo2ft
import ufoLib2
import statmake.classes
import statmake.lib
def dump_axes(font, axes_array):
dump_list = []
for axis in axes_array:
entry = {
"Name": dump_name_ids(font, axis.AxisNameID),
"AxisTag": axis.AxisTag,
"AxisOrdering": axis.AxisOrdering,
}
dump_list.append(entry)
return dump_list
def dump_axis_values(font, axis_value_array):
dump_list = []
for axis in axis_value_array:
entry = {
"Format": axis.Format,
"Name": dump_name_ids(font, axis.ValueNameID),
"Flags": axis.Flags,
}
if axis.Format == 1:
entry["AxisIndex"] = axis.AxisIndex
entry["Value"] = axis.Value
elif axis.Format == 2:
entry["AxisIndex"] = axis.AxisIndex
entry["NominalValue"] = axis.NominalValue
entry["RangeMinValue"] = axis.RangeMinValue
entry["RangeMaxValue"] = axis.RangeMaxValue
elif axis.Format == 3:
entry["AxisIndex"] = axis.AxisIndex
entry["Value"] = axis.Value
entry["LinkedValue"] = axis.LinkedValue
elif axis.Format == 4:
entry["AxisValueRecord"] = [
(r.AxisIndex, r.Value) for r in axis.AxisValueRecord
]
else:
raise ValueError("Unknown format")
dump_list.append(entry)
return dump_list
def dump_name_ids(otfont: fontTools.ttLib.TTFont, name_id: int) -> Mapping[str, str]:
"""Return a mapping of language codes to name strings."""
name_mapping = fontTools.ttLib.tables._n_a_m_e._WINDOWS_LANGUAGES
name_table = otfont["name"].names
matches = {
name_mapping[n.langID]: n.toUnicode()
for n in name_table
if n.platformID == 3 and n.nameID == name_id
}
return matches
def empty_UFO(style_name: str) -> ufoLib2.Font:
# pylint: disable=assigning-non-slot
ufo = ufoLib2.Font()
ufo.info.familyName = "Test"
ufo.info.styleName = style_name
ufo.info.unitsPerEm = 1000
ufo.info.ascender = 800
ufo.info.descender = -200
ufo.info.xHeight = 500
ufo.info.capHeight = 700
ufo.info.postscriptUnderlineThickness = 50
ufo.info.postscriptUnderlinePosition = -75
g = ufo.newGlyph("a")
g.width = 500
return ufo
def reload_font(font):
buf = io.BytesIO()
font.save(buf)
buf.seek(0)
return fontTools.ttLib.TTFont(buf)
def generate_variable_font(
designspace_path: Path,
stylespace_path: Path,
additional_locations: Optional[Mapping[str, float]] = None,
) -> fontTools.ttLib.TTFont:
designspace = fontTools.designspaceLib.DesignSpaceDocument.fromfile(
designspace_path
)
for source in designspace.sources:
source.font = empty_UFO(source.styleName)
ufo2ft.compileInterpolatableTTFsFromDS(designspace, inplace=True)
varfont, _, _ = fontTools.varLib.build(designspace)
stylespace = statmake.classes.Stylespace.from_file(stylespace_path)
if additional_locations is None:
additional_locations = designspace.lib.get(
"org.statmake.additionalLocations", {}
)
statmake.lib.apply_stylespace_to_variable_font(
stylespace, varfont, additional_locations
)
return reload_font(varfont)
|