[varLib] Rewrite axis configuration code

We do not accept nonstandard axes if <axes> element is not present.
Breaks one test.  Needs updated expectations.

Soon we'll be generating avar as well and all will be better, much better.
This commit is contained in:
Behdad Esfahbod 2017-04-12 17:14:22 -07:00
parent e73cc7ae34
commit 9034f4f3f1
3 changed files with 104 additions and 246 deletions

View File

@ -46,32 +46,26 @@ class VarLibError(Exception):
# #
# Move to fvar table proper? # Move to fvar table proper?
def _add_fvar(font, axes, instances, axis_map): def _add_fvar(font, axes, instances):
""" """
Add 'fvar' table to font. Add 'fvar' table to font.
axes is a dictionary mapping axis-id to axis (min,default,max) axes is an ordered dictionary of DesignspaceAxis objects.
coordinate values.
instances is list of dictionary objects with 'location', 'stylename', instances is list of dictionary objects with 'location', 'stylename',
and possibly 'postscriptfontname' entries. and possibly 'postscriptfontname' entries.
axis_map is an ordered dictionary mapping axis-id to (axis-tag, axis-name-dict).
""" """
assert "fvar" not in font assert "fvar" not in font
font['fvar'] = fvar = newTable('fvar') font['fvar'] = fvar = newTable('fvar')
nameTable = font['name'] nameTable = font['name']
for iden in axis_map.keys(): for a in axes.values():
if iden not in axes:
continue
axis = Axis() axis = Axis()
axis.axisTag = Tag(axis_map[iden][0]) axis.axisTag = Tag(a.tag)
axis.minValue, axis.defaultValue, axis.maxValue = axes[iden] axis.minValue, axis.defaultValue, axis.maxValue = a.minimum, a.default, a.maximum
# TODO: Add all languages: https://github.com/fonttools/fonttools/issues/921 # TODO: Add all languages: https://github.com/fonttools/fonttools/issues/921
axisName = tounicode(axis_map[iden][1]['en']) axis.axisNameID = nameTable.addName(tounicode(a.labelname['en']))
axis.axisNameID = nameTable.addName(axisName)
fvar.axes.append(axis) fvar.axes.append(axis)
for instance in instances: for instance in instances:
@ -84,7 +78,7 @@ def _add_fvar(font, axes, instances, axis_map):
if psname is not None: if psname is not None:
psname = tounicode(psname) psname = tounicode(psname)
inst.postscriptNameID = nameTable.addName(psname) inst.postscriptNameID = nameTable.addName(psname)
inst.coordinates = {axis_map[k][0]:v for k,v in coordinates.items()} inst.coordinates = {axes[k].tag:v for k,v in coordinates.items()}
fvar.instances.append(inst) fvar.instances.append(inst)
return fvar return fvar
@ -368,12 +362,6 @@ def build(designspace_filename, master_finder=lambda s:s):
raise VarLibError("no sources found in .designspace") raise VarLibError("no sources found in .designspace")
instances = ds.get('instances', []) instances = ds.get('instances', [])
log.info("Building variable font")
log.info("Loading master fonts")
basedir = os.path.dirname(designspace_filename)
master_ttfs = [master_finder(os.path.join(basedir, m['filename'])) for m in masters]
master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs]
standard_axis_map = OrderedDict([ standard_axis_map = OrderedDict([
('weight', ('wght', {'en':'Weight'})), ('weight', ('wght', {'en':'Weight'})),
('width', ('wdth', {'en':'Width'})), ('width', ('wdth', {'en':'Width'})),
@ -381,80 +369,103 @@ def build(designspace_filename, master_finder=lambda s:s):
('optical', ('opsz', {'en':'Optical Size'})), ('optical', ('opsz', {'en':'Optical Size'})),
]) ])
# Setup axes
class DesignspaceAxis(object):
pass
axis_objects = OrderedDict()
if axes is not None: if axes is not None:
# the designspace file loaded had an <axes> element. for axis_dict in axes:
# honor the order of the axes axis_name = axis_dict.get('name')
axis_map = OrderedDict() if not axis_name:
for axis in axes: axis_name = axis_dict['name'] = axis_dict['tag']
axis_name = axis['name']
if axis_name in standard_axis_map: if axis_name in standard_axis_map:
axis_map[axis_name] = standard_axis_map[axis_name] if 'tag' not in axis_dict:
axis_dict['tag'] = standard_axis_map[axis_name][0]
if 'labelname' not in axis_dict:
axis_dict['labelname'] = standard_axis_map[axis_name][1].copy()
axis = DesignspaceAxis()
for item in ['name', 'tag', 'labelname', 'minimum', 'default', 'maximum']:
assert item in axis_dict, 'Axis does not have "%s"' % item
axis.__dict__ = axis_dict
axis_objects[axis_name] = axis
else: else:
tag = axis['tag'] # No <axes> element. Guess things...
assert axis['labelname']['en']
labels = axis['labelname']
axis_map[axis_name] = (tag, labels)
else:
axis_map = standard_axis_map
master_locs = [o['location'] for o in masters]
axis_names = set(master_locs[0].keys())
assert all(axis_names == set(m.keys()) for m in master_locs)
base_idx = None base_idx = None
for i,m in enumerate(masters): for i,m in enumerate(masters):
if 'info' in m and m['info']['copy']: if 'info' in m and m['info']['copy']:
assert base_idx is None assert base_idx is None
base_idx = i base_idx = i
# Set up axes
axes_dict = {}
if axes is not None:
# the designspace file loaded had an <axes> element
for axis in axes:
default = axis['default']
lower = axis['minimum']
upper = axis['maximum']
name = axis['name']
axes_dict[name] = (lower, default, upper)
else:
for name in axis_names:
default = master_locs[base_idx][name]
lower = min(m[name] for m in master_locs)
upper = max(m[name] for m in master_locs)
if default == lower == upper:
continue
axes_dict[name] = (lower, default, upper)
log.info("Axes:\n%s", pformat(axes_dict))
assert all(name in axis_map for name in axes_dict.keys())
assert base_idx is not None, "Cannot find 'base' master; Either add <axes> element to .designspace document, or add <info> element to one of the sources in the .designspace document." assert base_idx is not None, "Cannot find 'base' master; Either add <axes> element to .designspace document, or add <info> element to one of the sources in the .designspace document."
log.info("Index of base master: %s", base_idx)
master_locs = [o['location'] for o in masters]
base_loc = master_locs[base_idx]
axis_names = set(base_loc.keys())
assert all(name in standard_axis_map for name in axis_names), "Non-standard axis found and there exist no <axes> element."
for name,(tag,labelname) in standard_axis_map.items():
if name not in axis_names:
continue
axis = Axis()
axis.name = name
axis.tag = tag
axis.labelname = labelname.copy()
axis.default = base_loc[name]
axis.minimum = min(m[name] for m in master_locs if name in m)
axis.maximum = max(m[name] for m in master_locs if name in m)
axis_objects[name] = axis
del base_idx, base_loc, axis_names, master_locs
axes = axis_objects
del axis_objects
axis_supports = {}
for axis in axes.values():
axis_supports[axis.name] = (axis.minimum, axis.default, axis.maximum)
log.info("Axis supports:\n%s", pformat(axis_supports))
# TODO
# check all masters and instances locations are valid and fill in default items
master_locs = [o['location'] for o in masters]
log.info("Master locations:\n%s", pformat(master_locs)) log.info("Master locations:\n%s", pformat(master_locs))
# We can use the base font straight, but it's faster to load it again since # Normalize master locations
# then we won't be recompiling the existing ('glyf', 'hmtx', ...) tables. master_locs = [models.normalizeLocation(m, axis_supports) for m in master_locs]
#gx = master_fonts[base_idx] log.info("Normalized master locations:\n%s", pformat(master_locs))
gx = TTFont(master_ttfs[base_idx])
# Find base master
base_idx = None
for i,m in enumerate(master_locs):
if all(v == 0 for v in m.values()):
assert base_idx is None
base_idx = i
assert base_idx is not None, "Base master not found; no master at default location?"
log.info("Index of base master: %s", base_idx)
log.info("Building variable font")
log.info("Loading master fonts")
basedir = os.path.dirname(designspace_filename)
master_ttfs = [master_finder(os.path.join(basedir, m['filename'])) for m in masters]
master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs]
# Reload base font as target font
vf = TTFont(master_ttfs[base_idx])
# TODO append masters as named-instances as well; needs .designspace change. # TODO append masters as named-instances as well; needs .designspace change.
fvar = _add_fvar(gx, axes_dict, instances, axis_map) fvar = _add_fvar(vf, axes, instances)
# Normalize master locations
master_locs = [models.normalizeLocation(m, axes_dict) for m in master_locs]
log.info("Normalized master locations:\n%s", pformat(master_locs))
# TODO Clean this up. # TODO Clean this up.
del instances del instances
del axes_dict del axis_supports
master_locs = [{axis_map[k][0]:v for k,v in loc.items()} for loc in master_locs]
#instance_locs = [{axis_map[k][0]:v for k,v in loc.items()} for loc in instance_locs] # Map from axis names to axis tags...
master_locs = [{axes[k].tag:v for k,v in loc.items()} for loc in master_locs]
#del axes
# From here on, we use fvar axes only
axisTags = [axis.axisTag for axis in fvar.axes] axisTags = [axis.axisTag for axis in fvar.axes]
# Assume single-model for now. # Assume single-model for now.
@ -462,13 +473,13 @@ def build(designspace_filename, master_finder=lambda s:s):
assert 0 == model.mapping[base_idx] assert 0 == model.mapping[base_idx]
log.info("Building variations tables") log.info("Building variations tables")
_add_MVAR(gx, model, master_fonts, axisTags) _add_MVAR(vf, model, master_fonts, axisTags)
if 'glyf' in gx: if 'glyf' in vf:
_add_gvar(gx, model, master_fonts) _add_gvar(vf, model, master_fonts)
_add_HVAR(gx, model, master_fonts, axisTags) _add_HVAR(vf, model, master_fonts, axisTags)
_merge_OTL(gx, model, master_fonts, axisTags, base_idx) _merge_OTL(vf, model, master_fonts, axisTags, base_idx)
return gx, model, master_ttfs return vf, model, master_ttfs
def main(args=None): def main(args=None):
@ -486,10 +497,10 @@ def main(args=None):
finder = lambda s: s.replace('master_ufo', 'master_ttf_interpolatable').replace('.ufo', '.ttf') finder = lambda s: s.replace('master_ufo', 'master_ttf_interpolatable').replace('.ufo', '.ttf')
outfile = os.path.splitext(designspace_filename)[0] + '-VF.ttf' outfile = os.path.splitext(designspace_filename)[0] + '-VF.ttf'
gx, model, master_ttfs = build(designspace_filename, finder) vf, model, master_ttfs = build(designspace_filename, finder)
log.info("Saving variation font %s", outfile) log.info("Saving variation font %s", outfile)
gx.save(outfile) vf.save(outfile)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -13,31 +13,11 @@
<info copy="1" /> <info copy="1" />
<location> <location>
<dimension name="weight" xvalue="368" /> <dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</source> </source>
<source familyname="Test Family" filename="master_ufo/TestFamily-Master2.ufo" name="master_2" stylename="Master2"> <source familyname="Test Family" filename="master_ufo/TestFamily-Master2.ufo" name="master_2" stylename="Master2">
<location> <location>
<dimension name="weight" xvalue="1000" /> <dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location>
</source>
<source familyname="Test Family" filename="master_ufo/TestFamily-Master3.ufo" name="master_3" stylename="Master3">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="100" />
</location>
</source>
<source familyname="Test Family" filename="master_ufo/TestFamily-Master0.ufo" name="master_0" stylename="Master0">
<location>
<dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="100" />
</location>
</source>
<source familyname="Test Family" filename="master_ufo/TestFamily-Master4.ufo" name="master_4" stylename="Master4">
<location>
<dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="100" />
</location> </location>
</source> </source>
</sources> </sources>
@ -45,7 +25,6 @@
<instance familyname="Test Family" filename="instances/TestFamily-ExtraLight.ufo" name="instance_ExtraLight" postscriptfontname="TestFamily-ExtraLight" stylename="ExtraLight"> <instance familyname="Test Family" filename="instances/TestFamily-ExtraLight.ufo" name="instance_ExtraLight" postscriptfontname="TestFamily-ExtraLight" stylename="ExtraLight">
<location> <location>
<dimension name="weight" xvalue="0" /> <dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<kerning /> <kerning />
<info /> <info />
@ -53,7 +32,6 @@
<instance familyname="Test Family" filename="instances/TestFamily-Light.ufo" name="instance_Light" postscriptfontname="TestFamily-Light" stylename="Light"> <instance familyname="Test Family" filename="instances/TestFamily-Light.ufo" name="instance_Light" postscriptfontname="TestFamily-Light" stylename="Light">
<location> <location>
<dimension name="weight" xvalue="150" /> <dimension name="weight" xvalue="150" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<kerning /> <kerning />
<info /> <info />
@ -61,7 +39,6 @@
<instance familyname="Test Family" filename="instances/TestFamily-Regular.ufo" name="instance_Regular" postscriptfontname="TestFamily-Regular" stylename="Regular"> <instance familyname="Test Family" filename="instances/TestFamily-Regular.ufo" name="instance_Regular" postscriptfontname="TestFamily-Regular" stylename="Regular">
<location> <location>
<dimension name="weight" xvalue="394" /> <dimension name="weight" xvalue="394" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<kerning /> <kerning />
<info /> <info />
@ -69,7 +46,6 @@
<instance familyname="Test Family" filename="instances/TestFamily-Semibold.ufo" name="instance_Semibold" postscriptfontname="TestFamily-Semibold" stylename="Semibold"> <instance familyname="Test Family" filename="instances/TestFamily-Semibold.ufo" name="instance_Semibold" postscriptfontname="TestFamily-Semibold" stylename="Semibold">
<location> <location>
<dimension name="weight" xvalue="600" /> <dimension name="weight" xvalue="600" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<kerning /> <kerning />
<info /> <info />
@ -77,49 +53,41 @@
<instance familyname="Test Family" filename="instances/TestFamily-Bold.ufo" name="instance_Bold" postscriptfontname="TestFamily-Bold" stylename="Bold"> <instance familyname="Test Family" filename="instances/TestFamily-Bold.ufo" name="instance_Bold" postscriptfontname="TestFamily-Bold" stylename="Bold">
<location> <location>
<dimension name="weight" xvalue="824" /> <dimension name="weight" xvalue="824" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<glyphs> <glyphs>
<glyph name="dollar" unicode="0x24"> <glyph name="dollar" unicode="0x24">
<location> <location>
<dimension name="weight" xvalue="824" /> <dimension name="weight" xvalue="824" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<masters> <masters>
<master glyphname="dollar.nostroke" source="master_0"> <master glyphname="dollar.nostroke" source="master_0">
<location> <location>
<dimension name="weight" xvalue="0" /> <dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_1"> <master glyphname="dollar.nostroke" source="master_1">
<location> <location>
<dimension name="weight" xvalue="368" /> <dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_2"> <master glyphname="dollar.nostroke" source="master_2">
<location> <location>
<dimension name="weight" xvalue="1000" /> <dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_3"> <master glyphname="dollar.nostroke" source="master_3">
<location> <location>
<dimension name="weight" xvalue="1000" /> <dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_0"> <master glyphname="dollar.nostroke" source="master_0">
<location> <location>
<dimension name="weight" xvalue="0" /> <dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_4"> <master glyphname="dollar.nostroke" source="master_4">
<location> <location>
<dimension name="weight" xvalue="368" /> <dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
</masters> </masters>
@ -131,157 +99,41 @@
<instance familyname="Test Family" filename="instances/TestFamily-Black.ufo" name="instance_Black" postscriptfontname="TestFamily-Black" stylename="Black"> <instance familyname="Test Family" filename="instances/TestFamily-Black.ufo" name="instance_Black" postscriptfontname="TestFamily-Black" stylename="Black">
<location> <location>
<dimension name="weight" xvalue="1000" /> <dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<glyphs> <glyphs>
<glyph name="dollar" unicode="0x24"> <glyph name="dollar" unicode="0x24">
<location> <location>
<dimension name="weight" xvalue="1000" /> <dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location> </location>
<masters> <masters>
<master glyphname="dollar.nostroke" source="master_0"> <master glyphname="dollar.nostroke" source="master_0">
<location> <location>
<dimension name="weight" xvalue="0" /> <dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_1"> <master glyphname="dollar.nostroke" source="master_1">
<location> <location>
<dimension name="weight" xvalue="368" /> <dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_2"> <master glyphname="dollar.nostroke" source="master_2">
<location> <location>
<dimension name="weight" xvalue="1000" /> <dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_3"> <master glyphname="dollar.nostroke" source="master_3">
<location> <location>
<dimension name="weight" xvalue="1000" /> <dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_0"> <master glyphname="dollar.nostroke" source="master_0">
<location> <location>
<dimension name="weight" xvalue="0" /> <dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
<master glyphname="dollar.nostroke" source="master_4"> <master glyphname="dollar.nostroke" source="master_4">
<location> <location>
<dimension name="weight" xvalue="368" /> <dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
</masters>
</glyph>
</glyphs>
<kerning />
<info />
</instance>
<instance familyname="Test Family" filename="instances/TestFamily-BlackMediumContrast.ufo" name="instance_BlackMediumContrast" postscriptfontname="TestFamily-BlackMediumContrast" stylename="Black Medium Contrast">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="50" />
</location>
<glyphs>
<glyph name="dollar" unicode="0x24">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location>
<masters>
<master glyphname="dollar.nostroke" source="master_0">
<location>
<dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_1">
<location>
<dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_2">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_3">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_0">
<location>
<dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_4">
<location>
<dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
</masters>
</glyph>
</glyphs>
<kerning />
<info />
</instance>
<instance familyname="Test Family" filename="instances/TestFamily-BlackHighContrast.ufo" name="instance_BlackHighContrast" postscriptfontname="TestFamily-BlackHighContrast" stylename="Black High Contrast">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="100" />
</location>
<glyphs>
<glyph name="dollar" unicode="0x24">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location>
<masters>
<master glyphname="dollar.nostroke" source="master_0">
<location>
<dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_1">
<location>
<dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_2">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_3">
<location>
<dimension name="weight" xvalue="1000" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_0">
<location>
<dimension name="weight" xvalue="0" />
<dimension name="contrast" xvalue="0" />
</location>
</master>
<master glyphname="dollar.nostroke" source="master_4">
<location>
<dimension name="weight" xvalue="368" />
<dimension name="contrast" xvalue="0" />
</location> </location>
</master> </master>
</masters> </masters>

