def test_debugFeatureFile(self, designspace): tmp = io.StringIO() varfont = compileVariableTTF(designspace, debugFeatureFile=tmp) assert "### LayerFont-Regular ###" in tmp.getvalue() assert "### LayerFont-Bold ###" in tmp.getvalue()
def compile_variable_and_save( designspace: fontTools.designspaceLib.DesignSpaceDocument, vtt_compile: bool = True, ) -> None: familyName = designspace.default.font.info.familyName file_stem = familyName.replace(" ", "") file_path: Path = (OUTPUT_TTF_DIR / file_stem).with_suffix(".ttf") print(f"[{familyName}] Compiling") varFont = ufo2ft.compileVariableTTF(designspace, inplace=True) print(f"[{familyName}] Adding STAT table") styleSpace = statmake.classes.Stylespace.from_file(INPUT_DIR / "STAT.plist") statmake.lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) print(f"[{familyName}] Merging VTT") vttLib.transfer.merge_from_file(varFont, VTT_DATA_FILE) if vtt_compile: print(f"[{familyName}] Compiling VTT") vttLib.compile_instructions(varFont, ship=True) set_overlap_flag(varFont) # last minute manual corrections to set things correctly manualHacks(varFont) print(f"[{familyName}] Saving") file_path.parent.mkdir(exist_ok=True, parents=True) varFont.save(file_path) print(f"[{familyName}] Done: {file_path}")
def build_variable_fonts(designspace, *steps): for font in [source.font for source in designspace.sources]: for step in steps: step(font) set_font_metaData(font, "var") familyName = designspace.default.font.info.familyName file_stem = familyName.replace(" ", "") file_path = (OUTPUT_DIR / file_stem).with_suffix(f".ttf") print(f"[{familyName}] Compiling") varFont = ufo2ft.compileVariableTTF(designspace) print(f"[{familyName}] Adding STAT table") styleSpace = classes.Stylespace.from_file(INPUT_DIR / "STAT.plist") lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) print(f"[{familyName}] Merging VTT") vttLib.transfer.merge_from_file(varFont, VTT_DATA_FILE) print(f"[{familyName}] Saving") varFont.save(file_path) print(f"[{familyName}] Done: {file_path}")
def build_variable_font( self, designspace, output_path=None, output_dir=None, ttf=True, optimize_gvar=True, optimize_cff=CFFOptimization.SPECIALIZE, use_production_names=None, reverse_direction=True, conversion_error=None, feature_writers=None, cff_round_tolerance=None, debug_feature_file=None, flatten_components=False, filters=None, **kwargs, ): """Build OpenType variable font from masters in a designspace.""" assert not (output_path and output_dir), "mutually exclusive args" designspace = self._load_designspace_sources(designspace) if output_path is None: output_path = ( os.path.splitext(os.path.basename(designspace.path))[0] + "-VF" ) ext = "ttf" if ttf else "otf" output_path = self._output_path( output_path, ext, is_variable=True, output_dir=output_dir ) logger.info("Building variable font " + output_path) if ttf: font = ufo2ft.compileVariableTTF( designspace, featureWriters=feature_writers, useProductionNames=use_production_names, cubicConversionError=conversion_error, reverseDirection=reverse_direction, optimizeGvar=optimize_gvar, flattenComponents=flatten_components, debugFeatureFile=debug_feature_file, filters=filters, inplace=True, ) else: font = ufo2ft.compileVariableCFF2( designspace, featureWriters=feature_writers, useProductionNames=use_production_names, roundTolerance=cff_round_tolerance, debugFeatureFile=debug_feature_file, optimizeCFF=optimize_cff, filters=filters, inplace=True, ) font.save(output_path)
def test_compileVariableTTF(self, designspace, useProductionNames): varfont = compileVariableTTF(designspace, useProductionNames=useProductionNames) expectTTX( varfont, "TestVariableFont-TTF{}.ttx".format( "-useProductionNames" if useProductionNames else ""), )
def build_variable_fonts(designspace, *steps): sourceFonts = [ ufoLib2.Font.open(INPUT_DIR / designspace.sources[0].filename), ufoLib2.Font.open(INPUT_DIR / designspace.sources[1].filename), ufoLib2.Font.open(INPUT_DIR / designspace.sources[2].filename) ] for source in sourceFonts: set_font_metaData(source, "var") designspace.sources[0].font = sourceFonts[0] #ExtraLight designspace.sources[1].font = sourceFonts[1] #Regular designspace.sources[2].font = sourceFonts[2] #Bold for font in sourceFonts: for step in steps: step(font) familyName = sourceFonts[1].info.familyName file_stem = sourceFonts[1].info.familyName.replace(" ", "") file_path = (OUTPUT_DIR / file_stem).with_suffix(f".ttf") print(f"[{familyName}] Compiling") varFont = ufo2ft.compileVariableTTF(designspace) print(f"[{familyName}] Adding STAT table") styleSpace = classes.Stylespace.from_file(INPUT_DIR / "STAT.plist") lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) print(f"[{familyName}] Merging VTT") vttLib.transfer.merge_from_file(varFont, VTT_DATA_FILE) print(f"[{familyName}] Saving") varFont.save(file_path) print(f"[{familyName}] Done: {file_path}") print(f"[{familyName}] Compiling CFF2") file_path_cff2 = (OUTPUT_DIR / file_stem).with_suffix(f".otf") #Do not optimize, because we have to do it again after autohinting. varFontCFF2 = ufo2ft.compileVariableCFF2( designspace, inplace=True, useProductionNames=True, optimizeCFF=ufo2ft.CFFOptimization.NONE, ) print(f"[{familyName}] Adding STAT table") styleSpace = classes.Stylespace.from_file(INPUT_DIR / "STAT.plist") lib.apply_stylespace_to_variable_font(styleSpace, varFontCFF2, {}) print(f"[{familyName}] Saving") varFontCFF2.save(file_path_cff2) print(f"[{familyName}] Done: {file_path_cff2}")
def compile_variable_and_save( designspace: fontTools.designspaceLib.DesignSpaceDocument, vtt_compile: bool = True, ) -> None: if "Italic" in designspace.default.font.info.familyName: #Some weird stuff happens with Italics designspace.default.font.info.familyName = designspace.default.font.info.familyName.replace( " Italic", "") familyName = designspace.default.font.info.familyName styleName = designspace.default.font.info.styleName file_stem = familyName.replace(" ", "") if "Italic" in styleName and "Italic" not in file_stem: file_stem = file_stem + "Italic" file_path: Path = (OUTPUT_TTF_DIR / file_stem).with_suffix(".ttf") print(f"[{familyName} {styleName}] Compiling") varFont = ufo2ft.compileVariableTTF(designspace, inplace=True) print(f"[{familyName} {styleName}] Merging VTT") if "Italic" in styleName: font_vtt = fontTools.ttLib.TTFont(ITALIC_VTT_DATA_FILE) else: font_vtt = fontTools.ttLib.TTFont(VTT_DATA_FILE) for table in ["TSI0", "TSI1", "TSI2", "TSI3", "TSI5", "TSIC", "maxp"]: varFont[table] = fontTools.ttLib.newTable(table) varFont[table] = font_vtt[table] # this will correct the OFFSET[R] commands in TSI1 if font_vtt.getGlyphOrder() != varFont.getGlyphOrder(): tsi1.fixOFFSET(varFont, font_vtt) pass if vtt_compile: print(f"[{familyName} {styleName}] Compiling VTT") vttLib.compile_instructions(varFont, ship=True) else: file_path = (OUTPUT_TTF_DIR / str(file_stem + "_VTT")).with_suffix(".ttf") # last minute manual corrections to set things correctly # set two flags to enable proper rendering (one for overlaps in Mac, the other for windows hinting) # Helping mac office generage the postscript name correctly for variable fonts when an italic is present set_overlap_flag(varFont) varFont["head"].flags = 0x000b if "Regular" in styleName: varFont["name"].setName( familyName.replace(" ", "") + "Roman", 25, 3, 1, 1033) print(f"[{familyName} {styleName}] Saving") file_path.parent.mkdir(exist_ok=True, parents=True) varFont.save(file_path) print(f"[{familyName}] Done: {file_path}")
def export(): master.showInterface = false for w in range(8): for t in range(10): for r in range(5): generateSource(f"masterW{w}T{t}R{r}", xHeight, ascender, (w+1)*100, t*5, 1/r) varFont = ufo2ft.compileVariableTTF(doc) varFont.save(f"export/{familyName} variable {datetime.now()}.ttf") print(f"Done exporting {familyName} variable {datetime.now()}.ttf")
def main(argv): ufos = tuple(a for a in argv[1:] if a.endswith(".ufo")) config_file = None if FLAGS.config_file: config_file = Path(FLAGS.config_file) font_config = config.load(config_file) designspace = designspaceLib.DesignSpaceDocument() import pprint pp = pprint.PrettyPrinter() # define axes names, tags and min/default/max axis_defs = [ dict( tag=a.axisTag, name=a.name, minimum=min( p.position for m in font_config.masters for p in m.position if p.axisTag == a.axisTag ), default=a.default, maximum=max( p.position for m in font_config.masters for p in m.position if p.axisTag == a.axisTag ), ) for a in font_config.axes ] logging.info(pp.pformat(axis_defs)) for axis_def in axis_defs: designspace.addAxisDescriptor(**axis_def) axis_names = {a.axisTag: a.name for a in font_config.axes} for master in font_config.masters: ufo = ufoLib2.Font.open(master.output_ufo) ufo.info.styleName = master.style_name location = {axis_names[p.axisTag]: p.position for p in master.position} designspace.addSourceDescriptor( name=master.output_ufo, location=location, font=ufo ) # build a variable TTFont from the designspace document # TODO: Use ufo2ft.compileVariableCFF2 for CFF vf = ufo2ft.compileVariableTTF(designspace) vf.save(font_config.output_file)
def build_fonts(designspace, static, *steps): for font in [source.font for source in designspace.sources]: for step in steps: step(font) set_font_metaData(font, "var") familyName = designspace.default.font.info.familyName file_stem = familyName.replace(" ", "") file_path = (OUTPUT_DIR / file_stem).with_suffix(f".ttf") print(f"[{familyName}] Compiling") varFont = ufo2ft.compileVariableTTF(designspace) print(f"[{familyName}] Adding STAT table") styleSpace = classes.Stylespace.from_file(INPUT_DIR / "STAT.plist") lib.apply_stylespace_to_variable_font(styleSpace,varFont,{}) print(f"[{familyName}] Merging VTT") vttLib.transfer.merge_from_file(varFont, VTT_DATA_FILE) varFont = overlapFlag(varFont) print(f"[{familyName}] Saving") varFont.save(file_path) print(f"[{familyName}] Done: {file_path}") if static: if os.path.exists(OUTPUT_DIR / "static") == False: os.mkdir(OUTPUT_DIR / "static") generator = fontmake.instantiator.Instantiator.from_designspace(designspace) print(f"[{familyName}] Building static instances") for instance_descriptor in designspace.instances: instance = generator.generate_instance(instance_descriptor) print(f"[{familyName}] "+instance.info.styleName) instance = generator.generate_instance(instance_descriptor) staticTTF = ufo2ft.compileTTF(instance,removeOverlaps=True) staticOTF = ufo2ft.compileOTF(instance,removeOverlaps=True) file_name = file_stem+"-"+instance.info.styleName file_path_static = (OUTPUT_DIR / "static" / file_name).with_suffix(f".ttf") file_path_static_otf = (OUTPUT_DIR / "static" / file_name).with_suffix(f".otf") staticTTF.save(file_path_static) staticOTF.save(file_path_static_otf) print(f"[{familyName}] Done building static instances")
def buildVariable( self, designspace, # designspace filename string or DesignSpaceDocument object outputFilename, # output filename string cff=False, # if true, builds CFF-2 font, else TTF **kwargs, # passed along to ufo2ft.compileVariable*() ): designspace = self._loadDesignspace(designspace) # check in the designspace's <lib> element if user supplied a custom featureWriters # configuration; if so, use that for all the UFOs built from this designspace. featureWriters = None if ufo2ft.featureWriters.FEATURE_WRITERS_KEY in designspace.lib: featureWriters = ufo2ft.featureWriters.loadFeatureWriters( designspace) compilerOptions = dict( useProductionNames=True, featureWriters=featureWriters, inplace=True, # avoid extra copy **kwargs) if log.isEnabledFor(logging.INFO): log.info("compiling %s -> %s (%s)", designspace.path, outputFilename, "OTF/CFF-2" if cff else "TTF") if cff: font = ufo2ft.compileVariableCFF2(designspace, **compilerOptions) else: font = ufo2ft.compileVariableTTF(designspace, **compilerOptions) # Rename fullName record to familyName (VF only). # Note: Even though we set openTypeNameCompatibleFullName it seems that the fullName # record is still computed by fonttools, so we override it here. setFullName(font, getFamilyName(font)) # rebuild STAT table to correct VF instance information rebuildStatTable(font, designspace) log.debug("writing %s", outputFilename) font.save(outputFilename)
def build_variable_fonts(designspace, *steps): sourceFonts = [ ufoLib2.Font.open(INPUT_DIR / designspace.sources[0].filename), ufoLib2.Font.open(INPUT_DIR / designspace.sources[1].filename), ufoLib2.Font.open(INPUT_DIR / designspace.sources[2].filename) ] for source in sourceFonts: set_font_metaData(source, "var") designspace.sources[0].font = sourceFonts[0] #ExtraLight designspace.sources[1].font = sourceFonts[1] #Regular designspace.sources[2].font = sourceFonts[2] #Bold for font in sourceFonts: for step in steps: step(font) familyName = sourceFonts[1].info.familyName file_stem = sourceFonts[1].info.familyName.replace(" ", "") file_path = (OUTPUT_DIR / file_stem).with_suffix(f".ttf") print(f"[{familyName}] Compiling") varFont = ufo2ft.compileVariableTTF(designspace) print(f"[{familyName}] Adding STAT table") styleSpace = classes.Stylespace.from_file(INPUT_DIR / "STAT.plist") lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) print(f"[{familyName}] Merging VTT") vttLib.transfer.merge_from_file(varFont, VTT_DATA_FILE) print(f"[{familyName}] Saving") varFont.save(file_path) print(f"[{familyName}] Done: {file_path}")
def compile_variable_and_save( designspace: fontTools.designspaceLib.DesignSpaceDocument, vtt_compile: bool = True, ) -> None: familyName = designspace.default.font.info.familyName file_stem = familyName.replace(" ", "") file_path: Path = (OUTPUT_TTF_DIR / file_stem).with_suffix(".ttf") print(f"[{familyName}] Compiling") varFont = ufo2ft.compileVariableTTF(designspace, inplace=True) print(f"[{familyName}] Adding STAT table") styleSpace = statmake.classes.Stylespace.from_file(INPUT_DIR / "STAT.plist") statmake.lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) print(f"[{familyName}] Merging VTT") font_vtt = fontTools.ttLib.TTFont(VTT_DATA_FILE) for table in ["TSI0", "TSI1", "TSI2", "TSI3", "TSI5", "TSIC", "maxp"]: varFont[table] = fontTools.ttLib.newTable(table) varFont[table] = font_vtt[table] # this will correct the OFFSET[R] commands in TSI1 if font_vtt.getGlyphOrder() != varFont.getGlyphOrder(): tsi1.fixOFFSET(varFont, font_vtt) pass if vtt_compile: print(f"[{familyName}] Compiling VTT") tree = ET.ElementTree() TSICfile = tempfile.NamedTemporaryFile() varFont.saveXML(TSICfile.name, tables=["TSIC"]) tree = ET.parse(TSICfile.name) vttLib.compile_instructions(varFont, ship=True) tsic.makeCVAR(varFont, tree) else: file_path = Path(str(file_path)[:-4] + "_VTT.ttf") set_overlap_flag(varFont) # last minute manual corrections to set things correctly # flag to enable proper rendering # Adjusted the font full name so that it aligns with font spec, and also shows as expected! # Adjusting postscript name to make room for the upcoming Italic # Helping mac office generage the postscript name correctly for variable fonts varFont["head"].flags = 0x000b varFont["name"].setName( familyName.replace(" ", "") + "-Roman", 6, 3, 1, 1033) varFont["name"].setName("Roman", 17, 3, 1, 1033) varFont["name"].setName( familyName.replace(" ", "") + "Roman", 25, 3, 1, 1033) print(f"[{familyName}] Saving") file_path.parent.mkdir(exist_ok=True, parents=True) varFont.save(file_path) print(f"[{familyName}] Done: {file_path}")
def build_variable(type: str, ds: DesignSpaceDocument) -> None: output = Path("fonts/ttf") if type == "latin": for instance in ds.instances: instance.name = instance.name.replace("Code", "Code Latin") instance.familyName = instance.familyName.replace( "Code", "Code Latin") if instance.styleMapFamilyName: instance.styleMapFamilyName = str( instance.styleMapFamilyName).replace("Code", "Code Latin") varFont = ufo2ft.compileVariableTTF(ds) styleSpace = statmake.classes.Stylespace.from_file( "sources/Latin_STAT.plist") statmake.lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) DSIG_modification(varFont) varFont["name"].setName("Mplus Code Latin", 1, 3, 1, 1033) varFont["name"].setName("UFDN;MplusCodeLatin-Regular", 3, 3, 1, 1033) varFont["name"].setName("Mplus Code Latin Regular", 4, 3, 1, 1033) varFont["name"].setName("MplusCodeLatin-Regular", 6, 3, 1, 1033) varFont.save(output / "MplusCodeLatin[wdth,wght].ttf") autohint(output / "MplusCodeLatin[wdth,wght].ttf") prefix = "MplusCodeLatin" if type == "one" or type == "two": print("[MPLUS " + type + "] Importing Kanji") for source in ds.sources: if "{" not in source.name: step_merge_glyphs_from_ufo( Path("sources/M+1p-" + source.filename[7:-4] + ".ufo"), source.font) source.font.features.text = Path( "sources/features.fea").read_text() print("[MPLUS " + type + "] Importing Kanji replacement rules") kanji_ds = DesignSpaceDocument.fromfile( "sources/MPLUS-Kanji.designspace") for rule in kanji_ds.rules: ds.rules.append(rule) print("[MPLUS " + type + "] Building") varFont = ufo2ft.compileVariableTTF(ds) styleSpace = statmake.classes.Stylespace.from_file( "sources/MPLUS_STAT.plist") statmake.lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) DSIG_modification(varFont) print("[MPLUS " + type + "] Saving") if type == "one": varFont.save(output / "Mplus1[wght].ttf") autohint(output / "Mplus1[wght].ttf") prefix = "Mplus1" elif type == "two": varFont.save(output / "Mplus2[wght].ttf") autohint(output / "Mplus2[wght].ttf") prefix = "Mplus2" if type == "code": for instance in ds.instances: instance.name = instance.name.replace("Mplus", "Mplus 1 ") instance.familyName = instance.familyName.replace( "Mplus", "Mplus 1 ") if instance.styleMapFamilyName: instance.styleMapFamilyName = instance.styleMapFamilyName.replace( "MplusCode", "Mplus 1 Code") print("[MPLUS " + type + "] Importing glyphs") for source in ds.sources: if "{" not in source.name: step_merge_glyphs_from_ufo( Path("sources/Mplus1-" + str(source.name).split(" ")[2] + ".ufo"), source.font, "sources/kana_glyphs.txt") step_merge_glyphs_from_ufo( Path("sources/M+1p-" + str(source.name).split(" ")[2] + ".ufo"), source.font) source.name = source.name.replace("Mplus", "Mplus 1") source.font.features.text = Path("sources/code.fea").read_text() print("[MPLUS " + type + "] Importing Kanji replacement rules") kanji_ds = DesignSpaceDocument.fromfile( "sources/MPLUS-Kanji.designspace") for rule in kanji_ds.rules: ds.rules.append(rule) print("[MPLUS " + type + "] Building") varFont = ufo2ft.compileVariableTTF(ds) styleSpace = statmake.classes.Stylespace.from_file( "sources/MPLUS_STAT.plist") statmake.lib.apply_stylespace_to_variable_font(styleSpace, varFont, {}) DSIG_modification(varFont) varFont["name"].setName("Mplus 1 Code", 1, 3, 1, 1033) varFont["name"].setName("UFDN;Mplus1Code-Regular", 3, 3, 1, 1033) varFont["name"].setName("Mplus 1 Code Regular", 4, 3, 1, 1033) varFont["name"].setName("Mplus1Code-Regular", 6, 3, 1, 1033) print("[MPLUS " + type + "] Saving") varFont.save(output / "Mplus1Code[wght].ttf") autohint(output / "Mplus1Code[wght].ttf") prefix = "Mplus1Code" generator = fontmake.instantiator.Instantiator.from_designspace(ds) pool = multiprocessing.pool.Pool(processes=multiprocessing.cpu_count()) processes = [] for instance_descriptor in ds.instances: # GOTTA GO FAST processes.append( pool.apply_async( make_static, (instance_descriptor, generator, prefix), )) pool.close() pool.join() for process in processes: process.get() del processes, pool
"designspace_path", type=Path, help="The path to the Designspace file." ) parser.add_argument( "stylespace_path", type=Path, help="The path to the Stylespace file." ) parser.add_argument("output_path", type=Path, help="The variable TTF output path.") args = parser.parse_args() designspace_path = args.designspace_path.resolve() stylespace_path = args.stylespace_path.resolve() output_path = args.output_path.resolve() # 1. Load Designspace and filter out instances that are marked as non-exportable. designspace = fontTools.designspaceLib.DesignSpaceDocument.fromfile(designspace_path) designspace.loadSourceFonts(ufoLib2.Font.open) designspace.instances = [ s for s in designspace.instances if s.lib.get("com.schriftgestaltung.export", True) ] # 2. Compile variable TTF from the masters. varfont = ufo2ft.compileVariableTTF(designspace, inplace=True) # 3. Generate STAT table. stylespace = statmake.classes.Stylespace.from_file(stylespace_path) statmake.lib.apply_stylespace_to_variable_font(stylespace, varfont, {}) varfont.save(output_path)