def compileDSToFont(dsPath, ttFolder): doc = DesignSpaceDocument.fromfile(dsPath) doc.findDefault() ufoPathToTTPath = getTTPaths(doc, ttFolder) for source in doc.sources: if source.layerName is None: ttPath = ufoPathToTTPath[source.path] if not os.path.exists(ttPath): raise FileNotFoundError(ttPath) source.font = TTFont(ttPath, lazy=False) assert doc.default.font is not None if "name" not in doc.default.font: doc.default.font["name"] = newTable( "name") # This is the template for the VF, and needs a name table if any(s.layerName is not None for s in doc.sources): fb = FontBuilder(unitsPerEm=doc.default.font["head"].unitsPerEm) fb.setupGlyphOrder(doc.default.font.getGlyphOrder()) fb.setupPost() # This makes sure we store the glyph names font = fb.font for source in doc.sources: if source.font is None: source.font = font try: ttFont, masterModel, _ = varLib.build( doc, exclude=['MVAR', 'HVAR', 'VVAR', 'STAT']) except VarLibError as e: if 'GSUB' in e.args: extraExclude = ['GSUB'] elif 'GPOS' in e.args: extraExclude = ['GPOS', 'GDEF'] else: raise print(f"{e!r}", file=sys.stderr) print( f"Error while building {extraExclude[0]} table, trying again without {' and '.join(extraExclude)}.", file=sys.stderr) ttFont, masterModel, _ = varLib.build( doc, exclude=['MVAR', 'HVAR', 'VVAR', 'STAT'] + extraExclude) # Our client needs the masterModel, so we save a pickle into the font ttFont["MPcl"] = newTable("MPcl") ttFont["MPcl"].data = pickle.dumps(masterModel) return ttFont
def compileDSToFont(dsPath, ttFolder): doc = DesignSpaceDocument.fromfile(dsPath) doc.findDefault() ufoPathToTTPath = getTTPaths(doc, ttFolder) for source in doc.sources: if source.layerName is None: ttPath = ufoPathToTTPath[source.path] if not os.path.exists(ttPath): raise FileNotFoundError(ttPath) source.font = TTFont(ttPath, lazy=False) assert doc.default.font is not None if "name" not in doc.default.font: doc.default.font["name"] = newTable( "name") # This is the template for the VF, and needs a name table if any(s.layerName is not None for s in doc.sources): fb = FontBuilder(unitsPerEm=doc.default.font["head"].unitsPerEm) fb.setupGlyphOrder(doc.default.font.getGlyphOrder()) fb.setupPost() # This makes sure we store the glyph names font = fb.font for source in doc.sources: if source.font is None: source.font = font ttFont, masterModel, _ = varLib.build( doc, exclude=['MVAR', 'HVAR', 'VVAR', 'STAT']) # Our client needs the masterModel, so we save a pickle into the font ttFont["MPcl"] = newTable("MPcl") ttFont["MPcl"].data = pickle.dumps(masterModel) return ttFont
def test_varlib_mutator_ttf(self): suffix = '.ttf' ds_path = self.get_test_input('Build.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace( '.ufo', suffix) varfont, _, _ = build(ds_path, finder) varfont_name = 'Mutator' varfont_path = os.path.join(self.tempdir, varfont_name + suffix) varfont.save(varfont_path) args = [varfont_path, 'wght=500', 'cntr=50'] mutator(args) instfont_path = os.path.splitext( varfont_path)[0] + '-instance' + suffix instfont = TTFont(instfont_path) tables = [ table_tag for table_tag in instfont.keys() if table_tag != 'head' ] expected_ttx_path = self.get_test_output(varfont_name + '.ttx') self.expect_ttx(instfont, expected_ttx_path, tables)
def build_variable_font(self, designspace_path): """Build OpenType variable font from masters in a designspace.""" # TODO: make output filename user configurable outfile = os.path.splitext(designspace_path)[0] + '-Variable.ttf' #outfile = os.path.splitext(os.path.basename(designspace_path))[0] + '-VF' #outfile = self._output_path(outfile, 'ttf', is_variable=True) logger.info('Building variable font ' + outfile) #master_locations, _ = self._designspace_locations(designspace_path) #ufo_paths = list(master_locations.keys()) #ufodir = os.path.dirname(ufo_paths[0]) #assert all(p.startswith(ufodir) for p in ufo_paths) #ttfdir = self._output_dir('ttf', interpolatable=True) #if ufodir: # finder = lambda s: s.replace(ufodir, ttfdir).replace('.ufo', '.ttf') #else: # finder = lambda s: os.path.join(ttfdir, s).replace('.ufo', '.ttf') finder = lambda s: s.replace('.ufo', '.ttf') font, _, _ = varLib.build(designspace_path, finder) font.save(outfile)
def test_varlib_build_BASE(self): self.temp_dir() ds_path = self.get_test_input('TestBASE.designspace', copy=True) ttx_dir = self.get_test_input("master_base_test") expected_ttx_name = 'TestBASE' suffix = '.otf' for path in self.get_file_list(ttx_dir, '.ttx', 'TestBASE'): font, savepath = self.compile_font(path, suffix, 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", suffix) ) ds.updatePaths() varfont, _, _ = build(ds) varfont = reload_font(varfont) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') tables = ["BASE"] self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def test_varlib_mutator_ttf(self): suffix = '.ttf' ds_path = self.get_test_input('Build.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) varfont, _, _ = build(ds_path, finder) varfont_name = 'Mutator' varfont_path = os.path.join(self.tempdir, varfont_name + suffix) varfont.save(varfont_path) args = [varfont_path, 'wght=500', 'cntr=50'] mutator(args) instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix instfont = TTFont(instfont_path) tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head'] expected_ttx_path = self.get_test_output(varfont_name + '.ttx') self.expect_ttx(instfont, expected_ttx_path, tables)
def test_varlib_interpolate_layout_main_ttf(self): """Mostly for testing varLib.interpolate_layout.main() """ suffix = '.ttf' ds_path = self.get_test_input('Build.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable') os.makedirs(ttf_dir) ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-') for path in ttx_paths: self.compile_font(path, suffix, ttf_dir) finder = lambda s: s.replace(ufo_dir, ttf_dir).replace('.ufo', suffix) varfont, _, _ = build(ds_path, finder) varfont_name = 'InterpolateLayoutMain' varfont_path = os.path.join(self.tempdir, varfont_name + suffix) varfont.save(varfont_path) ds_copy = os.path.splitext(varfont_path)[0] + '.designspace' shutil.copy2(ds_path, ds_copy) args = [ds_copy, 'weight=500', 'contrast=50'] interpolate_layout_main(args) instfont_path = os.path.splitext( varfont_path)[0] + '-instance' + suffix instfont = TTFont(instfont_path) tables = [ table_tag for table_tag in instfont.keys() if table_tag != 'head' ] expected_ttx_path = self.get_test_output(varfont_name + '.ttx') self.expect_ttx(instfont, expected_ttx_path, tables)
def _run_varlib_build_test(self, designspace_name, font_name, tables, expected_ttx_name, save_before_dump=False): suffix = '.ttf' ds_path = self.get_test_input(designspace_name + '.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', font_name + '-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace( '.ufo', suffix) varfont, model, _ = build(ds_path, finder) if save_before_dump: # some data (e.g. counts printed in TTX inline comments) is only # calculated at compile time, so before we can compare the TTX # dumps we need to save to a temporary stream, and realod the font varfont = reload_font(varfont) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def run(args=None): if args is None: args = sys.argv[1:] if '-u' in args: print(__usage__) return if '-h' in args: print(__help__) return if len(args) == 2: designSpacePath, varFontPath = args elif len(args) == 1: designSpacePath = args[0] varFontPath = os.path.splitext(designSpacePath)[0] + '.otf' else: print(__usage__) return if os.path.exists(varFontPath): os.remove(varFontPath) varFont, varModel, masterPaths = varLib.build(designSpacePath, otfFinder) blendError = buildCFF2Font(varFontPath, varFont, varModel, masterPaths) if not blendError: print("Built variable font '%s'" % (varFontPath))
def build_variable_font(self, designspace_path, output_path=None, output_dir=None, master_bin_dir=None): """Build OpenType variable font from masters in a designspace.""" assert not (output_path and output_dir), "mutually exclusive args" if output_path is None: output_path = os.path.splitext( os.path.basename(designspace_path))[0] + '-VF' output_path = self._output_path(output_path, 'ttf', is_variable=True, output_dir=output_dir) logger.info('Building variable font ' + output_path) if master_bin_dir is None: master_bin_dir = self._output_dir('ttf', interpolatable=True) finder = partial(_varLib_finder, directory=master_bin_dir) font, _, _ = varLib.build(designspace_path, finder) font.save(output_path)
def designSpaceAndMti2VF(family): dspath, folder = getFile(".designspace", family) designSpace = openDesignSpace(dspath) mti_source = os.path.join(folder, family + ".plist") mti_paths = readThePlist(open(mti_source, "rb")) masters = designSpace.loadSourceFonts(Font) for master in masters: key = master.info.familyName.replace( " ", "") + "-" + master.info.styleName.replace(" ", "") for table, path in mti_paths[key].items(): with open(os.path.join(folder, path), "rb") as mti_: ufo_path = ("com.github.googlei18n.ufo2ft.mtiFeatures/%s.mti" % table.strip()) master.data[ufo_path] = mti_.read() # If we have MTI sources, any Adobe feature files derived from # the Glyphs file should be ignored. We clear it here because # it only contains junk information anyway. master.features.text = "" font, _, _ = varLib.build( compileInterpolatableTTFsFromDS( designSpace, featureCompilerClass=MtiFeatureCompiler)) destination = folder + "/fonts/VAR" if not os.path.exists(destination): os.makedirs(destination) print("\t" + family + " Variable Font generated\n") font.save(os.path.join(destination, family + "-VF.ttf"))
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") 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: filename = os.path.join( self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf")) source.font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False, lazy=True) 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)
def test_varlib_interpolate_layout_main_ttf(self): """Mostly for testing varLib.interpolate_layout.main() """ suffix = '.ttf' ds_path = self.get_test_input('Build.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable') os.makedirs(ttf_dir) ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-') for path in ttx_paths: self.compile_font(path, suffix, ttf_dir) finder = lambda s: s.replace(ufo_dir, ttf_dir).replace('.ufo', suffix) varfont, _, _ = build(ds_path, finder) varfont_name = 'InterpolateLayoutMain' varfont_path = os.path.join(self.tempdir, varfont_name + suffix) varfont.save(varfont_path) ds_copy = os.path.splitext(varfont_path)[0] + '.designspace' shutil.copy2(ds_path, ds_copy) args = [ds_copy, 'weight=500', 'contrast=50'] interpolate_layout_main(args) instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix instfont = TTFont(instfont_path) tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head'] expected_ttx_path = self.get_test_output(varfont_name + '.ttx') self.expect_ttx(instfont, expected_ttx_path, tables)
def _run_varlib_build_test(self, designspace_name, font_name, tables, expected_ttx_name, save_before_dump=False): suffix = '.ttf' ds_path = self.get_test_input(designspace_name + '.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', font_name + '-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) varfont, model, _ = build(ds_path, finder) if save_before_dump: # some data (e.g. counts printed in TTX inline comments) is only # calculated at compile time, so before we can compare the TTX # dumps we need to save to a temporary stream, and realod the font buf = BytesIO() varfont.save(buf) buf.seek(0) varfont = TTFont(buf) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def test_varlib_build_sparse_masters(self): ds_path = self.get_test_input("SparseMasters.designspace") expected_ttx_path = self.get_test_output("SparseMasters.ttx") varfont, _, _ = build(ds_path) 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 main(args=None): options = get_options(args) if os.path.exists(options.var_font_path): os.remove(options.var_font_path) designspace = DesignSpaceDocument.fromfile(options.design_space_path) ds_data = varLib.load_designspace(designspace) master_fonts = varLib.load_masters(designspace, otfFinder) logger.progress("Reading source fonts...") for i, master_font in enumerate(master_fonts): designspace.sources[i].font = master_font # Subset source fonts if options.include_glyphs_path: logger.progress("Subsetting source fonts...") subsetDict = getSubset(options.include_glyphs_path) subset_masters(designspace, subsetDict) if options.check_compatibility: logger.progress("Checking outline compatibility in source fonts...") font_list = [src.font for src in designspace.sources] default_font = designspace.sources[ds_data.base_idx].font vf = deepcopy(default_font) # We copy vf from default_font, because we use VF to hold # merged arguments from each source font charstring - this alters # the font, which we don't want to do to the default font. do_compatibility(vf, font_list, ds_data.base_idx) logger.progress("Building variable OTF (CFF2) font...") # Note that we now pass in the design space object, rather than a path to # the design space file, in order to pass in the modified source fonts # fonts without having to recompile and save them. try: varFont, _, _ = varLib.build(designspace, otfFinder) except VarLibCFFPointTypeMergeError: logger.error("The input set requires compatibilization. Please try " "again with the -c (--check-compat) option.") return 0 if not options.keep_glyph_names: suppress_glyph_names(varFont) if options.omit_mac_names: remove_mac_names(varFont) stat_file_path = os.path.join(os.path.dirname(options.var_font_path), STAT_FILENAME) if os.path.exists(stat_file_path): logger.progress("Importing STAT table override...") import_stat_override(varFont, stat_file_path) validate_stat_axes(varFont) validate_stat_values(varFont) update_stat_name_ids(varFont) varFont.save(options.var_font_path) logger.progress(f"Built variable font '{options.var_font_path}'")
def compileVariableTTF( designSpaceDoc, preProcessorClass=TTFInterpolatablePreProcessor, outlineCompilerClass=OutlineTTFCompiler, featureCompilerClass=None, featureWriters=None, glyphOrder=None, useProductionNames=None, cubicConversionError=None, reverseDirection=True, excludeVariationTables=(), optimizeGvar=True, flattenComponents=False, inplace=False, debugFeatureFile=None, notdefGlyph=None, ): """Create FontTools TrueType variable font from the DesignSpaceDocument UFO sources with interpolatable outlines, using fontTools.varLib.build. *optimizeGvar*, if set to False, will not perform IUP optimization on the generated 'gvar' table. *excludeVariationTables* is a list of sfnt table tags (str) that is passed on to fontTools.varLib.build, to skip building some variation tables. The rest of the arguments works the same as in the other compile functions. Returns a new variable TTFont object. """ baseUfo = getDefaultMasterFont(designSpaceDoc) ttfDesignSpace = compileInterpolatableTTFsFromDS( designSpaceDoc, preProcessorClass=preProcessorClass, outlineCompilerClass=outlineCompilerClass, featureCompilerClass=featureCompilerClass, featureWriters=featureWriters, glyphOrder=glyphOrder, useProductionNames=False, # will rename glyphs after varfont is built cubicConversionError=cubicConversionError, reverseDirection=reverseDirection, flattenComponents=flattenComponents, inplace=inplace, debugFeatureFile=debugFeatureFile, notdefGlyph=notdefGlyph, ) logger.info("Building variable TTF font") varfont = varLib.build(ttfDesignSpace, exclude=excludeVariationTables, optimize=optimizeGvar)[0] postProcessor = PostProcessor(varfont, baseUfo) varfont = postProcessor.process(useProductionNames) return varfont
def test_varlib_build_VVAR_CFF2(self): ds_path = self.get_test_input('TestVVAR.designspace') suffix = '.otf' expected_ttx_name = 'TestVVAR' tables = ["VVAR"] finder = lambda s: s.replace('.ufo', suffix) varfont, model, _ = build(ds_path, finder) varfont = reload_font(varfont) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def designSpace2Var(self): ds = self.designSpace family = os.path.basename(self.familyPath) print("\n>>> Load the {} designspace".format(family)) print(" Load " + family + " files") ds.loadSourceFonts(Font) print(" Start to build Variable Tables") feature_Writers = [KernFeatureWriter(mode="append"), MarkFeatureWriter] font, _, _ = varLib.build(compileInterpolatableTTFsFromDS( ds, featureWriters=feature_Writers), optimize=False) font.save(os.path.join(self.destination, family + "-VF.ttf")) print(" " + family + " Variable Font generated\n")
def test_varlib_build_CFF2(self): ds_path = self.get_test_input('TestCFF2.designspace') suffix = '.otf' expected_ttx_name = 'BuildTestCFF2' tables = ["fvar", "CFF2"] finder = lambda s: s.replace('.ufo', suffix) varfont, model, _ = build(ds_path, finder) # some data (e.g. counts printed in TTX inline comments) is only # calculated at compile time, so before we can compare the TTX # dumps we need to save to a temporary stream, and realod the font varfont = reload_font(varfont) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
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 compileVariableCFF2( designSpaceDoc, preProcessorClass=OTFPreProcessor, outlineCompilerClass=OutlineOTFCompiler, featureCompilerClass=None, featureWriters=None, glyphOrder=None, useProductionNames=None, roundTolerance=None, excludeVariationTables=(), inplace=False, debugFeatureFile=None, ): """Create FontTools CFF2 variable font from the DesignSpaceDocument UFO sources with interpolatable outlines, using fontTools.varLib.build. *excludeVariationTables* is a list of sfnt table tags (str) that is passed on to fontTools.varLib.build, to skip building some variation tables. The rest of the arguments works the same as in the other compile functions. Returns a new variable TTFont object. """ baseUfo = getDefaultMasterFont(designSpaceDoc) otfDesignSpace = compileInterpolatableOTFsFromDS( designSpaceDoc, preProcessorClass=preProcessorClass, outlineCompilerClass=outlineCompilerClass, featureCompilerClass=featureCompilerClass, featureWriters=featureWriters, glyphOrder=glyphOrder, useProductionNames=False, # will rename glyphs after varfont is built roundTolerance=roundTolerance, inplace=inplace, debugFeatureFile=debugFeatureFile, ) logger.info("Building variable CFF2 font") varfont = varLib.build(otfDesignSpace, exclude=excludeVariationTables)[0] postProcessor = PostProcessor(varfont, baseUfo) varfont = postProcessor.process(useProductionNames) return varfont
def makeVarFont(self): (minimum, maximum), tag2name = self.findRegularBoldDefaultLocation() # check if defautl exist otherTags = [ a.tag for a in self.designSpaceDocument.axes if a.tag != "wght" ] print("\tLoad {}.designspace".format(self.familyName)) self.designSpaceDocument.loadSourceFonts(defcon.Font) vfont, _, _ = varLib.build(compileInterpolatableTTFsFromDS( self.designSpaceDocument, featureWriters=[ KernFeatureWriter(mode="append"), MarkFeatureWriter() ]), optimize=False) fullfontsFolder = os.path.join(self.dirpath, "fonts/VAR") if not os.path.exists(fullfontsFolder): os.makedirs(fullfontsFolder) path = os.path.join(fullfontsFolder, self.familyName + "-VF.ttf") vfont.save(path) vfont = TTFont(path) tags = {"wght": (minimum, maximum)} if len(otherTags) == 0: slimft = instancer.instantiateVariableFont(vfont, tags) else: for t in otherTags: tags[t] = None slimft = instancer.instantiateVariableFont(vfont, tags) for namerecord in slimft['name'].names: namerecord.string = namerecord.toUnicode() if namerecord.nameID == 3: unicID = namerecord.string.split(";") newID = "4.444" + ";" + unicID[1] + ";" + unicID[2] print("\tTagging the font as a 'slim' one:", newID) namerecord.string = newID if namerecord.nameID == 5: namerecord.string = "Version 4.444" print("\tSaving " + self.familyName + "Slim-VF.ttf\n") slimFontFolder = os.path.join(fullfontsFolder + "/SlimVF") if not os.path.exists(slimFontFolder): os.makedirs(slimFontFolder) slimft.save( os.path.join(slimFontFolder, "%sSlim-VF.ttf" % self.familyName))
def designSpace2Var(family): print(">>> Load the {} designspace".format(family)) path, folder = getFile(".designspace", family) designSpace = openDesignSpace(path) print(" Load " + family + " files") designSpace.loadSourceFonts(Font) print(" Start to build Variable Tables") feature_Writers = [KernFeatureWriter(mode="append"), MarkFeatureWriter] font, _, _ = varLib.build(compileInterpolatableTTFsFromDS( designSpace, featureWriters=feature_Writers), optimize=False) destination = folder + "/fonts/VAR" if not os.path.exists(destination): os.makedirs(destination) font.save(os.path.join(destination, family + "-VF.ttf")) print(" " + family + " Variable Font generated\n")
def _run_varlib_build_test(self, designspace_name, font_name, tables, expected_ttx_name): suffix = '.ttf' ds_path = self.get_test_input(designspace_name + '.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', font_name + '-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) varfont, model, _ = build(ds_path, finder) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def test_varlib_build_ttf(self): """Designspace file contains <axes> element.""" suffix = '.ttf' ds_path = self.get_test_input('Build.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix) varfont, model, _ = build(ds_path, finder) tables = ['GDEF', 'HVAR', 'fvar', 'gvar'] expected_ttx_path = self.get_test_output('Build.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def _run_varlib_build_test(self, designspace_name, font_name, tables, expected_ttx_name): suffix = '.ttf' ds_path = self.get_test_input(designspace_name + '.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', font_name + '-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace( '.ufo', suffix) varfont, model, _ = build(ds_path, finder) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def build_variable_font(self, designspace_path): """Build OpenType variable font from masters in a designspace.""" outfile = os.path.splitext(designspace_path)[0] + '-GX.ttf' logger.info('Building variable font ' + outfile) master_locations, _ = self._designspace_locations(designspace_path) ufo_paths = master_locations.keys() ufodir = os.path.dirname(ufo_paths[0]) assert all(p.startswith(ufodir) for p in ufo_paths) ttfdir = self._output_dir('ttf', interpolatable=True) if ufodir: finder = lambda s: s.replace(ufodir, ttfdir).replace( '.ufo', '.ttf') else: finder = lambda s: os.path.join(ttfdir, s).replace('.ufo', '.ttf') font, _, _ = varLib.build(designspace_path, finder) font.save(outfile)
def makeVarFont(self, mti=False): self.designSpaceDocument.loadSourceFonts(Font) print("\tStart to build Variable Tables") self.vfont, _, _ = varLib.build(compileInterpolatableTTFsFromDS( self.designSpaceDocument, featureCompilerClass=MtiFeatureCompiler, featureWriters=None), optimize=False) if self.makeUIVersion is False: path = os.path.join(self.destination, self.familyName + "-VF.ttf") print(path) self.vfont.save(path) print("\t" + self.familyName + " Variable Font generated\n") else: path = os.path.join(self.destination, self.familyName + "UI-VF.ttf") vfontUI = self.renamer_() vfontUI.save(path) print("\t" + self.familyName + "UI Variable Font generated\n")
def test_varlib_build3_ttf(self): """Designspace file does not contain an <axes> element.""" suffix = '.ttf' ds_path = self.get_test_input('InterpolateLayout3.designspace') ufo_dir = self.get_test_input('master_ufo') ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf') self.temp_dir() ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily2-') for path in ttx_paths: self.compile_font(path, suffix, self.tempdir) finder = lambda s: s.replace(ufo_dir, self.tempdir).replace( '.ufo', suffix) varfont, model, _ = build(ds_path, finder) tables = ['GDEF', 'HVAR', 'MVAR', 'fvar', 'gvar'] expected_ttx_path = self.get_test_output('Build3.ttx') self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def test_varlib_build_lazy_masters(self): # See https://github.com/fonttools/fonttools/issues/1808 ds_path = self.get_test_input("SparseMasters.designspace") expected_ttx_path = self.get_test_output("SparseMasters.ttx") def _open_font(master_path, master_finder=lambda s: s): font = TTFont() font.importXML(master_path) buf = BytesIO() font.save(buf, reorderTables=False) buf.seek(0) font = TTFont(buf, lazy=True) # reopen in lazy mode, to reproduce #1808 return font ds = DesignSpaceDocument.fromfile(ds_path) ds.loadSourceFonts(_open_font) 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_ttf_paths(self): self.temp_dir() ds_path = self.get_test_input("Build.designspace", copy=True) ttx_dir = self.get_test_input("master_ttx_interpolatable_ttf") expected_ttx_path = self.get_test_output("BuildMain.ttx") 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_sparse_CFF2(self): ds_path = self.get_test_input('TestSparseCFF2VF.designspace') ttx_dir = self.get_test_input("master_sparse_cff2") expected_ttx_path = self.get_test_output("TestSparseCFF2VF.ttx") self.temp_dir() for path in self.get_file_list(ttx_dir, '.ttx', 'MasterSet_Kanji-'): self.compile_font(path, ".otf", 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", ".otf")) ds.updatePaths() varfont, _, _ = build(ds) varfont = reload_font(varfont) tables = ["fvar", "CFF2"] self.expect_ttx(varfont, expected_ttx_path, tables)
def test_varlib_build_sparse_CFF2(self): ds_path = self.get_test_input('TestSparseCFF2VF.designspace') ttx_dir = self.get_test_input("master_sparse_cff2") expected_ttx_path = self.get_test_output("TestSparseCFF2VF.ttx") self.temp_dir() for path in self.get_file_list(ttx_dir, '.ttx', 'MasterSet_Kanji-'): self.compile_font(path, ".otf", 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", ".otf") ) ds.updatePaths() varfont, _, _ = build(ds) varfont = reload_font(varfont) tables = ["fvar", "CFF2"] self.expect_ttx(varfont, expected_ttx_path, tables)
def test_varlib_build_vpal(self): ds_path = self.get_test_input('test_vpal.designspace') ttx_dir = self.get_test_input("master_vpal_test") expected_ttx_path = self.get_test_output("test_vpal.ttx") self.temp_dir() for path in self.get_file_list(ttx_dir, '.ttx', 'master_vpal_test_'): self.compile_font(path, ".otf", 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", ".otf")) ds.updatePaths() varfont, _, _ = build(ds) varfont = reload_font(varfont) tables = ["GPOS"] self.expect_ttx(varfont, expected_ttx_path, tables)
def test_varlib_build_CFF2_from_CFF2(self): self.temp_dir() ds_path = self.get_test_input('TestCFF2Input.designspace', copy=True) ttx_dir = self.get_test_input("master_cff2_input") expected_ttx_path = self.get_test_output("BuildTestCFF2.ttx") for path in self.get_file_list(ttx_dir, '.ttx', 'TestCFF2_'): self.compile_font(path, ".otf", 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", ".otf") ) ds.updatePaths() varfont, _, _ = build(ds) varfont = reload_font(varfont) tables = ["fvar", "CFF2"] self.expect_ttx(varfont, expected_ttx_path, tables)
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") 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: filename = os.path.join( self.tempdir, os.path.basename(source.filename).replace(".ufo", ".ttf") ) source.font = TTFont( filename, recalcBBoxes=False, recalcTimestamp=False, lazy=True ) 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)
def run(args=None): post_format_3 = False if not args: args = sys.argv[1:] if '-u' in args: print(__usage__) return if '-h' in args: print(__help__) return if '-p' in args: post_format_3 = True args.remove('-p') if parse_version(fontToolsVersion) < parse_version("3.19"): print("Quitting. The Python fonttools module must be at least 3.19.0 " "in order for buildcff2vf to work.") return if len(args) == 2: designSpacePath, varFontPath = args elif len(args) == 1: designSpacePath = args[0] varFontPath = os.path.splitext(designSpacePath)[0] + '.otf' else: print(__usage__) return if os.path.exists(varFontPath): os.remove(varFontPath) varFont, varModel, masterPaths = varLib.build(designSpacePath, otfFinder, exclude=("CFF2",)) blendError = buildCFF2Font(varFontPath, varFont, varModel, masterPaths, post_format_3) if not blendError: print("Built variable font '%s'" % (varFontPath))
def test_varlib_build_VVAR_CFF2(self): ds_path = self.get_test_input('TestVVAR.designspace') ttx_dir = self.get_test_input("master_vvar_cff2") expected_ttx_name = 'TestVVAR' suffix = '.otf' self.temp_dir() for path in self.get_file_list(ttx_dir, '.ttx', 'TestVVAR'): font, savepath = self.compile_font(path, suffix, 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", suffix) ) ds.updatePaths() varfont, _, _ = build(ds) varfont = reload_font(varfont) expected_ttx_path = self.get_test_output(expected_ttx_name + '.ttx') tables = ["VVAR"] self.expect_ttx(varfont, expected_ttx_path, tables) self.check_ttx_dump(varfont, expected_ttx_path, tables, suffix)
def test_varlib_build_sparse_masters_MVAR(self): import fontTools.varLib.mvar ds_path = self.get_test_input("SparseMasters.designspace") ds = DesignSpaceDocument.fromfile(ds_path) load_masters(ds) # Trigger MVAR generation so varLib is forced to create deltas with a # sparse master inbetween. font_0_os2 = ds.sources[0].font["OS/2"] font_0_os2.sTypoAscender = 1 font_0_os2.sTypoDescender = 1 font_0_os2.sTypoLineGap = 1 font_0_os2.usWinAscent = 1 font_0_os2.usWinDescent = 1 font_0_os2.sxHeight = 1 font_0_os2.sCapHeight = 1 font_0_os2.ySubscriptXSize = 1 font_0_os2.ySubscriptYSize = 1 font_0_os2.ySubscriptXOffset = 1 font_0_os2.ySubscriptYOffset = 1 font_0_os2.ySuperscriptXSize = 1 font_0_os2.ySuperscriptYSize = 1 font_0_os2.ySuperscriptXOffset = 1 font_0_os2.ySuperscriptYOffset = 1 font_0_os2.yStrikeoutSize = 1 font_0_os2.yStrikeoutPosition = 1 font_0_vhea = newTable("vhea") font_0_vhea.ascent = 1 font_0_vhea.descent = 1 font_0_vhea.lineGap = 1 font_0_vhea.caretSlopeRise = 1 font_0_vhea.caretSlopeRun = 1 font_0_vhea.caretOffset = 1 ds.sources[0].font["vhea"] = font_0_vhea font_0_hhea = ds.sources[0].font["hhea"] font_0_hhea.caretSlopeRise = 1 font_0_hhea.caretSlopeRun = 1 font_0_hhea.caretOffset = 1 font_0_post = ds.sources[0].font["post"] font_0_post.underlineThickness = 1 font_0_post.underlinePosition = 1 font_2_os2 = ds.sources[2].font["OS/2"] font_2_os2.sTypoAscender = 800 font_2_os2.sTypoDescender = 800 font_2_os2.sTypoLineGap = 800 font_2_os2.usWinAscent = 800 font_2_os2.usWinDescent = 800 font_2_os2.sxHeight = 800 font_2_os2.sCapHeight = 800 font_2_os2.ySubscriptXSize = 800 font_2_os2.ySubscriptYSize = 800 font_2_os2.ySubscriptXOffset = 800 font_2_os2.ySubscriptYOffset = 800 font_2_os2.ySuperscriptXSize = 800 font_2_os2.ySuperscriptYSize = 800 font_2_os2.ySuperscriptXOffset = 800 font_2_os2.ySuperscriptYOffset = 800 font_2_os2.yStrikeoutSize = 800 font_2_os2.yStrikeoutPosition = 800 font_2_vhea = newTable("vhea") font_2_vhea.ascent = 800 font_2_vhea.descent = 800 font_2_vhea.lineGap = 800 font_2_vhea.caretSlopeRise = 800 font_2_vhea.caretSlopeRun = 800 font_2_vhea.caretOffset = 800 ds.sources[2].font["vhea"] = font_2_vhea font_2_hhea = ds.sources[2].font["hhea"] font_2_hhea.caretSlopeRise = 800 font_2_hhea.caretSlopeRun = 800 font_2_hhea.caretOffset = 800 font_2_post = ds.sources[2].font["post"] font_2_post.underlineThickness = 800 font_2_post.underlinePosition = 800 varfont, _, _ = build(ds) mvar_tags = [vr.ValueTag for vr in varfont["MVAR"].table.ValueRecord] assert all(tag in mvar_tags for tag in fontTools.varLib.mvar.MVAR_ENTRIES)
def _generateVariationFont(self, outPutPath): """ Generate a variation font. """ dirname = os.path.dirname(outPutPath) # fontCompiler settings options = FontCompilerOptions() options.saveFDKPartsNextToUFO = getDefault("saveFDKPartsNextToUFO") options.shouldDecomposeWithCheckOutlines = False options.generateCheckComponentMatrix = True options.defaultDrawingSegmentType = "qcurve" options.format = "ttf" options.decompose = False options.checkOutlines = False options.autohint = self.compileSettingAutohint options.releaseMode = self.compileSettingReleaseMode options.glyphOrder = self.compileGlyphOrder options.useMacRoman = False options.fdk = CurrentFDK() options.generateFeaturesWithFontTools = False self.generateReport.newLine() self.generateReport.writeTitle("Generate TTF", "'") self.generateReport.indent() # map all master ufo paths to generated binaries masterBinaryPaths = VarLibMasterFinder() masterCount = 0 for sourceDescriptor in self.sources: master = self.masters[sourceDescriptor.name] # get the output path outputPath = os.path.join(dirname, "temp_%02d_%s-%s-%s.ttf" % (masterCount, master.font.info.familyName, master.font.info.styleName, master.name)) masterBinaryPaths[sourceDescriptor.path] = outputPath self._generatedFiles.add(outputPath) masterCount += 1 # set the output path options.outputPath = outputPath options.layerName = master.name try: # generate the font result = generateFont(master.font, options=options) if getDefault("Batch.Debug", False): tempSavePath = os.path.join(dirname, "temp_%s-%s-%s.ufo" % (master.font.info.familyName, master.font.info.styleName, master.name)) font = master.font font.save(tempSavePath) if font.layers.defaultLayer.name != master.name: tempFont = defcon.Font(tempSavePath) tempFont.layers.defaultLayer = tempFont.layers[master.name] tempFont.save() self._generatedFiles.add(tempSavePath) except Exception: import traceback result = traceback.format_exc() print(result) self.generateReport.newLine() self.generateReport.write("Generate %s %s (%s)" % (master.font.info.familyName, master.font.info.styleName, master.name)) self.generateReport.indent() self.generateReport.write(result) self.generateReport.dedent() self.generateReport.dedent() # optimize the design space for varlib designSpacePath = os.path.join(os.path.dirname(self.path), "temp_%s" % os.path.basename(self.path)) self.write(designSpacePath) self._generatedFiles.add(designSpacePath) try: # let varLib build the variation font varFont, _, _ = varLib.build(designSpacePath, master_finder=masterBinaryPaths) # save the variation font varFont.save(outPutPath) except Exception: import traceback result = traceback.format_exc() print(result)
def test_varlib_build_no_axes_ttf(self): """Designspace file does not contain an <axes> element.""" ds_path = self.get_test_input('InterpolateLayout3.designspace') with self.assertRaisesRegex(DesignSpaceDocumentError, "No axes defined"): build(ds_path)