View File

@ -98,9 +98,7 @@ class BuildTest(unittest.TestCase):
# ----- # -----
def test_varlib_build_ttf(self): def test_varlib_build_ttf(self):
"""Designspace file contains <axes> element. """Designspace file contains <axes> element."""
build() is called without an axisMap parameter.
"""
suffix = '.ttf' suffix = '.ttf'
ds_path = self.get_test_input('Build.designspace') ds_path = self.get_test_input('Build.designspace')
ufo_dir = self.get_test_input('master_ufo') ufo_dir = self.get_test_input('master_ufo')
@ -121,9 +119,7 @@ class BuildTest(unittest.TestCase):
def test_varlib_build2_ttf(self): def test_varlib_build2_ttf(self):
"""Designspace file does not contain an <axes> element. """Designspace file does not contain an <axes> element."""
build() is called with an axisMap parameter.
"""
suffix = '.ttf' suffix = '.ttf'
ds_path = self.get_test_input('Build2.designspace') ds_path = self.get_test_input('Build2.designspace')
ufo_dir = self.get_test_input('master_ufo') ufo_dir = self.get_test_input('master_ufo')
@ -134,9 +130,8 @@ class BuildTest(unittest.TestCase):
for path in ttx_paths: for path in ttx_paths:
self.compile_font(path, suffix, self.tempdir) self.compile_font(path, suffix, self.tempdir)
axisMap = {'contrast': ('cntr', 'Contrast')}
finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
varfont, model, _ = build(ds_path, finder, axisMap) varfont, model, _ = build(ds_path, finder)
tables = ['GDEF', 'HVAR', 'fvar', 'gvar'] tables = ['GDEF', 'HVAR', 'fvar', 'gvar']
expected_ttx_path = self.get_test_output('Build.ttx') expected_ttx_path = self.get_test_output('Build.ttx')