From e464e450ac9dc25bf00035cf5eb96906fe88b7f9 Mon Sep 17 00:00:00 2001 From: Nikolaus Waxweiler Date: Wed, 19 Dec 2018 13:40:11 +0000 Subject: [PATCH] Make build and load_designspace accept a DS object --- Lib/fontTools/varLib/__init__.py | 43 ++++++++++++++++++++++++-------- Tests/varLib/varLib_test.py | 29 ++++++++++++++++++++- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 92d6faa8d..fa3539663 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -38,6 +38,7 @@ from fontTools.varLib.iup import iup_delta_optimize from fontTools.varLib.featureVars import addFeatureVariations from fontTools.designspaceLib import DesignSpaceDocument, AxisDescriptor from collections import OrderedDict, namedtuple +import io import os.path import logging from pprint import pformat @@ -647,9 +648,12 @@ def _add_CFF2(varFont, model, master_fonts): merge_region_fonts(varFont, model, ordered_fonts_list, glyphOrder) -def load_designspace(designspace_filename): +def load_designspace(designspace): + if hasattr(designspace, "sources"): # Assume a DesignspaceDocument + ds = designspace + else: # Assume a file path + ds = DesignSpaceDocument.fromfile(designspace) - ds = DesignSpaceDocument.fromfile(designspace_filename) masters = ds.sources if not masters: raise VarLibError("no sources found in .designspace") @@ -731,7 +735,7 @@ def load_designspace(designspace_filename): ) -def build(designspace_filename, master_finder=lambda s:s, exclude=[], optimize=True): +def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): """ Build variation font from a designspace file. @@ -740,15 +744,32 @@ def build(designspace_filename, master_finder=lambda s:s, exclude=[], optimize=T binary as to be opened (eg. .ttf or .otf). """ - ds = load_designspace(designspace_filename) - + ds = load_designspace(designspace) 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 ds.masters] - master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs] - # Reload base font as target font - vf = TTFont(master_ttfs[ds.base_idx]) + + if hasattr(designspace, "sources"): # Assume a DesignspaceDocument + for master in ds.masters: + if master.font is None: + raise AttributeError( + "designspace source '%s' is missing required 'font' attribute" + % getattr(master, "name", "") + ) + master_fonts = [master.font for master in ds.masters] + master_ttfs = [] + # Make a copy of the designated base font that we can then modify in-place. + buffer = io.BytesIO() + master_fonts[ds.base_idx].save(buffer) + buffer.seek(0) + vf = TTFont(buffer) + else: # Assume a file path + log.info("Loading master fonts") + basedir = os.path.dirname(designspace) + master_ttfs = [ + master_finder(os.path.join(basedir, m.filename)) for m in ds.masters + ] + master_fonts = [TTFont(ttf_path) for ttf_path in master_ttfs] + # Reload base font as target font + vf = TTFont(master_ttfs[ds.base_idx]) # TODO append masters as named-instances as well; needs .designspace change. fvar = _add_fvar(vf, ds.axes, ds.instances) diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py index 3253dbbb3..b9c822fec 100644 --- a/Tests/varLib/varLib_test.py +++ b/Tests/varLib/varLib_test.py @@ -3,7 +3,7 @@ from fontTools.misc.py23 import * from fontTools.ttLib import TTFont from fontTools.varLib import build from fontTools.varLib import main as varLib_main -from fontTools.designspaceLib import DesignSpaceDocumentError +from fontTools.designspaceLib import DesignSpaceDocumentError, DesignSpaceDocument import difflib import os import shutil @@ -285,6 +285,33 @@ class BuildTest(unittest.TestCase): expected_ttx_path = self.get_test_output('BuildMain.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) + def test_varlib_build_from_ds_object(self): + ds_path = self.get_test_input("Build.designspace") + ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") + expected_ttx_path = self.get_test_output("BuildMain.ttx") + + def reload_font(font): + """(De)serialize to get final binary layout.""" + buf = BytesIO() + font.save(buf) + buf.seek(0) + return TTFont(buf) + + ds = DesignSpaceDocument.fromfile(ds_path) + for source in ds.sources: + filename = os.path.join( + ttx_dir, os.path.basename(source.filename).replace(".ufo", ".ttx") + ) + font = TTFont(recalcBBoxes=False, recalcTimestamp=False) + font.importXML(filename) + source.font = reload_font(font) + source.filename = None # Make sure no file path gets into build() + + varfont, _, _ = build(ds) + varfont = reload_font(varfont) + tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] + self.expect_ttx(varfont, expected_ttx_path, tables) + if __name__ == "__main__": sys.exit(unittest.main())