def convertToOTF(ttfPath, dest, report): temp = tempfile.mkstemp(suffix=".otf")[1] font = RFont(ttfPath, document=False, showInterface=False) font.kerning.clear() for attr in font.info.asDict().keys(): if attr not in defaultFontInfoAttributes: setattr(font.info, attr, None) result = font.generate(path=temp, format="otf", decompose=False, checkOutlines=False, autohint=False, releaseMode=True, glyphOrder=font.glyphOrder) if not font.hasInterface(): font.close() report.write(result) sourceFont = TTFont(temp) sourceFontWithTables = TTFont(ttfPath) for table in ["loca", "OS/2", "cmap", "name", "GSUB", "GPOS", "GDEF", "kern"]: if table in sourceFontWithTables: sourceFont[table] = sourceFontWithTables[table] sourceFont.save(dest) result = OTFAutohint(dest) report.writeItems(result) os.remove(temp)
def ttCompile(input, output, options): print('Compiling "%s" to "%s"...' % (input, output)) ttf = TTFont(options.mergeFile, recalcBBoxes=options.recalcBBoxes, verbose=options.verbose, allowVID=options.allowVID) ttf.importXML(input) try: ttf.save(output) except OTLOffsetOverflowError as e: # XXX This shouldn't be here at all, it should be as close to the # OTL code as possible. overflowRecord = e.value print("Attempting to fix OTLOffsetOverflowError", e) lastItem = overflowRecord while 1: ok = 0 if overflowRecord.itemName == None: ok = fixLookupOverFlows(ttf, overflowRecord) else: ok = fixSubTableOverFlows(ttf, overflowRecord) if not ok: raise try: ttf.save(output) break except OTLOffsetOverflowError as e: print("Attempting to fix OTLOffsetOverflowError", e) overflowRecord = e.value if overflowRecord == lastItem: raise if options.verbose: import time print("finished at", time.strftime("%H:%M:%S", time.localtime(time.time())))
def post(self): fontdata = self.request.POST.get('font', None) # Need to use isinstance as cgi.FieldStorage always evaluates to False. # See http://bugs.python.org/issue19097 if not isinstance(fontdata, cgi.FieldStorage): self.redirect('/font_conversion.html?' + urllib.urlencode( {'err_msg': 'Please select a font'})) return #TODO(bstell) make this work correctly. font_type = 'woff' name = os.path.splitext(os.path.basename(fontdata.filename))[0] try: font = TTFont(fontdata.file) except: self.redirect('/font_conversion.html?' + urllib.urlencode( {'err_msg': 'failed to parse font'})) return self.response.headers['Content-Type'] = 'application/font-woff' self.response.headers['Content-Disposition'] = \ 'attachment; filename={0}.{1}'.format(name, font_type) font.flavor = font_type output = StringIO.StringIO() font.save(output) self.response.out.write(output.getvalue())
def test_check_unique_glyphnames(): """ Font contains unique glyph names? """ import io from fontbakery.profiles.universal import com_google_fonts_check_unique_glyphnames as check test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") test_font = TTFont(test_font_path) status, _ = list(check(test_font))[-1] assert status == PASS # Fonttools renames duplicate glyphs with #1, #2, ... on load. # Code snippet from https://github.com/fonttools/fonttools/issues/149. glyph_names = test_font.getGlyphOrder() glyph_names[2] = glyph_names[3] # Load again, we changed the font directly. test_font = TTFont(test_font_path) test_font.setGlyphOrder(glyph_names) test_font['post'] # Just access the data to make fonttools generate it. test_file = io.BytesIO() test_font.save(test_file) test_font = TTFont(test_file) status, message = list(check(test_font))[-1] assert status == FAIL assert "space" in message # Upgrade to post format 3.0 and roundtrip data to update TTF object. test_font = TTFont(test_font_path) test_font.setGlyphOrder(glyph_names) test_font["post"].formatType = 3.0 test_file = io.BytesIO() test_font.save(test_file) test_font = TTFont(test_file) status, message = list(check(test_font))[-1] assert status == SKIP
def test_check_valid_glyphnames(): """ Glyph names are all valid? """ import io from fontbakery.profiles.universal import com_google_fonts_check_valid_glyphnames as check test_font_path = TEST_FILE("nunito/Nunito-Regular.ttf") test_font = TTFont(test_font_path) status, _ = list(check(test_font))[-1] assert status == PASS bad_name1 = "a" * 32 bad_name2 = "3cents" bad_name3 = ".threecents" good_name1 = "b" * 31 test_font.glyphOrder[2] = bad_name1 test_font.glyphOrder[3] = bad_name2 test_font.glyphOrder[4] = bad_name3 test_font.glyphOrder[5] = good_name1 status, message = list(check(test_font))[-1] assert status == FAIL assert bad_name1 in message assert bad_name2 in message assert bad_name3 in message assert good_name1 not in message # Upgrade to post format 3.0 and roundtrip data to update TTF object. test_font = TTFont(test_font_path) test_font["post"].formatType = 3.0 test_file = io.BytesIO() test_font.save(test_file) test_font = TTFont(test_file) status, message = list(check(test_font))[-1] assert status == SKIP
def main(args=None): if args is None: args = sys.argv[1:] if len(args) < 2: print("usage: merge_woff_metadata.py METADATA.xml " "INPUT.woff [OUTPUT.woff]", file=sys.stderr) return 1 metadata_file = args[0] with open(metadata_file, 'rb') as f: metadata = f.read() infile = args[1] if len(args) > 2: outfile = args[2] else: filename, ext = os.path.splitext(infile) outfile = makeOutputFileName(filename, None, ext) font = TTFont(infile) if font.flavor not in ("woff", "woff2"): print("Input file is not a WOFF or WOFF2 font", file=sys.stderr) return 1 data = font.flavorData # this sets the new WOFF metadata data.metaData = metadata font.save(outfile)
def generate(font, extension): """ Clean GSUB lookups and merge them from the feature files """ for lookup in font.gsub_lookups: font.removeLookup(lookup) font.mergeFeature('%s/%s_features.fea' %(feafiles, style)) font.selection.all() font.autoHint() if extension == 'ttf': # font.em = 2048 font.round() font.autoInstr() path = '%s/%s.%s' %(build, font.fontname, extension) tmp_path = '%s.tmp.%s' %(font.fontname, extension) font.generate(tmp_path) tmp_font = TTFont(tmp_path) # tmp_font['OS/2'].sxHeight, tmp_font['OS/2'].sCapHeight = getHeights(font) tmp_font.save(path) tmp_font.close() os.remove(tmp_path)
def __init__(self, sourcePath, location, DEBUG=False): self.instancePath = tempfile.mkstemp()[1] tempPath = sourcePath.replace('.ttf', '-instance.ttf') cmds = ['python', pathToFontToolsMutator, sourcePath] for k, v in location.items(): cmds.append('%s=%s' %(k, v)) proc = subprocess.Popen(cmds, stdout=subprocess.PIPE) out = proc.communicate()[0] if DEBUG: print '---' print ' '.join(cmds) print '---' print out myUUID = str(uuid.uuid4()).replace('-', '') f = TTFont(tempPath) self.fontName = 'VF'+str(myUUID) f['name'].setName(self.fontName, 6, 1, 0, 0) # Macintosh f['name'].setName(self.fontName, 6, 3, 1, 0x409) # Windows os.remove(tempPath) f.save(self.instancePath)
def main(fontTargetPath, fontBracePath, braceGlyphsString): try: fontTarget = TTFont(fontTargetPath) fontBrace = TTFont(fontBracePath) except IOError as e: print e exit() except TTLibError as e: print e exit() braceGlyphsList = braceGlyphsString.split(",") print braceGlyphsList # print "Replacing brace glyphs in " + fontTargetPath + " with those from " + fontBracePath for glyphVariationsName in fontBrace['gvar'].variations: if glyphVariationsName in braceGlyphsList: fontTarget['gvar'].variations[glyphVariationsName] = fontBrace['gvar'].variations[glyphVariationsName] print "Replaced glyphVariation " + glyphVariationsName # We replace TTGlyphs after as updating glyphVariations otherwise an erorr is raised for TTGlyphName in fontBrace['glyf'].glyphs: if glyphVariationsName in braceGlyphsList: fontTarget['glyf'].glyphs[TTGlyphName] = fontBrace['glyf'].glyphs[TTGlyphName] print "Replaced TTGlyph " + TTGlyphName if __name__ == "__main__": fontTarget.save(fontTargetPath)
def handle_font(font_name): font = TTFont(font_name) orig_size = os.path.getsize(font_name) if decompress: from fontTools import subset options = subset.Options() options.desubroutinize = True subsetter = subset.Subsetter(options=options) subsetter.populate(glyphs=font.getGlyphOrder()) subsetter.subset(font) if verbose: print("Compressing font through iterative_encode:") out_name = "%s.compressed%s" % os.path.splitext(font_name) compreffor = Compreffor(font, verbose=verbose, **comp_kwargs) compreffor.compress() # save compressed font font.save(out_name) if generate_cff: # save CFF version font["CFF "].cff.compile(open("%s.cff" % os.path.splitext(out_name)[0], "w"), None) comp_size = os.path.getsize(out_name) print("Compressed to %s -- saved %s" % (os.path.basename(out_name), human_size(orig_size - comp_size))) if check: test_compression_integrity(filename, out_name) test_call_depth(out_name)
def generateFont(font, outfile): flags = ("opentype", "dummy-dsig", "round", "omit-instructions") font.selection.all() font.correctReferences() font.selection.none() # fix some common font issues validateGlyphs(font) tmpfile = mkstemp(suffix=os.path.basename(outfile))[1] font.generate(tmpfile, flags=flags) font.close() # now open in fontTools from fontTools.ttLib import TTFont ftfont = TTFont(tmpfile) # force compiling tables by fontTools, saves few tens of KBs for tag in ftfont.keys(): if hasattr(ftfont[tag], "compile"): ftfont[tag].compile(ftfont) ftfont.save(outfile) ftfont.close() os.remove(tmpfile)
def build(args): font, features = merge(args) build_encoded(font, features) with tempfile.NamedTemporaryFile(mode="r", suffix=args.out_file) as tmp: font.generate(tmp.name, flags=["round", "opentype"]) ttfont = TTFont(tmp.name) try: builder.addOpenTypeFeatures(ttfont, features) except: with tempfile.NamedTemporaryFile(mode="w+", delete=False) as tmp: tmp.write(features.asFea()) print("Failed! Inspect temporary file: %r" % tmp.name) raise # Filter-out useless Macintosh names ttfont["name"].names = [n for n in ttfont["name"].names if n.platformID != 1] # https://github.com/fontforge/fontforge/pull/3235 # fontDirectionHint is deprecated and must be set to 2 ttfont["head"].fontDirectionHint = 2 # unset bits 6..10 ttfont["head"].flags &= ~0x7e0 # Drop useless table with timestamp if "FFTM" in ttfont: del ttfont["FFTM"] ttfont.save(args.out_file)
def run(self): font = TTFont(self.in_font) gs = font.getGlyphSet() glyf = font["glyf"] hmtx = font["hmtx"] for gname, (adw, lsb) in hmtx.metrics.items(): # https://github.com/fonttools/fonttools/blob/master/Lib/fontTools/ttLib/tables/_g_l_y_f.py#L189-L192 # __getitem__ internally calls table__g_l_y_f.expand which is essential to obtain xMin g = glyf[gname] # obtain recalculated xMin from glyph's control bounds g.recalcBounds(glyf) hmtx.metrics[gname] = (adw, g.xMin) if self.update_vmtx: from fontTools.pens.boundsPen import BoundsPen vmtx = font["vmtx"] for gname, (adh, tsb) in vmtx.metrics.items(): g = glyf[gname] # obtain yMax g.recalcBounds(glyf) pen = BoundsPen(gs) g.draw(pen, glyf) if pen.bounds is None: continue left, bottom, right, top = pen.bounds vmtx.metrics[gname] = (adh, top + tsb - g.yMax) font.save(self.out_font) return 0
class MyFontTools(object): def __init__(self, fontfile): self.font = TTFont(fontfile) def cmap_format_gbk2utf8(self): cmap = self.font['cmap'] outtables = [] for table in cmap.tables: if table.format in [4, 12, 13, 14]: outtables.append(table) # Convert ot format4 if table.getEncoding() in SUPPORT_CONVERT_FROM_ENCODE: for gbk_code in table.cmap.keys(): uni_code= convert_from_gbk(gbk_code) if gbk_code != uni_code: table.cmap[uni_code] = table.cmap.pop(gbk_code) newtable = CmapSubtable.newSubtable(4) newtable.platformID = self.to_platformID newtable.platEncID = self.to_platEncID newtable.language = table.language newtable.cmap = table.cmap outtables.append(newtable) cmap.tables = outtables def vhea_fixed(self): self.font['vhea'].tableVersion=1.0 def save(self, outfile): import pdb;pdb.set_trace() self.font.save(outfile)
def saveFont(glyphs, outputFileName, asXML, pixelSize, descent, fontName, copyrightYear, creator, version): f = TTFont() vectorizedGlyphs = {glyph : [vectorizeGlyph(glyphs[glyph][0], pixelSize, descent), glyphs[glyph][1]] for glyph in glyphs} unicodes = [code for glyph in glyphs for code in glyphs[glyph][1]] # Populate basic tables (there are a few dependencies so order matters) makeTable_glyf(f, vectorizedGlyphs) makeTable_maxp(f) makeTable_loca(f) makeTable_head(f) makeTable_hmtx(f) makeTable_hhea(f, pixelSize, descent) makeTable_OS2(f, pixelSize, descent, min(unicodes), max(unicodes)) makeTable_cmap(f, glyphs) makeTable_name(f, fontName, "Regular", copyrightYear, creator, version) makeTable_post(f, pixelSize, descent) if asXML: # We have to compile the TTFont manually when saving as TTX # (to auto-calculate stuff here and there) f["glyf"].compile(f) f["maxp"].compile(f) f["loca"].compile(f) f["head"].compile(f) f["hmtx"].compile(f) f["hhea"].compile(f) f["OS/2"].compile(f) f["cmap"].compile(f) f["name"].compile(f) f["post"].compile(f) print "PLEASE NOTE: When exporting directly to XML, the checkSumAdjustment value in the head table will be 0." f.saveXML(outputFileName) else: f.save(outputFileName)
def main(fontTargetPath, fontBracePath): try: fontTarget = TTFont(fontTargetPath) fontBrace = TTFont(fontBracePath) except IOError as e: print e exit() except TTLibError as e: print e exit() print "Replacing brace glyphs in " + fontTargetPath + " with those from " + fontBracePath excludedGlyphs = ['.notdef', 'space'] for glyphVariationsName in fontBrace['gvar'].variations: if glyphVariationsName in excludedGlyphs: continue fontTarget['gvar'].variations[glyphVariationsName] = fontBrace['gvar'].variations[glyphVariationsName] print "Replaced glyphVariation " + glyphVariationsName # We replace TTGlyphs after as updating glyphVariations otherwise an erorr is raised for TTGlyphName in fontBrace['glyf'].glyphs: if TTGlyphName in excludedGlyphs: continue fontTarget['glyf'].glyphs[TTGlyphName] = fontBrace['glyf'].glyphs[TTGlyphName] print "Replaced TTGlyph " + TTGlyphName if __name__ == "__main__": fontTarget.save(fontTargetPath)
def makeWeb(args): """If we are building a web version then try to minimise file size""" font = TTFont(args.file) # removed compatibility glyphs that of little use on the web ranges = ( (0xfb50, 0xfbb1), (0xfbd3, 0xfd3d), (0xfd50, 0xfdf9), (0xfdfc, 0xfdfc), (0xfe70, 0xfefc), ) cmap = font['cmap'].buildReversed() unicodes = set([min(cmap[c]) for c in cmap]) for r in ranges: unicodes -= set(range(r[0], r[1] + 1)) options = subset.Options() options.set(layout_features='*', name_IDs='*', drop_tables=['DSIG']) subsetter = subset.Subsetter(options=options) subsetter.populate(unicodes=unicodes) subsetter.subset(font) base, ext = os.path.splitext(args.file) for flavor in ("ttf", "woff", "woff2"): if flavor is not "ttf": font.flavor = flavor font.save(args.dir + "/" + base + "." + flavor) font.close()
def main(args=None): parser = argparse.ArgumentParser( description="Use fontTools to compile OpenType feature files (*.fea).") parser.add_argument( "input_fea", metavar="FEATURES", help="Path to the feature file") parser.add_argument( "input_font", metavar="INPUT_FONT", help="Path to the input font") parser.add_argument( "-o", "--output", dest="output_font", metavar="OUTPUT_FONT", help="Path to the output font.") parser.add_argument( "-t", "--tables", metavar="TABLE_TAG", choices=Builder.supportedTables, nargs='+', help="Specify the table(s) to be built.") parser.add_argument( "-v", "--verbose", help="increase the logger verbosity. Multiple -v " "options are allowed.", action="count", default=0) options = parser.parse_args(args) levels = ["WARNING", "INFO", "DEBUG"] configLogger(level=levels[min(len(levels) - 1, options.verbose)]) output_font = options.output_font or makeOutputFileName(options.input_font) log.info("Compiling features to '%s'" % (output_font)) font = TTFont(options.input_font) addOpenTypeFeatures(font, options.input_fea, tables=options.tables) font.save(output_font)
def compile_font(self, path, suffix, temp_dir): ttx_filename = os.path.basename(path) savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix)) font = TTFont(recalcBBoxes=False, recalcTimestamp=False) font.importXML(path) font.save(savepath, reorderTables=None) return font, savepath
def webfonts(infont, type): font = TTFont(infont, recalcBBoxes=0) woffFileName = makeOutputFileName( infont, outputDir=None, extension='.' + type) font.flavor = type font.save(woffFileName, reorderTables=False) font.close()
def vtt_merge(infile, outfile=None, **kwargs): ufo = infile if not os.path.exists(ufo) or not os.path.isdir(ufo): raise VTTLibArgumentError("No such directory: '%s'" % ufo) check_ufo_version(ufo) if not outfile: outfile = os.path.splitext(infile)[0] + ".ttf" if not os.path.exists(outfile): raise VTTLibArgumentError("'%s' not found" % outfile) font = TTFont(outfile) if font.sfntVersion not in ("\x00\x01\x00\x00", "true"): raise VTTLibArgumentError("Not a TrueType font (bad sfntVersion)") for ttx in glob.glob(os.path.join(ufo, "data", TTX_DATA_FOLDER, "*.ttx")): identifier = os.path.splitext(os.path.basename(ttx))[0] try: tag = identifierToTag(identifier) except: continue else: if tag not in VTT_TABLES: continue font.importXML(ttx) read_maxp_data(ufo, font) font.save(outfile)
def process(infile, outfile, layer): font = TTFont(infile) glyf = font["glyf"] glyphNamesToKeep = [] if layer == "letters": for glyphName in font.getGlyphNames(): if glyphName not in _LAYER2_GLYPHS and glyphName not in _LAYER3_GLYPHS: glyphNamesToKeep.append(glyphName) elif layer == "diacritics": glyphNamesToKeep = _LAYER2_GLYPHS elif layer == "quranic-signs": glyphNamesToKeep = _LAYER3_GLYPHS for glyphName in font.getGlyphNames(): if glyphName not in glyphNamesToKeep: glyph = glyf[glyphName] if glyphName in ("uni0670.medi", "uni06E5.medi", "uni06E6.medi") and layer == "letters": # We want to keep the kashida part of those glyphs. components = [] for component in glyph.components: if component.glyphName != glyphName.split(".")[0]: components.append(component) glyph.components = components else: # This will cause FontTools not to output any outlines for that # glyph. glyph.numberOfContours = 0 font.save(outfile)
def test_check_069(): """ Is there any unused data at the end of the glyf table? """ from fontbakery.specifications.glyf import com_google_fonts_check_069 as check test_font_path = os.path.join("data", "test", "nunito", "Nunito-Regular.ttf") test_font = TTFont(test_font_path) status, _ = list(check(test_font))[-1] assert status == PASS # Always start with a fresh copy, as fT works lazily. Accessing certain data # can prevent the test from working because we rely on uninitialized # behavior. test_font = TTFont(test_font_path) test_font["loca"].locations.pop() test_file = io.BytesIO() test_font.save(test_file) test_font = TTFont(test_file) status, message = list(check(test_font))[-1] assert status == FAIL assert message.code == "unreachable-data" test_font = TTFont(test_font_path) test_font["loca"].locations.append(50000) test_file = io.BytesIO() test_font.save(test_file) test_font = TTFont(test_file) status, message = list(check(test_font))[-1] assert status == FAIL assert message.code == "missing-data"
def componentize_ttf(self): """ Loads a TrueType font and iterates thru a dictionary of composites data. Remakes some glyphs in the glyf table from being made of countours to being made of components. Saves the modified font in a new location if an output path was provided, otherwise overwrites the original one. Updates a count of the glyphs that got componentized. """ font = TTFont(self.opts.font_path) glyf_table = font['glyf'] for gname in self.composites_data: if gname not in glyf_table: continue if not all([cname in glyf_table for cname in self.composites_data[ gname].names]): continue components = self.assemble_components(self.composites_data[gname]) glyph = glyf_table[gname] glyph.__dict__.clear() setattr(glyph, "components", components) glyph.numberOfContours = -1 self.comp_count += 1 if self.opts.output_path: font.save(os.path.realpath(self.opts.output_path)) else: font.save(self.opts.font_path)
def _optimizeForEOT(sourcePath, destPath): source = TTFont(sourcePath) # fix naming nameTable = source["name"] familyName = nameTable.getName(1, 3, 1) styleName = nameTable.getName(2, 3, 1) if familyName: familyName = familyName.string else: familyName = "Untitled" if styleName: styleName = styleName.string else: styleName = "Regular" records = [] for record in nameTable.names: # ignore preferred naming if record.nameID not in [16, 17]: if record.nameID == 4: s = "%s %s" % (familyName, styleName) if record.platformID == 3 and record.platEncID in (0, 1): record.string = _winStr(s) else: record.string = _macStr(s) records.append(record) nameTable.names = records # set embedding bit os2 = source["OS/2"] os2.fsType = 4 source.save(destPath) source.close()
def main(args=None): configLogger(logger=log) parser = argparse.ArgumentParser() parser.add_argument("input", nargs='+', metavar="INPUT") parser.add_argument("-o", "--output") parser.add_argument("-e", "--max-error", type=float, default=MAX_ERR) parser.add_argument("--post-format", type=float, default=POST_FORMAT) parser.add_argument( "--keep-direction", dest='reverse_direction', action='store_false') parser.add_argument("--face-index", type=int, default=0) parser.add_argument("--overwrite", action='store_true') options = parser.parse_args(args) if options.output and len(options.input) > 1: if not os.path.isdir(options.output): parser.error("-o/--output option must be a directory when " "processing multiple fonts") for path in options.input: if options.output and not os.path.isdir(options.output): output = options.output else: output = makeOutputFileName(path, outputDir=options.output, extension='.ttf', overWrite=options.overwrite) font = TTFont(path, fontNumber=options.face_index) otf_to_ttf(font, post_format=options.post_format, max_err=options.max_error, reverse_direction=options.reverse_direction) font.save(output)
def ttCompile(input, output, options): if not options.quiet: print 'Compiling "%s" to "%s"...' % (input, output) ttf = TTFont(options.mergeFile, recalcBBoxes=options.recalcBBoxes, verbose=options.verbose, allowVID=options.allowVID) ttf.importXML(input, quiet=options.quiet) try: ttf.save(output) except OTLOffsetOverflowError, e: # XXX This shouldn't be here at all, it should be as close to the # OTL code as possible. overflowRecord = e.value print "Attempting to fix OTLOffsetOverflowError", e lastItem = overflowRecord while 1: ok = 0 if overflowRecord.itemName == None: ok = fixLookupOverFlows(ttf, overflowRecord) else: ok = fixSubTableOverFlows(ttf, overflowRecord) if not ok: raise try: ttf.save(output) break except OTLOffsetOverflowError, e: print "Attempting to fix OTLOffsetOverflowError", e overflowRecord = e.value if overflowRecord == lastItem: raise
def webfonts(infont, type): font = TTFont(infont, recalcBBoxes=0) # Generate WOFF2 woffFileName = makeOutputFileName(infont, outputDir=None, extension='.' + type) print("Processing %s => %s" % (infont, woffFileName)) font.flavor = type font.save(woffFileName, reorderTables=False) font.close()
def patch(filepath): print "Patching '%s' ..." % filepath, base, ext = filepath.rsplit(".", 1) font = TTFont(filepath) patch_cmap(font) font.save("%s_patch.%s" % (base, ext)) font.close() print "OK"
def main(args=None): log = compreffor.log timer = compreffor.timer timer.reset() options = parse_arguments(args) # consume kwargs that are not passed on to 'compress' function infile = options.pop('infile') outfile = options.pop('outfile') decompress = options.pop('decompress') generate_cff = options.pop('generate_cff') check = options.pop('check') verbose = options.pop('verbose') if verbose == 1: level = logging.INFO elif verbose > 1: level = logging.DEBUG else: level = logging.WARNING configLogger(logger=log, level=level) orig_size = os.path.getsize(infile) font = TTFont(infile) if decompress: log.info("Decompressing font with FontTools Subsetter") with timer("decompress the font"): compreffor.decompress(font) log.info("Compressing font through %s Compreffor", "pure-Python" if options['method_python'] else "C++") compreffor.compress(font, **options) with timer("compile and save compressed font"): font.save(outfile) if generate_cff: cff_file = os.path.splitext(outfile)[0] + ".cff" with open(cff_file, 'wb') as f: font['CFF '].cff.compile(f, None) log.info("Saved CFF data to '%s'" % os.path.basename(cff_file)) if check: log.info("Checking compression integrity and call depth") passed = compreffor.check(infile, outfile) if not passed: return 1 comp_size = os.path.getsize(outfile) log.info("Compressed to '%s' -- saved %s" % (os.path.basename(outfile), human_size(orig_size - comp_size))) log.debug("Total time: %gs", timer.time())
def main(args=None): parser = argparse.ArgumentParser( description="Use fontTools to compile OpenType feature files (*.fea).") parser.add_argument("input_fea", metavar="FEATURES", help="Path to the feature file") parser.add_argument("input_font", metavar="INPUT_FONT", help="Path to the input font") parser.add_argument("-o", "--output", dest="output_font", metavar="OUTPUT_FONT", help="Path to the output font.") parser.add_argument("-t", "--tables", metavar="TABLE_TAG", choices=Builder.supportedTables, nargs='+', help="Specify the table(s) to be built.") parser.add_argument("-v", "--verbose", help="increase the logger verbosity. Multiple -v " "options are allowed.", action="count", default=0) parser.add_argument("--traceback", help="show traceback for exceptions.", action="store_true") options = parser.parse_args(args) levels = ["WARNING", "INFO", "DEBUG"] configLogger(level=levels[min(len(levels) - 1, options.verbose)]) output_font = options.output_font or makeOutputFileName(options.input_font) log.info("Compiling features to '%s'" % (output_font)) font = TTFont(options.input_font) try: addOpenTypeFeatures(font, options.input_fea, tables=options.tables) except FeatureLibError as e: if options.traceback: raise log.error(e) font.save(output_font)
def buildCOLRv1(designspacePath, ttfPath, outTTFPath, saveWoff2, neutralOnly=False): import pathlib ttfPath = pathlib.Path(ttfPath) if outTTFPath is None: outTTFPath = ttfPath.parent / (ttfPath.stem + "-colrv1" + ttfPath.suffix) else: outTTFPath = pathlib.Path(outTTFPath) ttf = TTFont(ttfPath) axisTags = [axis.axisTag for axis in ttf["fvar"].axes] axisTagToIndex = {tag: index for index, tag in enumerate(axisTags)} globalAxisNames = { axis.axisTag for axis in ttf["fvar"].axes if not axis.flags & 0x0001 } assert globalAxisNames == { axisTag for axisTag in axisTags if axisTag[0] != "V" } vcFont = VarCoFont(designspacePath) # Update the glyf table to contain bounding boxes for color glyphs estimateCOLRv1BoundingBoxes(vcFont, ttf, neutralOnly) vcData, varStore = prepareVariableComponentData(vcFont, axisTags, globalAxisNames, neutralOnly) colrGlyphs = buildCOLRGlyphs(vcData, axisTagToIndex) ttf["COLR"] = buildCOLR(colrGlyphs, varStore=varStore) ttf.save(outTTFPath) ttf = TTFont(outTTFPath, lazy=True) # Load from scratch if saveWoff2: outWoff2Path = outTTFPath.parent / (outTTFPath.stem + ".woff2") ttf.flavor = "woff2" ttf.save(outWoff2Path)
def ttCompile(input, output, options): log.info('Compiling "%s" to "%s"...' % (input, output)) if options.useZopfli: from fontTools.ttLib import sfnt sfnt.USE_ZOPFLI = True ttf = TTFont(options.mergeFile, flavor=options.flavor, recalcBBoxes=options.recalcBBoxes, recalcTimestamp=options.recalcTimestamp, allowVID=options.allowVID) ttf.importXML(input) if not options.recalcTimestamp: # use TTX file modification time for head "modified" timestamp mtime = os.path.getmtime(input) ttf['head'].modified = timestampSinceEpoch(mtime) ttf.save(output)
def main(): parser = argparse.ArgumentParser() parser.add_argument("font") parser.add_argument("new_name") parser.add_argument("-o", "--out") args = parser.parse_args() font = TTFont(args.font) current_name = font_familyname(font) rename_font(font, args.new_name) if args.out: out = args.out else: out = args.font.replace(current_name.replace(" ", ""), args.new_name.replace(" ", "")) print("Saving font: {}".format(out)) font.save(out)
def main(args=None): if args is None: args = sys.argv[1:] if len(args) < 1: print("One argument, the input filename, must be provided.", file=sys.stderr) sys.exit(1) filename = args[0] outfilename = makeOutputFileName(filename, outputDir=None, extension='.woff2') print("Processing %s => %s" % (filename, outfilename)) font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False) font.flavor = "woff2" font.save(outfilename, reorderTables=False)
def main(): description = "Removes unwanted tables from one or more font files" parser = argparse.ArgumentParser(description=description) parser.add_argument("-t", "--tables", type=str, help="One or more comma separated table names") parser.add_argument("FONTPATH", nargs="+", help="One or more font files") args = parser.parse_args() tables = parse_tables(args.tables) if args.tables else None for fontpath in args.FONTPATH: ttfont = TTFont(fontpath) remove_tables(ttfont, tables) ttfont.save(fontpath)
def expose_ttc(src, dest): with open(src, "rb") as f: f.seek(8) count = unpack(">L", f.read(4)) for index in range(count): font = TTFont(src, fontNumber=index) if font.sfntVersion == "OTTO": ext = ".otf" elif font.sfntVersion == "\0\1\0\0": ext = ".ttf" elif font.sfntVersion == "wOFF": ext = ".woff" elif font.sfntVersion == "wOF2": ext = ".woff2" else: ext = "" name = font["name"].getDebugName(6) + ext # 6: postscript name font.save(join(dest, name))
def test_check_180(): """Does the number of glyphs in the loca table match the maxp table?""" from fontbakery.specifications.loca import com_google_fonts_check_180 as check test_font_path = os.path.join("data", "test", "nunito", "Nunito-Regular.ttf") test_font = TTFont(test_font_path) status, _ = list(check(test_font))[-1] assert status == PASS test_font = TTFont(test_font_path) test_font["loca"].locations.pop() test_file = io.BytesIO() test_font.save(test_file) test_font = TTFont(test_file) status, _ = list(check(test_font))[-1] assert status == FAIL
def rename_font_family(path): """ Adds suffix to font family it doesn't conflict with font installed on system, which could be in incorrect version. """ font = TTFont(path) name_table = font['name'] name = name_table.getName(nameID=1, platformID=3, platEncID=1, langID=0x409) assert name name = name.toUnicode() assert name.startswith('Font Awesome') name = name + ' (CopyQ)' name = name_table.setName(name, nameID=1, platformID=3, platEncID=1, langID=0x409) font.save(path)
def handle_font(font_name): font = TTFont(font_name) td = font['CFF '].cff.topDictIndex[0] no_subrs = lambda fd: hasattr(fd, 'Subrs') and len(fd.Subrs) > 0 priv_subrs = (hasattr(td, 'FDArray') and any(no_subrs(fd) for fd in td.FDArray)) if len(td.GlobalSubrs) > 0 or priv_subrs: print("Warning: There are subrs in %s" % font_name) orig_size = os.path.getsize(font_name) if decompress: from fontTools import subset options = subset.Options() options.desubroutinize = True subsetter = subset.Subsetter(options=options) subsetter.populate(glyphs=font.getGlyphOrder()) subsetter.subset(font) out_name = "%s.compressed%s" % os.path.splitext(font_name) compreff(font, verbose=verbose, **comp_kwargs) # save compressed font start_time = time.time() font.save(out_name) if verbose: print("Compiled and saved (took %gs)" % (time.time() - start_time)) if generate_cff: # save CFF version font['CFF '].cff.compile( open("%s.cff" % os.path.splitext(out_name)[0], 'w'), None) comp_size = os.path.getsize(out_name) print("Compressed to %s -- saved %s" % (os.path.basename(out_name), human_size(orig_size - comp_size))) if check: test_compression_integrity(filename, out_name) test_call_depth(out_name)
def main(args=None): if args is None: import sys args = sys.argv[1:] varfilename = args[0] locargs = args[1:] outfile = os.path.splitext(varfilename)[0] + '-instance.ttf' loc = {} for arg in locargs: tag,val = arg.split('=') assert len(tag) <= 4 loc[tag.ljust(4)] = float(val) print("Location:", loc) print("Loading GX font") varfont = TTFont(varfilename) fvar = varfont['fvar'] axes = {a.axisTag:(a.minValue,a.defaultValue,a.maxValue) for a in fvar.axes} # TODO Round to F2Dot14? loc = normalizeLocation(loc, axes) # Location is normalized now print("Normalized location:", loc) gvar = varfont['gvar'] for glyphname,variations in gvar.variations.items(): coordinates,_ = _GetCoordinates(varfont, glyphname) for var in variations: scalar = supportScalar(loc, var.axes) if not scalar: continue # TODO Do IUP / handle None items coordinates += GlyphCoordinates(var.coordinates) * scalar _SetCoordinates(varfont, glyphname, coordinates) print("Removing GX tables") for tag in ('fvar','avar','gvar'): if tag in varfont: del varfont[tag] print("Saving instance font", outfile) varfont.save(outfile)
def convertToTTF(otfPath, dest, report): temp = tempfile.mkstemp(suffix=".ttf")[1] tempDest = tempfile.mkstemp(suffix=".ttf")[1] font = OpenFont(otfPath, showUI=False) font.lib[shouldAddPointsInSplineConversionLibKey] = 1 font.kerning.clear() for attr in font.info.asDict().keys(): if attr not in defaultFontInfoAttributes: setattr(font.info, attr, None) result = font.generate(path=temp, format="ttf", decompose=False, checkOutlines=False, autohint=False, releaseMode=True, glyphOrder=font.glyphOrder) font.close() report.write(result) sourceFont = TTFont(temp) sourceFontWithTables = TTFont(otfPath) for table in [ "loca", "OS/2", "cmap", "name", "GSUB", "GPOS", "GDEF", "kern" ]: if table in sourceFontWithTables: sourceFont[table] = sourceFontWithTables[table] fixMetrics(sourceFont) sourceFont.save(tempDest) sourceFont.close() del sourceFont sourceFontWithTables.close() del sourceFontWithTables autohintOptions = getExtensionDefault(settingsIdentifier, defaultOptions) result = TTFAutohint(tempDest, dest, autohintOptions) report.writeItems(result) os.remove(temp) os.remove(tempDest)
def main(): argparser = argparse.ArgumentParser( description='Rename family and/or styles of font' ) a = lambda *args, **kwargs: argparser.add_argument(*args, **kwargs) a('-o', '--output', metavar='<file>', help='Output font file. Defaults to input file (overwrite)') a('--family', metavar='<name>', help='Rename family to <name>') a('--style', metavar='<name>', help='Rename style to <name>') a('--google-style', action='store_true', help='Rename style names to Google Fonts standards') a('input', metavar='<file>', help='Input font file') args = argparser.parse_args() infile = args.input outfile = args.output or infile font = TTFont(infile, recalcBBoxes=False, recalcTimestamp=False) editCount = 0 try: if args.family: editCount += 1 setFamilyName(font, args.family) if args.style: editCount += 1 setStyleName(font, args.style) if args.google_style: editCount += 1 renameStylesGoogleFonts(font) if editCount == 0: print("no rename options provided", file=sys.stderr) argparser.print_help(sys.stderr) sys.exit(1) return font.save(outfile) finally: font.close()
def main(): filename = sys.argv[1] font = TTFont(filename, recalcBBoxes=False) fontName = font["name"] originalFontUniqueID = fontName.getName(3, 1, 0, 0).toUnicode() originalFontFullname = fontName.getName(4, 1, 0, 0).toUnicode() originalFontPreferredStyle = fontName.getName(17, 1, 0, 0).toUnicode() for entry in fontName.names: nameID = entry.nameID platformID = entry.platformID platEncID = entry.platEncID langID = entry.langID if langID in [1028, 1041, 2052, 3076]: string = (entry.toUnicode().replace( " CL", " CL Nerd Font").replace(" TC", " TC Nerd Font").replace( " J", " J Nerd Font").replace(" SC", " SC Nerd Font").replace( " HC", " HC Nerd Font")) fontName.setName(string, nameID, platformID, platEncID, langID) elif nameID in [1, 16]: string = originalFontUniqueID.replace( f" {originalFontPreferredStyle}", " Nerd Font") fontName.setName(string, nameID, platformID, platEncID, langID) elif nameID == 3: string = originalFontUniqueID.replace( f" {originalFontPreferredStyle}", f" Nerd Font {originalFontPreferredStyle}", ) fontName.setName(string, nameID, platformID, platEncID, langID) elif nameID == 6: fontName.setName(originalFontFullname, nameID, platformID, platEncID, langID) font.save(filename) font.close()
def main(args=None): parser = argparse.ArgumentParser() parser.add_argument("input", metavar="INPUT") parser.add_argument("-o", "--output") parser.add_argument("-e", "--max-error", type=float, default=MAX_ERR) parser.add_argument("--post-format", type=float, default=POST_FORMAT) parser.add_argument("--keep-direction", dest='reverse_direction', action='store_false') options = parser.parse_args(args) output = options.output or makeOutputFileName( options.input, outputDir=None, extension='.ttf') font = TTFont(options.input) otf_to_ttf(font, post_format=options.post_format, max_err=options.max_error, reverse_direction=options.reverse_direction) font.save(output)
def main(): parser = argparse.ArgumentParser() parser.add_argument("font", help="Path to font") parser.add_argument("-o", "--out", help="Output path for fixed font") parser.add_argument( "--include-source-fixes", action="store_true", help="Fix font issues that should be fixed in the source files.", ) args = parser.parse_args() font = TTFont(args.font) fix_font(font, args.include_source_fixes) if args.out: font.save(args.out) else: font.save(font.reader.file.name + ".fix")
def ttCompile(input, output, options): if not options.quiet: print('Compiling "%s" to "%s"...' % (input, output)) ttf = TTFont(options.mergeFile, recalcBBoxes=options.recalcBBoxes, recalcTimestamp=options.recalcTimestamp, verbose=options.verbose, allowVID=options.allowVID) ttf.importXML(input, quiet=options.quiet) if not options.recalcTimestamp: # use TTX file modification time for head "modified" timestamp mtime = os.path.getmtime(input) ttf['head'].modified = timestampSinceEpoch(mtime) ttf.save(output) if options.verbose: import time print("finished at", time.strftime("%H:%M:%S", time.localtime(time.time())))
def main(): args = parser.parse_args() for font_path in args.fonts: nametable = nametable_from_filename(font_path) font = TTFont(font_path) font_filename = ntpath.basename(font_path) font['name'] = nametable style = font_filename[:-4].split('-')[-1] font['OS/2'].usWeightClass = set_usWeightClass(style) font['OS/2'].fsSelection = set_fsSelection(font['OS/2'].fsSelection, style) win_style = font['name'].getName(2, 3, 1, 1033).string.decode('utf_16_be') font['head'].macStyle = set_macStyle(win_style) font.save(font_path + '.fix') print('font saved %s.fix' % font_path)
def subsetFontFT(path, unicodes, quran=False): from fontTools import subset font = TTFont(path, recalcTimestamp=False) options = subset.Options() options.set(layout_features='*', name_IDs='*', name_languages='*', notdef_outline=True, glyph_names=True) subsetter = subset.Subsetter(options=options) subsetter.populate(unicodes=unicodes) subsetter.subset(font) if quran: font["OS/2"].sTypoAscender = font["hhea"].ascent = font["head"].yMax font.save(path)
def hinting_stats(font): """ Return file size differences for a hinted font compared to an dehinted version of same file """ from io import BytesIO from dehinter.font import dehint from fontTools.ttLib import TTFont from fontTools.subset import main as pyftsubset from fontbakery.profiles.shared_conditions import (is_ttf, is_cff, is_cff2) hinted_size = os.stat(font).st_size ttFont = TTFont(font) if is_ttf(ttFont): dehinted_buffer = BytesIO() dehint(ttFont, verbose=False) ttFont.save(dehinted_buffer) dehinted_buffer.seek(0) dehinted_size = len(dehinted_buffer.read()) elif is_cff(ttFont) or is_cff2(ttFont): ext = os.path.splitext(font)[1] tmp = font.replace(ext, "-tmp-dehinted%s" % ext) args = [ font, "--no-hinting", "--glyphs=*", "--ignore-missing-glyphs", "--no-notdef-glyph", "--no-recommended-glyphs", "--no-layout-closure", "--layout-features=*", "--no-desubroutinize", "--name-languages=*", "--glyph-names", "--no-prune-unicode-ranges", "--output-file=%s" % tmp ] pyftsubset(args) dehinted_size = os.stat(tmp).st_size os.remove(tmp) else: return None return { "dehinted_size": dehinted_size, "hinted_size": hinted_size, }
def buildVarC( designspacePath, ttfPath, outTTFPath, doTTX, saveWoff2, neutralOnly=False ): import pathlib registerCustomTableClass("VarC", "rcjktools.table_VarC", "table_VarC") ttfPath = pathlib.Path(ttfPath) if outTTFPath is None: outTTFPath = ttfPath.parent / (ttfPath.stem + "-varc" + ttfPath.suffix) else: outTTFPath = pathlib.Path(outTTFPath) ttf = TTFont(ttfPath) axisTags = [axis.axisTag for axis in ttf["fvar"].axes] globalAxisNames = {axisTag for axisTag in axisTags if axisTag[0] != "V"} vcFont = VarCoFont(designspacePath) vcData, allLocations, neutralGlyphNames = vcFont.extractVarCoData( globalAxisNames, neutralOnly ) if neutralGlyphNames: gvarTable = ttf["gvar"] for glyphName in neutralGlyphNames: del gvarTable.variations[glyphName] buildVarCTable(ttf, vcData, allLocations) if doTTX: outTTXPath = outTTFPath.parent / (outTTFPath.stem + "-before.ttx") ttf.saveXML(outTTXPath, tables=["VarC"]) ttf.save(outTTFPath) ttf = TTFont(outTTFPath, lazy=True) # Load from scratch if doTTX: outTTXPath = outTTFPath.parent / (outTTFPath.stem + "-after.ttx") ttf.saveXML(outTTXPath, tables=["VarC"]) if saveWoff2: outWoff2Path = outTTFPath.parent / (outTTFPath.stem + ".woff2") ttf.flavor = "woff2" ttf.save(outWoff2Path)
def install_fonts(): """ Install fonts to ~/.fonts. The fonts being installed is Titillium Web ~ https://fonts.google.com/specimen/Titillium+Web Open Source Approved fonts. # TODO support for SystemWide Installation :return: True if installation successful, else False """ sys_font_dir = os.path.join(os.path.expanduser('~'), '.fonts') if not os.path.exists(sys_font_dir): os.makedirs(sys_font_dir) try: from fontTools.ttLib import TTFont except ModuleNotFoundError as e: logging.error( "Error Installing the fonts. " "You might have to manually install the fonts" "Titillium Web : " "https://fonts.google.com/specimen/Titillium+Web " "Error: {}".format(e) ) return False font_dir = os.path.join(os.path.abspath( os.path.dirname(os.path.dirname(__file__))), 'ui', 'fonts') try: fonts = os.listdir(font_dir) for i in fonts: font = TTFont(os.path.join(font_dir, i)) font.save(os.path.join(sys_font_dir, i)) return True except Exception as e: logging.error( "Error Installing the fonts. " "You might have to manually install the fonts" "Titillium Web : " "https://fonts.google.com/specimen/Titillium+Web " "Error: {}".format(e) ) return False
def main(): parser = argparse.ArgumentParser( description= "Create a version of Amiri with colored marks using COLR/CPAL tables.") parser.add_argument("infile", metavar="INFILE", type=str, help="input font to process") parser.add_argument("outfile", metavar="OUTFILE", type=str, help="output font to write") args = parser.parse_args() font = TTFont(args.infile, recalcTimestamp=False) rename(font) font.save(args.outfile)
def main(args=None): if args is None: args = sys.argv[1:] if len(args) < 1: print("usage: gzip.py " "INPUT.ttx [OUTPUT.ttf]", file=sys.stderr) return 1 infile = args[0] if len(args) > 1: outfile = args[1] else: outfile = makeOutputFileName(infile, None, ".ttf") font = TTFont() font.importXML(infile) svg = font["SVG "] svg.compressed = True font.save(outfile)
def main(): argparser = argparse.ArgumentParser(description='Fix names in variable font') a = lambda *args, **kwargs: argparser.add_argument(*args, **kwargs) a('-o', '--output', metavar='<file>', help='Output font file. Defaults to input file (overwrite)') a('input', metavar='<file>', help='Input font file') args = argparser.parse_args() infile = args.input outfile = args.output or infile font = TTFont(infile, recalcBBoxes=False, recalcTimestamp=False) fullName = fix_fullname(font) fix_unique_id(font, fullName) clear_subfamily_name(font) font.save(outfile) font.close()
def main(args=None): """Optimize the layout tables of an existing font.""" from argparse import ArgumentParser from fontTools import configLogger parser = ArgumentParser( prog="otlLib.optimize", description=main.__doc__, formatter_class=RawTextHelpFormatter, ) parser.add_argument("font") parser.add_argument("-o", metavar="OUTPUTFILE", dest="outfile", default=None, help="output file") parser.add_argument( "--gpos-compression-level", help=COMPRESSION_LEVEL.help, default=COMPRESSION_LEVEL.default, choices=list(range(10)), type=int, ) logging_group = parser.add_mutually_exclusive_group(required=False) logging_group.add_argument("-v", "--verbose", action="store_true", help="Run more verbosely.") logging_group.add_argument("-q", "--quiet", action="store_true", help="Turn verbosity off.") options = parser.parse_args(args) configLogger(level=( "DEBUG" if options.verbose else "ERROR" if options.quiet else "INFO")) font = TTFont(options.font) compact(font, options.gpos_compression_level) font.save(options.outfile or options.font)
def encode_glyph(ttf_path: str, glyph_name: str, unicode: str): font = TTFont(ttf_path) go = font.getGlyphOrder() if glyph_name not in go: font.close() print(f"Glyph '{glyph_name}' not in font, skipping.") return if unicode.startswith("U+"): try: unicode_number = int(unicode[2:], 16) except: font.close() print(f"Could not parse Unicode value '{glyph_name}', " "it must start with 'U+' followed by the value as " "a hexadecimal number.") return tsiv = font["TSIV"] data = tsiv.data.decode() # data = data.split("\0") lines = data.splitlines() seen_glyph_def = False for i, line in enumerate(lines): if line.strip().startswith("DEF_GLYPH"): seen_glyph_def = True continue if not seen_glyph_def: continue index = go.index(glyph_name) entry = (f'DEF_GLYPH "{glyph_name}" ID {index} ' f"UNICODE {unicode_number} TYPE BASE END_GLYPH") lines.insert(i, entry) print(f"Inserting entry at line {i}: \n {entry}") set_TSIV(tsiv, lines) font.save(ttf_path) break font.close()
def main(): parser = argparse.ArgumentParser() parser.add_argument("font_path") parser.add_argument("-f", "--remove-incompatible-hinting", action="store_true") args = parser.parse_args() font = TTFont(args.font_path) if "TSI1" not in font: print("Font is not VTT hinted") return glyph_names = font.getGlyphOrder() incompatible_glyphs = [] for gid, glyph_name in enumerate(glyph_names): try: data = get_glyph_assembly(font, glyph_name) except KeyError: pass try: program, components = make_glyph_program(data, glyph_name) except: incompatible_glyphs.append((gid, glyph_name)) if not incompatible_glyphs: print("All glyphs compile") return print("Following glyphs cannot compile using vttLib:") print("GlyphID GlyphName") for gid, glyph_name in incompatible_glyphs: print(gid, glyph_name) if not args.remove_incompatible_hinting: return if args.remove_incompatible_hinting: for _, glyph_name in incompatible_glyphs: font['TSI1'].glyphPrograms[glyph_name] = "" font['TSI3'].glyphPrograms[glyph_name] = "" font.save(args.font_path + ".fix") print("Incompatible glyph hints have been removed")
def rexify(font, out): ''' Rexify font. This will take all inaccessible glyphs from the font, place them in a PUA, and modify the CMAPs so that these glyphs are publicly accessible. This will also generate the required tables for ReX. ''' # Make glyphs accessible. ttfont = TTFont(font, recalcBBoxes=False) make_accessible(ttfont) ttfont.save(out + os.path.basename(font)) gen_constants(ttfont, out) gen_glyphs(ttfont, out) gen_kerning(ttfont, out) gen_symbols(ttfont, out) gen_variants(ttfont, out)
def main(): filepath = sys.argv[1] tt = TTFont(filepath) # remove & replace last item in AXES to work for italic font if "Italic" in filepath: AXES.pop() AXES.append( dict( tag="ital", name="Italic", ordering=4, values=[ dict(nominalValue=1, name="Italic"), ], )) buildStatTable(tt, AXES) update_fvar(tt) tt.save(filepath) print(f"Added STAT table to {filepath}")