diff --git a/Lib/fontTools/varLib/__init__.py b/Lib/fontTools/varLib/__init__.py index 884c8550c..3efb2548c 100644 --- a/Lib/fontTools/varLib/__init__.py +++ b/Lib/fontTools/varLib/__init__.py @@ -811,14 +811,36 @@ def build(designspace, master_finder=lambda s:s, exclude=[], optimize=True): return vf, model, master_ttfs +def _open_font(path, master_finder): + # load TTFont masters from given 'path': this can be either a .TTX or an + # OpenType binary font; or if neither of these, try use the 'master_finder' + # callable to resolve the path to a valid .TTX or OpenType font binary. + from fontTools.ttx import guessFileType + + master_path = os.path.normpath(path) + tp = guessFileType(master_path) + if tp is None: + # not an OpenType binary/ttx, fall back to the master finder. + master_path = master_finder(master_path) + tp = guessFileType(master_path) + if tp in ("TTX", "OTX"): + font = TTFont() + font.importXML(master_path) + elif tp in ("TTF", "OTF", "WOFF", "WOFF2"): + font = TTFont(master_path) + else: + raise VarLibError("Invalid master path: %r" % master_path) + return font + + def load_masters(designspace, master_finder=lambda s: s): """Ensure that all SourceDescriptor.font attributes have an appropriate TTFont object loaded, or else open TTFont objects from the SourceDescriptor.path attributes. - The paths can point to either an OpenType font or to a UFO. In the latter case, - use the provided master_finder callable to map from UFO paths to the respective - master font binaries (e.g. .ttf or .otf). + The paths can point to either an OpenType font, a TTX file, or a UFO. In the + latter case, use the provided master_finder callable to map from UFO paths to + the respective master font binaries (e.g. .ttf, .otf or .ttx). Return list of master TTFont objects in the same order they are listed in the DesignSpaceDocument. @@ -845,15 +867,10 @@ def load_masters(designspace, master_finder=lambda s: s): "Designspace source '%s' has neither 'font' nor 'path' " "attributes" % (master.name or "") ) - # 2. A SourceDescriptor's path might point to a UFO or an OpenType - # binary. Find out the hard way. - master_path = os.path.normpath(master.path) - try: - font = TTFont(master_path) - except (IOError, TTLibError): - # 3. Not an OpenType binary, fall back to the master finder. - master_path = master_finder(master_path) - font = TTFont(master_path) + # 2. A SourceDescriptor's path might point an OpenType binary, a + # TTX file, or another source file (e.g. UFO), in which case we + # resolve the path using 'master_finder' function + font = _open_font(master.path, master_finder) master_fonts.append(font) return master_fonts diff --git a/Tests/varLib/varLib_test.py b/Tests/varLib/varLib_test.py index 6638ea363..16747fd7a 100644 --- a/Tests/varLib/varLib_test.py +++ b/Tests/varLib/varLib_test.py @@ -290,7 +290,7 @@ 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): + def test_varlib_build_from_ds_object_in_memory_ttfonts(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") @@ -314,6 +314,44 @@ class BuildTest(unittest.TestCase): tables = [table_tag for table_tag in varfont.keys() if table_tag != "head"] self.expect_ttx(varfont, expected_ttx_path, tables) + def test_varlib_build_from_ttf_paths(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") + + self.temp_dir() + for path in self.get_file_list(ttx_dir, '.ttx', 'TestFamily-'): + self.compile_font(path, ".ttf", self.tempdir) + + ds = DesignSpaceDocument.fromfile(ds_path) + for source in ds.sources: + source.path = os.path.join( + self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf") + ) + ds.updatePaths() + + 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) + + def test_varlib_build_from_ttx_paths(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") + + ds = DesignSpaceDocument.fromfile(ds_path) + for source in ds.sources: + source.path = os.path.join( + ttx_dir, os.path.basename(source.filename).replace(".ufo", ".ttx") + ) + ds.updatePaths() + + 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) + def test_load_masters_layerName_without_required_font(): ds = DesignSpaceDocument()