def find_and_replace_ttx(ttx_path, find_string, replace_string, tables=['name']): count = 0 # 1. modify 'name' table if 'name' in tables: tree = parse(ttx_path) root = tree.getroot() for child in root.find('name'): if child.text.find(find_string) != -1: new_text = child.text.replace(find_string, replace_string) child.text = new_text count += 1 tree.write(ttx_path) # 2. modify 'CFF ' table if 'CFF ' in tables: CFF_elements = ['version', 'Notice', 'Copyright', 'FullName', 'FamilyName', 'Weight'] tt_font = TTFont() tt_font.importXML(ttx_path) font_dict = tt_font['CFF '].cff.topDictIndex.items[0] for element in CFF_elements: text = getattr(font_dict, element) if text.find(find_string) != -1: new_text = text.replace(find_string, replace_string) setattr(font_dict, element, new_text) count += 1 tt_font.saveXML(ttx_path) # done return count
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, 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 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 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 test_bit6_draw_to_pen_issue1771(self): # https://github.com/fonttools/fonttools/issues/1771 font = TTFont(sfntVersion="\x00\x01\x00\x00") # glyph00003 contains a bit 6 flag on the first point, # which triggered the issue font.importXML(GLYF_TTX) glyfTable = font['glyf'] pen = RecordingPen() glyfTable["glyph00003"].draw(pen, glyfTable=glyfTable) expected = [('moveTo', ((501, 1430),)), ('lineTo', ((683, 1430),)), ('lineTo', ((1172, 0),)), ('lineTo', ((983, 0),)), ('lineTo', ((591, 1193),)), ('lineTo', ((199, 0),)), ('lineTo', ((12, 0),)), ('lineTo', ((501, 1430),)), ('closePath', ()), ('moveTo', ((249, 514),)), ('lineTo', ((935, 514),)), ('lineTo', ((935, 352),)), ('lineTo', ((249, 352),)), ('lineTo', ((249, 514),)), ('closePath', ())] self.assertEqual(pen.value, expected)
def test_max_ctx_calc_features_ttx(file_name, max_context): ttx_path = os.path.join(os.path.dirname(__file__), 'data', '{}.ttx'.format(file_name)) font = TTFont() font.importXML(ttx_path) assert maxCtxFont(font) == max_context
def main(args): #assume input is .ttx file #TODO:input is .ttf file input = args[0] ttf = TTFont() ttf.importXML(input,quiet=True) bytecode_font = BytecodeFont() constructCVTTable(ttf['cvt '].values) # TODO:for now just analyze the font program file, later # should add the prep and all the glyphs body = Body(bytecode_font, 'fpgm', ttf) constructInstructions(body) constructSuccessor(body) constructPredecessor(body) current_state = Environment() executor = AbstractExecutor(bytecode_font) instruction = body.instructions[0] while instruction in body.successors: executor.execute(instruction, current_state) instruction.prettyPrint() instruction = body.successors[instruction][0] for key,value in bytecode_font.function_table.items(): print('Function #{0}:'.format(key)) for instruction in value: instruction.prettyPrint()
def main(): if EXT == 'ttf': # convert source to xml print('[+] Loading {}..'.format(args.filename)) font = TTFont(args.filename) font.saveXML(OUT_TTX) # parsing XML print('[+] Generating {} file...'.format(OUT_TTX)) font_tree = ET.parse(OUT_TTX) font_root = font_tree.getroot() for letter in font_root.iter('map'): if letter.attrib['name'] in subs.keys(): letter.set('code', hex(ord(subs[letter.attrib['name']]))) font_tree.write(OUT_TTX) # convert to ttf print('[+] Generating {} file...'.format(OUT_TTF)) font = TTFont() font.importXML(OUT_TTX) font.save(OUT_TTF) elif EXT == 'ttx': # load the xml font_root = ET.parse(args.filename).getroot() for letter in asci: for item in font_root.iter('map'): if item.attrib['code'] == hex(ord(letter)): subs[item.attrib['name']] = letter if args.t: print('\n' + ''.join(subs[letter] if letter in subs.keys() else letter for letter in args.t))
def test_ttcompile_timestamp_calcs(inpath, outpath1, outpath2, tmpdir): inttx = os.path.join("Tests", "ttx", "data", inpath) outttf1 = tmpdir.join(outpath1) outttf2 = tmpdir.join(outpath2) options = ttx.Options([], 1) # build with default options = do not recalculate timestamp ttx.ttCompile(inttx, str(outttf1), options) # confirm that font was built assert outttf1.check(file=True) # confirm that timestamp is same as modified time on ttx file mtime = os.path.getmtime(inttx) epochtime = timestampSinceEpoch(mtime) ttf = TTFont(str(outttf1)) assert ttf["head"].modified == epochtime # reset options to recalculate the timestamp and compile new font options.recalcTimestamp = True ttx.ttCompile(inttx, str(outttf2), options) # confirm that font was built assert outttf2.check(file=True) # confirm that timestamp is more recent than modified time on ttx file mtime = os.path.getmtime(inttx) epochtime = timestampSinceEpoch(mtime) ttf = TTFont(str(outttf2)) assert ttf["head"].modified > epochtime # --no-recalc-timestamp will keep original timestamp options.recalcTimestamp = False ttx.ttCompile(inttx, str(outttf2), options) assert outttf2.check(file=True) inttf = TTFont() inttf.importXML(inttx) assert inttf["head"].modified == TTFont(str(outttf2))["head"].modified
def load_ttx(ttx): f = StringIO() f.write(ttx) f.seek(0) font = TTFont() font.importXML(f) return font
def load_ttx(ttx): f = UnicodeIO() f.write(ttx) f.seek(0) font = TTFont() font.importXML(f) return font
def test_virtualGlyphId(): otfpath = os.path.join(DATA_DIR, "TestVGID-Regular.otf") ttxpath = os.path.join(DATA_DIR, "TestVGID-Regular.ttx") otf = TTFont(otfpath) ttx = TTFont() ttx.importXML(ttxpath) with open(ttxpath, encoding="utf-8") as fp: xml = normalize_TTX(fp.read()).splitlines() for font in (otf, ttx): GSUB = font["GSUB"].table assert GSUB.LookupList.LookupCount == 37 lookup = GSUB.LookupList.Lookup[32] assert lookup.LookupType == 8 subtable = lookup.SubTable[0] assert subtable.LookAheadGlyphCount == 1 lookahead = subtable.LookAheadCoverage[0] assert len(lookahead.glyphs) == 46 assert "glyph00453" in lookahead.glyphs out = io.StringIO() font.saveXML(out) outxml = normalize_TTX(out.getvalue()).splitlines() assert xml == outxml
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 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 _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
def ttf_path(tmp_path): # $(dirname $0)/../ttLib/data ttLib_data = pathlib.Path(__file__).parent.parent / "ttLib" / "data" font = TTFont() font.importXML(ttLib_data / "TestTTF-Regular.ttx") font_path = tmp_path / "TestTTF-Regular.ttf" font.save(font_path) return font_path
def setUp(self): font = TTFont(None, lazy=False, recalcBBoxes=True, verbose=False, allowVID=False) font.importXML(self.operator.path, quiet=True) self.font = font
def setUp(self): # TODO: Need somebody to check this options font = TTFont(None, lazy=False, recalcBBoxes=True, verbose=False, allowVID=False) font.importXML(self.path, quiet=True) self.font = font
def test_recalc_empty(self): font = TTFont() font.importXML(os.path.join(DATA_DIR, '_h_h_e_a_recalc_empty.ttx')) hhea = font['hhea'] hhea.recalc(font) self.assertEqual(hhea.advanceWidthMax, 600) self.assertEqual(hhea.minLeftSideBearing, 0) self.assertEqual(hhea.minRightSideBearing, 0) self.assertEqual(hhea.xMaxExtent, 0)
def isTTX(pathOrFile): from fontTools.ttLib import TTFont try: font = TTFont() font.importXML(pathOrFile) del font except Exception: return False return True
def test_recalc_empty(self): font = TTFont() font.importXML(os.path.join(DATA_DIR, '_v_h_e_a_recalc_empty.ttx')) vhea = font['vhea'] vhea.recalc(font) self.assertEqual(vhea.advanceHeightMax, 900) self.assertEqual(vhea.minTopSideBearing, 0) self.assertEqual(vhea.minBottomSideBearing, 0) self.assertEqual(vhea.yMaxExtent, 0)
def test_draw_vs_drawpoints(self): font = TTFont(sfntVersion="\x00\x01\x00\x00") font.importXML(GLYF_TTX) glyfTable = font['glyf'] pen1 = RecordingPen() pen2 = RecordingPen() glyfTable["glyph00003"].draw(pen1, glyfTable) glyfTable["glyph00003"].drawPoints(PointToSegmentPen(pen2), glyfTable) self.assertEqual(pen1.value, pen2.value)
def isTTX(pathOrFile): from fontTools.ttLib import TTFont, TTLibError try: font = TTFont() font.importXML(pathOrFile) del font except TTLibError: return False return True
def test_compile_empty_table(self): font = TTFont(sfntVersion="\x00\x01\x00\x00") font.importXML(GLYF_TTX) glyfTable = font['glyf'] # set all glyphs to zero contours glyfTable.glyphs = {glyphName: Glyph() for glyphName in font.getGlyphOrder()} glyfData = glyfTable.compile(font) self.assertEqual(glyfData, b"\x00") self.assertEqual(list(font["loca"]), [0] * (font["maxp"].numGlyphs+1))
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) ttf.save(output) if options.verbose: import time print("finished at", time.strftime("%H:%M:%S", time.localtime(time.time())))
def test_sfntVersionFromTTX(): # https://github.com/fonttools/fonttools/issues/2370 font = TTFont() assert font.sfntVersion == "\x00\x01\x00\x00" ttx = io.StringIO(ttxOTF) # Font is "empty", TTX file will determine sfntVersion font.importXML(ttx) assert font.sfntVersion == "OTTO" ttx = io.StringIO(ttxTTF) # Font is not "empty", sfntVersion in TTX file will be ignored font.importXML(ttx) assert font.sfntVersion == "OTTO"
def test_FDSelect_format_4(self): ttx_path = self.getpath('TestFDSelect4.ttx') font = TTFont(recalcBBoxes=False, recalcTimestamp=False) font.importXML(ttx_path) self.temp_dir() save_path = os.path.join(self.tempdir, 'TestOTF.otf') font.save(save_path) font2 = TTFont(save_path) topDict2 = font2["CFF2"].cff.topDictIndex[0] self.assertEqual(topDict2.FDSelect.format, 4) self.assertEqual(topDict2.FDSelect.gidArray, [0, 0, 1])
def conv_otf(self): from fontTools.ttLib import TTFont, newTable info("creating squished font, pls wait") mul = 1.1 font = TTFont(self.otf_src) baseAsc = font["OS/2"].sTypoAscender baseDesc = font["OS/2"].sTypoDescender font["hhea"].ascent = round(baseAsc * mul) font["hhea"].descent = round(baseDesc * mul) font["OS/2"].usWinAscent = round(baseAsc * mul) font["OS/2"].usWinDescent = round(baseDesc * mul) * -1 xfn = "softchat.xml" font.saveXML(xfn, tables=["name"]) with open(xfn, "r", encoding="utf-8") as f: xml = f.read() xml = xml.replace("Noto Sans", "Squished Noto Sans") xml = xml.replace("NotoSans", "SquishedNotoSans") with open(xfn, "w", encoding="utf-8") as f: f.write(xml) font["name"] = newTable("name") font.importXML(xfn) os.unlink(xfn) try: del font["post"].mapping["Delta#1"] except: pass for fp in self.otf_mod: try: fdir = fp.rsplit(os.sep, 1)[0] os.makedirs(fdir, exist_ok=True) with open(fp, "wb") as f: f.write(b"h") os.unlink(fp) break except: fp = None if not fp: err = "could not write modified font to any of the following locations:" raise Exception("\n ".join([err] + self.otf_mod)) self.otf_mod = fp info(f"writing {self.otf_mod}") font.save(self.otf_mod)
def test_recalc_timestamp_otf(self): ttxpath = self.getpath("TestOTF-Regular.ttx") font = TTFont() font.importXML(ttxpath) modified = font['head'].modified _, fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") # by default, the subsetter does not recalculate the modified timestamp subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) self.assertEqual(modified, TTFont(subsetpath)['head'].modified) subset.main([fontpath, "--recalc-timestamp", "--output-file=%s" % subsetpath, "*"]) self.assertLess(modified, TTFont(subsetpath)['head'].modified)
def test_CFF_deepcopy(self): """Test that deepcopying a TTFont with a CFF table does not recurse infinitely.""" ttx_path = os.path.join( os.path.dirname(__file__), "..", "varLib", "data", "master_ttx_interpolatable_otf", "TestFamily2-Master0.ttx", ) font = TTFont(recalcBBoxes=False, recalcTimestamp=False) font.importXML(ttx_path) copy.deepcopy(font)
def test_blend_programToCommands(self): ttx_path = self.getpath('TestCFF2Widths.ttx') ttf_font = TTFont(recalcBBoxes=False, recalcTimestamp=False) ttf_font.importXML(ttx_path) fontGlyphList = ttf_font.getGlyphOrder() topDict = ttf_font['CFF2'].cff.topDictIndex[0] charstrings = topDict.CharStrings for glyphName in fontGlyphList: cs = charstrings[glyphName] cs.decompile() cmds = programToCommands(cs.program, getNumRegions=cs.getNumRegions) program = commandsToProgram(cmds) self.assertEqual(program, cs.program)
def ttx2otf(ttx_path, otf_path=None): """Generate an ``.otf`` font from a ``.ttx`` file. :param str ttx_path: Path of the .ttx font source. :param str otf_path: Path of the target .otf font. """ # make otf path if not otf_path: otf_path = '%s.otf' % os.path.splitext(ttx_path)[0] # save otf font tt = TTFont() tt.importXML(ttx_path) tt.save(otf_path)
def ttx2otf(ttx_path, otf_path=None): """Generate an .otf font from a .ttx file. ttx_path: Path of the .ttx font source. otf_path: Path of the target .otf font. """ # make otf path if not otf_path: otf_path = '%s.otf' % os.path.splitext(ttx_path)[0] # save otf font with SuppressPrint(): tt = TTFont() tt.verbose = False tt.importXML(ttx_path) tt.save(otf_path)
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) ttf.importXML(input) if options.recalcTimestamp is None and 'head' in ttf: # use TTX file modification time for head "modified" timestamp mtime = os.path.getmtime(input) ttf['head'].modified = timestampSinceEpoch(mtime) ttf.save(output)
def extractFontFromTTX(pathOrFile, destination, doGlyphs=True, doInfo=True, doKerning=True, customFunctions=[]): from fontTools.ttLib import TTFont, TTLibError source = TTFont() source.importXML(pathOrFile) if doInfo: extractOpenTypeInfo(source, destination) if doGlyphs: extractOpenTypeGlyphs(source, destination) if doKerning: kerning, groups = extractOpenTypeKerning(source, destination) destination.groups.update(groups) destination.kerning.clear() destination.kerning.update(kerning) for function in customFunctions: function(source, destination) source.close()
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 test_topDict_set_Encoding(self): ttx_path = self.getpath('TestOTF.ttx') font = TTFont(recalcBBoxes=False, recalcTimestamp=False) font.importXML(ttx_path) topDict = font["CFF "].cff.topDictIndex[0] encoding = [".notdef"] * 256 encoding[0x20] = "space" topDict.Encoding = encoding self.temp_dir() save_path = os.path.join(self.tempdir, 'TestOTF.otf') font.save(save_path) font2 = TTFont(save_path) topDict2 = font2["CFF "].cff.topDictIndex[0] self.assertEqual(topDict2.Encoding[32], "space")
def test_recalc_max_context(self): ttxpath = self.getpath("Lobster.subset.ttx") font = TTFont() font.importXML(ttxpath) max_context = font['OS/2'].usMaxContext _, fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") # by default, the subsetter does not recalculate the usMaxContext subset.main([fontpath, "--drop-tables+=GSUB,GPOS", "--output-file=%s" % subsetpath]) self.assertEqual(max_context, TTFont(subsetpath)['OS/2'].usMaxContext) subset.main([fontpath, "--recalc-max-context", "--drop-tables+=GSUB,GPOS", "--output-file=%s" % subsetpath]) self.assertEqual(0, TTFont(subsetpath)['OS/2'].usMaxContext)
def test_recalc_bounds_otf(self): ttxpath = self.getpath("TestOTF-Regular.ttx") font = TTFont() font.importXML(ttxpath) head = font['head'] bounds = [head.xMin, head.yMin, head.xMax, head.yMax] _, fontpath = self.compile_font(ttxpath, ".otf") subsetpath = self.temp_path(".otf") # by default, the subsetter does not recalculate the bounding box subset.main([fontpath, "--output-file=%s" % subsetpath, "*"]) head = TTFont(subsetpath)['head'] self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax]) subset.main([fontpath, "--recalc-bounds", "--output-file=%s" % subsetpath, "*"]) head = TTFont(subsetpath)['head'] bounds = [132, 304, 365, 567] self.assertEqual(bounds, [head.xMin, head.yMin, head.xMax, head.yMax])
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 test_blend_round_trip(self): ttx_path = self.getpath('TestSparseCFF2VF.ttx') ttf_font = TTFont(recalcBBoxes=False, recalcTimestamp=False) ttf_font.importXML(ttx_path) fontGlyphList = ttf_font.getGlyphOrder() topDict = ttf_font['CFF2'].cff.topDictIndex[0] charstrings = topDict.CharStrings for glyphName in fontGlyphList: cs = charstrings[glyphName] cs.decompile() cmds = programToCommands(cs.program, getNumRegions=cs.getNumRegions) cmds_g = generalizeCommands(cmds) cmds = specializeCommands(cmds_g, generalizeFirst=False) program = commandsToProgram(cmds) self.assertEqual(program, cs.program) program = specializeProgram(program, getNumRegions=cs.getNumRegions) self.assertEqual(program, cs.program) program_g = generalizeProgram(program, getNumRegions=cs.getNumRegions) program = commandsToProgram(cmds_g) self.assertEqual(program, program_g)
def _open_font(path, master_finder): # load TTFont masters from given 'path': this can be either a .TTX or an # OpenType binary font; or if neither of these, try use the 'master_finder' # callable to resolve the path to a valid .TTX or OpenType font binary. from fontTools.ttx import guessFileType master_path = os.path.normpath(path) tp = guessFileType(master_path) if tp is None: # not an OpenType binary/ttx, fall back to the master finder. master_path = master_finder(master_path) tp = guessFileType(master_path) if tp in ("TTX", "OTX"): font = TTFont() font.importXML(master_path) elif tp in ("TTF", "OTF", "WOFF", "WOFF2"): font = TTFont(master_path) else: raise VarLibError("Invalid master path: %r" % master_path) return font
def compile_font(self, path, suffix): savepath = self.temp_path(suffix=suffix) font = TTFont(recalcBBoxes=False, recalcTimestamp=False) font.importXML(path) font.save(savepath, reorderTables=None) return font, savepath
def process(jobs, options): for (input, origin) in jobs: tt = TTFont() tt.importXML(input, quiet=None) bc = BytecodeContainer(tt) if (options.allGlyphs): glyphs = filter(lambda x: x != 'fpgm' and x != 'prep', bc.tag_to_programs.keys()) else: glyphs = map(lambda x: 'glyf.'+x, options.glyphs) if options.outputIR or options.reduceFunctions: ae, called_functions = analysis(bc, glyphs) if (options.outputPrep): print ("PREP:") if (options.outputIR): if 'prep' in bc.tag_to_programs: bc.print_IR(bc.IRs['prep']) else: print (" <no prep>") else: bc.tag_to_programs['prep'].body.pretty_print() print () if (options.outputFunctions): for key, value in bc.function_table.items(): print ("Function #%d:" % (key)) if (options.outputIR): tag = "fpgm_%s" % key if tag in bc.IRs: bc.print_IR(bc.IRs[tag]) else: print (" <not executed, no IR>") else: value.body.pretty_print() print () if (options.outputGlyfPrograms): for glyph in glyphs: print ("%s:" % glyph) if (options.outputIR): bc.print_IR(bc.IRs[glyph]) else: bc.tag_to_programs[glyph].body.pretty_print() print () if (options.outputCallGraph): print ("called function set:") print (called_functions) print ("call graph (function, # calls to):") for item in ae.global_function_table.items(): print (item) if (options.outputState): ae.environment.pretty_print() if (options.outputCVT): print("CVT = ", ae.environment.cvt) if (options.outputMaxStackDepth): print("Max Stack Depth =", ae.maximum_stack_depth) if (options.reduceFunctions): function_set = bc.function_table.keys() unused_functions = [item for item in function_set if item not in called_functions] bc.removeFunctions(unused_functions) bc.updateTTFont(tt) output = "Reduced"+origin if (options.outputXML): output = makeOutputFileName(output, ".ttx") tt.saveXML(output) else: output = makeOutputFileName(output, ".ttf") tt.save(output) if type(input) is file: input.close()
def makeLookup1(): # make a variation of the shell TTX data f = open(shellSourcePath) ttxData = f.read() f.close() ttxData = ttxData.replace("__familyName__", "gsubtest-lookup1") tempShellSourcePath = shellSourcePath + ".temp" f = open(tempShellSourcePath, "wb") f.write(ttxData) f.close() # compile the shell shell = TTFont(sfntVersion="OTTO") shell.importXML(tempShellSourcePath) shell.save(shellTempPath) os.remove(tempShellSourcePath) # load the shell shell = TTFont(shellTempPath) # grab the PASS and FAIL data hmtx = shell["hmtx"] glyphSet = shell.getGlyphSet() failGlyph = glyphSet["F"] failGlyph.decompile() failGlyphProgram = list(failGlyph.program) failGlyphMetrics = hmtx["F"] passGlyph = glyphSet["P"] passGlyph.decompile() passGlyphProgram = list(passGlyph.program) passGlyphMetrics = hmtx["P"] # grab some tables hmtx = shell["hmtx"] cmap = shell["cmap"] # start the glyph order existingGlyphs = [".notdef", "space", "F", "P"] glyphOrder = list(existingGlyphs) # start the CFF cff = shell["CFF "].cff globalSubrs = cff.GlobalSubrs topDict = cff.topDictIndex[0] topDict.charset = existingGlyphs private = topDict.Private charStrings = topDict.CharStrings charStringsIndex = charStrings.charStringsIndex features = sorted(mapping) # build the outline, hmtx and cmap data cp = baseCodepoint for index, tag in enumerate(features): # tag.pass glyphName = "{0!s}.pass".format(tag) glyphOrder.append(glyphName) addGlyphToCFF( glyphName=glyphName, program=passGlyphProgram, private=private, globalSubrs=globalSubrs, charStringsIndex=charStringsIndex, topDict=topDict, charStrings=charStrings ) hmtx[glyphName] = passGlyphMetrics for table in cmap.tables: if table.format == 4: table.cmap[cp] = glyphName else: raise NotImplementedError, "Unsupported cmap table format: {0:d}".format(table.format) cp += 1 # tag.fail glyphName = "{0!s}.fail".format(tag) glyphOrder.append(glyphName) addGlyphToCFF( glyphName=glyphName, program=failGlyphProgram, private=private, globalSubrs=globalSubrs, charStringsIndex=charStringsIndex, topDict=topDict, charStrings=charStrings ) hmtx[glyphName] = failGlyphMetrics for table in cmap.tables: if table.format == 4: table.cmap[cp] = glyphName else: raise NotImplementedError, "Unsupported cmap table format: {0:d}".format(table.format) # bump this up so that the sequence is the same as the lookup 3 font cp += 3 # set the glyph order shell.setGlyphOrder(glyphOrder) # start the GSUB shell["GSUB"] = newTable("GSUB") gsub = shell["GSUB"].table = GSUB() gsub.Version = 1.0 # make a list of all the features we will make featureCount = len(features) # set up the script list scriptList = gsub.ScriptList = ScriptList() scriptList.ScriptCount = 1 scriptList.ScriptRecord = [] scriptRecord = ScriptRecord() scriptList.ScriptRecord.append(scriptRecord) scriptRecord.ScriptTag = "DFLT" script = scriptRecord.Script = Script() defaultLangSys = script.DefaultLangSys = DefaultLangSys() defaultLangSys.FeatureCount = featureCount defaultLangSys.FeatureIndex = range(defaultLangSys.FeatureCount) defaultLangSys.ReqFeatureIndex = 65535 defaultLangSys.LookupOrder = None script.LangSysCount = 0 script.LangSysRecord = [] # set up the feature list featureList = gsub.FeatureList = FeatureList() featureList.FeatureCount = featureCount featureList.FeatureRecord = [] for index, tag in enumerate(features): # feature record featureRecord = FeatureRecord() featureRecord.FeatureTag = tag feature = featureRecord.Feature = Feature() featureList.FeatureRecord.append(featureRecord) # feature feature.FeatureParams = None feature.LookupCount = 1 feature.LookupListIndex = [index] # write the lookups lookupList = gsub.LookupList = LookupList() lookupList.LookupCount = featureCount lookupList.Lookup = [] for tag in features: # lookup lookup = Lookup() lookup.LookupType = 1 lookup.LookupFlag = 0 lookup.SubTableCount = 1 lookup.SubTable = [] lookupList.Lookup.append(lookup) # subtable subtable = SingleSubst() subtable.Format = 2 subtable.LookupType = 1 subtable.mapping = { "{0!s}.pass".format(tag) : "{0!s}.fail".format(tag), "{0!s}.fail".format(tag) : "{0!s}.pass".format(tag), } lookup.SubTable.append(subtable) path = outputPath % 1 + ".otf" if os.path.exists(path): os.remove(path) shell.save(path) # get rid of the shell if os.path.exists(shellTempPath): os.remove(shellTempPath)
def test_fromXML(self): font = TTFont(sfntVersion='OTTO') font.importXML(CFF_TTX) cffTable = font['CFF '] cffData = cffTable.compile(font) self.assertEqual(cffData, self.cffData)