def test_retain_gids_cff2(self): fontpath = self.getpath("../../varLib/data/TestCFF2VF.otf") font = TTFont(fontpath) self.assertEqual(font["hmtx"]["A"], (600, 31)) self.assertEqual(font["hmtx"]["T"], (600, 41)) font["CFF2"].cff[0].decompileAllCharStrings() cs = font["CFF2"].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertGreater(len(cs["T"].program), 0) subsetpath = self.temp_path(".otf") subset.main( [ fontpath, "--retain-gids", "--output-file=%s" % subsetpath, "A", ] ) subsetfont = TTFont(subsetpath) self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder())) hmtx = subsetfont["hmtx"] self.assertEqual(hmtx["A"], (600, 31)) self.assertEqual(hmtx["glyph00002"], (0, 0)) subsetfont["CFF2"].cff[0].decompileAllCharStrings() cs = subsetfont["CFF2"].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertEqual(cs["glyph00002"].program, [])
def get_new_font_dict(self): """ 用初始化的传入的基准字体和新字体文件对比,得到新字体文件编码与真实文字的映射。 :return: 新字体文件中原编码与实际文字的映射字典 """ standard_font = TTFont(self.standard_font) # ['x', 'uniE01D', 'uniE70B', 'uniEDB9', 'uniE6D2', 'uniF5E8', 'uniE17E', 'uniF359', 'uniE3B4', 'uniE88D', 'uniE086'] uni_tuple = standard_font.getGlyphOrder()[2:] # 获取基准字体坐标库 standard_coordinate_list = self.get_font_coordinate_list(standard_font, uni_tuple) print(f"standard_coordinate_list is {standard_coordinate_list}") # 下载获取当前自定义字体的二进制文件 b_font = self.get_font_content() # with open("my.woff", "wb") as f: # f.write(b_font) # 将二进制文件当做文件操作 new_font = TTFont(io.BytesIO(b_font)) # 读取新字体坐标,去除第一个空值 uni_list2 = new_font.getGlyphOrder()[2:] # 获取新字体坐标库 new_coordinate_list = self.get_font_coordinate_list(new_font, uni_list2) print(f"new_coordinate_list:{new_coordinate_list}") new_font_dict = dict() # 比较基准字体和新字体坐标,映射新字体对应文字 for nc_index, ncd in enumerate(new_coordinate_list): for sc_index, scd in enumerate(standard_coordinate_list): if self.comparison(scd, ncd): new_font_dict[uni_list2[nc_index]] = self.word_list[sc_index] return new_font_dict
def parse_ttf(self, parse_font_name): base_font_map = { 'e0fb': '8', 'e318': '9', 'e684': '7', 'e702': '2', 'eae2': '5', 'ec24': '6', 'ed97': '3', 'f138': '1', 'f3f1': '0', 'f589': '4', } base_font = TTFont('base_font.woff') base_font_orders = base_font.getGlyphOrder()[2:] parse_font = TTFont(parse_font_name) parse_font_orders = parse_font.getGlyphOrder()[2:] base_font_flags = [list(base_font['glyf'][i].flags) for i in base_font_orders] parse_font_flags = [list(parse_font['glyf'][i].flags) for i in parse_font_orders] parse_font_map = {} for base_index, base_flag in enumerate(base_font_flags): for parse_index, parse_flag in enumerate(parse_font_flags): if self.compare_flag(base_flag, parse_flag): base_key = base_font_orders[base_index].replace('uni', '').lower() if base_key in base_font_map: parse_key = parse_font_orders[parse_index].replace('uni', '').lower() parse_key = eval(r'u"\u' + str(parse_key) + '"') parse_font_map[parse_key] = base_font_map[base_key] return parse_font_map
def get_new_font_dict(self): """ 用初始化的传入的基准字体和新字体文件对比,得到新字体文件编码与真实文字的映射。 :return: 新字体文件中原编码与实际文字的映射字典 """ standard_font = TTFont(self.standard_ttf) # 获取基准字体坐标库 uni_tuple = standard_font.getGlyphOrder()[2:] # print("uni1:", uni_tuple, len(uni_tuple)) standard_coordinate_list = self.get_font_coordinate_list(standard_font, uni_tuple) # 下载获取当前自定义字体的二进制文件 b_font = self.get_font_content() # 将二进制文件当做文件操作 new_font = TTFont(io.BytesIO(b_font)) # 读取新字体坐标,去除第一个空值 uni_list2 = new_font.getGlyphOrder()[2:] # print("uni2:", uni_list2, len(uni_list2)) # 获取新字体坐标库 new_coordinate_list = self.get_font_coordinate_list(new_font, uni_list2) new_font_dict = {} # 比较基准字体和新字体坐标,映射新字体对应文字 for nc_index, ncd in enumerate(new_coordinate_list): for sc_index, scd in enumerate(standard_coordinate_list): # 若相等构造新的对造表 if self.comparison(scd, ncd): new_font_dict[uni_list2[nc_index]] = self.word_tuple[sc_index] return new_font_dict
def parse_font(): font = TTFont(r'E:\vscode_code\练习\58\maoyan.woff') font.saveXML(r'E:\vscode_code\练习\58\maoyan.xml') font_base_order = font.getGlyphOrder()[2:] print(font_base_order) # 根据第一次下载的文件写出对应 map_list = ['1', '2', '8', '0', '7', '6', '3', '5', '9', '4'] font_new = TTFont(r'E:\vscode_code\练习\58\maoyan.woff') font_new.saveXML(r'E:\vscode_code\练习\58\maoyan.xml') font_new_order = font_new.getGlyphOrder()[2:] print(font_new_order) base_flag_list = list() new_flag_list = list() # 得到两个二维列表,对里面没个一维列表进行内容的比对,得到对应的字体 for i, j in zip(font_base_order, font_new_order): flag_base = font['glyf'][i].flags flag_new = font_new['glyf'][j].flags base_flag_list.append(list(flag_base)) new_flag_list.append(list(flag_new)) memory_dict = dict() for index1, x in enumerate(base_flag_list): for index2, y in enumerate(new_flag_list): if common(x, y): key = font_new_order[index2] key = '&#x' + key.replace('uni', '').lower() + ';' memory_dict[key] = map_list[index1] print(memory_dict) return memory_dict
def tff_parse(font_parse_name): # 我这里的字体的顺序,如果你的不同,一定要修改 font_dict = [u'博', u'经', u'硕', u'届', u'大', u'刘', u'8', u'1', u'士', u'E', u'2', u'6', u'张', u'M', u'验', u'5', u'本', u'赵', u'陈', u'吴', u'李', u'生', u'4', u'校', u'以', u'应', u'黄', u'技', u'无', u'女', u'A', u'周', u'中', u'3', u'王', u'7', u'0', u'9', u'科', u'高', u'男', u'杨', u'专', u'下', u'B'] font_base = TTFont('font_base.ttf') font_base_order = font_base.getGlyphOrder()[1:] # font_base.saveXML('font_base.xml') 调试用 font_parse = TTFont(font_parse_name) # font_parse.saveXML('font_parse_2.xml')调试用 font_parse_order = font_parse.getGlyphOrder()[1:] f_base_flag = [] for i in font_base_order: flags = font_base['glyf'][i].flags f_base_flag.append(list(flags)) f_flag = [] for i in font_parse_order: flags = font_parse['glyf'][i].flags f_flag.append(list(flags)) result_dict = {} for a, i in enumerate(f_base_flag): for b, j in enumerate(f_flag): if comp(i, j): key = font_parse_order[b].replace('uni', '') key = eval(r'u"\u' + str(key) + '"').lower() result_dict[key] = font_dict[a] return result_dict
class DetectIntersections(BaseDetectIntersections): def __init__(self, in_font): self.in_font = in_font self.font = TTFont(self.in_font) self._is_cjk = self.is_cjk() basename, ext = os.path.splitext(os.path.basename(in_font)) self.degree = 2 if ext.lower() == ".ttf" else 3 def run(self): for gname in self.font.getGlyphOrder(): intersections = self.detect(gname) if intersections: print "{}:".format(gname), ", ".join(["({}, {})".format(pt[0], pt[1]) for pt in intersections]) def detect(self, name): glyph = self.font.getGlyphSet()[name] pen = ConvPen(self.degree) glyph.draw(pen) return self.detect_intersections(pen.contours) def gid2name(self, gid): return self.font.getGlyphOrder()[gid] def is_cjk(self): if "CFF " not in self.font: return False return hasattr(self.font["CFF "].cff.topDictIndex[0], "ROS")
def test_retain_gids_cff(self): _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf") font = TTFont(fontpath) self.assertEqual(font["hmtx"]["A"], (500, 132)) self.assertEqual(font["hmtx"]["B"], (400, 132)) font["CFF "].cff[0].decompileAllCharStrings() cs = font["CFF "].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertGreater(len(cs["B"].program), 0) subsetpath = self.temp_path(".otf") subset.main([ fontpath, "--retain-gids", "--output-file=%s" % subsetpath, "--glyph-names", "A", ]) subsetfont = TTFont(subsetpath) self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()) hmtx = subsetfont["hmtx"] self.assertEqual(hmtx["A"], (500, 132)) self.assertEqual(hmtx["B"], (0, 0)) subsetfont["CFF "].cff[0].decompileAllCharStrings() cs = subsetfont["CFF "].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertEqual(cs["B"].program, ["endchar"])
def test_retain_gids_ttf(self): _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") font = TTFont(fontpath) self.assertEqual(font["hmtx"]["A"], (500, 132)) self.assertEqual(font["hmtx"]["B"], (400, 132)) self.assertGreater(font["glyf"]["A"].numberOfContours, 0) self.assertGreater(font["glyf"]["B"].numberOfContours, 0) subsetpath = self.temp_path(".ttf") subset.main([ fontpath, "--retain-gids", "--output-file=%s" % subsetpath, "--glyph-names", "A", ]) subsetfont = TTFont(subsetpath) self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()) hmtx = subsetfont["hmtx"] self.assertEqual(hmtx["A"], (500, 132)) self.assertEqual(hmtx["B"], (0, 0)) glyf = subsetfont["glyf"] self.assertGreater(glyf["A"].numberOfContours, 0) self.assertEqual(glyf["B"].numberOfContours, 0)
def test_retain_gids_cff2(self): fontpath = self.getpath("../../varLib/data/TestCFF2VF.otf") font = TTFont(fontpath) self.assertEqual(font["hmtx"]["A"], (600, 31)) self.assertEqual(font["hmtx"]["T"], (600, 41)) font["CFF2"].cff[0].decompileAllCharStrings() cs = font["CFF2"].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertGreater(len(cs["T"].program), 0) subsetpath = self.temp_path(".otf") subset.main([ fontpath, "--retain-gids", "--output-file=%s" % subsetpath, "A", ]) subsetfont = TTFont(subsetpath) self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder())) hmtx = subsetfont["hmtx"] self.assertEqual(hmtx["A"], (600, 31)) self.assertEqual(hmtx["glyph00002"], (0, 0)) subsetfont["CFF2"].cff[0].decompileAllCharStrings() cs = subsetfont["CFF2"].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertEqual(cs["glyph00002"].program, [])
def creatdic(self, tmp_dic): woff1 = os.path.dirname(os.path.abspath(__file__)) + r'\1.woff' font1 = TTFont(woff1) # font0.saveXML('2.xml') # for developer # obj_list1 = font1.getGlyphNames()[1:-1] # same uni_list1 = font1.getGlyphOrder()[ 2:] # all code except first and second # to get by eyes code_dict = { 'uniE40E': '8', 'uniE113': '2', 'uniE33A': '1', 'uniED95': '9', 'uniF08A': '3', 'uniE101': '4', 'uniE10E': '0', 'uniF560': '7', 'uniEB8C': '5', 'uniE129': '6' } woff2 = os.path.dirname(os.path.abspath(__file__)) + r'\2.woff' font2 = TTFont(woff2) # 2.ttf # obj_list2 = font2.getGlyphNames()[1:-1] developer uni_list2 = font2.getGlyphOrder()[2:] for uni2 in uni_list2: obj2 = font2['glyf'][uni2] for uni1 in uni_list1: obj1 = font1['glyf'][uni1] if obj1 == obj2: # same symbol means same value tmp_dic.update({uni2[3:]: code_dict[uni1]}) # get dic
def test_retain_gids_cff(self): _, fontpath = self.compile_font(self.getpath("TestOTF-Regular.ttx"), ".otf") font = TTFont(fontpath) self.assertEqual(font["hmtx"]["A"], (500, 132)) self.assertEqual(font["hmtx"]["B"], (400, 132)) font["CFF "].cff[0].decompileAllCharStrings() cs = font["CFF "].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertGreater(len(cs["B"].program), 0) subsetpath = self.temp_path(".otf") subset.main( [ fontpath, "--retain-gids", "--output-file=%s" % subsetpath, "--glyph-names", "A", ] ) subsetfont = TTFont(subsetpath) self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()) hmtx = subsetfont["hmtx"] self.assertEqual(hmtx["A"], (500, 132)) self.assertEqual(hmtx["B"], (0, 0)) subsetfont["CFF "].cff[0].decompileAllCharStrings() cs = subsetfont["CFF "].cff[0].CharStrings self.assertGreater(len(cs["A"].program), 0) self.assertEqual(cs["B"].program, ["endchar"])
def test_retain_gids_ttf(self): _, fontpath = self.compile_font(self.getpath("TestTTF-Regular.ttx"), ".ttf") font = TTFont(fontpath) self.assertEqual(font["hmtx"]["A"], (500, 132)) self.assertEqual(font["hmtx"]["B"], (400, 132)) self.assertGreater(font["glyf"]["A"].numberOfContours, 0) self.assertGreater(font["glyf"]["B"].numberOfContours, 0) subsetpath = self.temp_path(".ttf") subset.main( [ fontpath, "--retain-gids", "--output-file=%s" % subsetpath, "--glyph-names", "B", ] ) subsetfont = TTFont(subsetpath) self.assertEqual(subsetfont.getGlyphOrder(), font.getGlyphOrder()[0:3]) hmtx = subsetfont["hmtx"] self.assertEqual(hmtx["A"], ( 0, 0)) self.assertEqual(hmtx["B"], (400, 132)) glyf = subsetfont["glyf"] self.assertEqual(glyf["A"].numberOfContours, 0) self.assertGreater(glyf["B"].numberOfContours, 0)
def parse_font(self, response): font_name = re.search( r"url\('.*?vfile\.meituan\.net\/colorstone\/(\w+\.woff)'\)", response, re.S).group(1) #print(font_name) font_url = 'https://vfile.meituan.net/colorstone/' + font_name font_response = self.get_url(font_url) with open(font_name, 'wb') as f: f.write(font_response.content) basefont = TTFont('basefont.woff') basefont.saveXML('basefont.xml') basefont_dict = { 'uniE46E': '9', 'uniE759': '8', 'uniF175': '6', 'uniE442': '5', 'uniE977': '2', 'uniE497': '7', 'uniE966': '1', 'uniF801': '0', 'uniF560': '4', 'uniEDD2': '3' } basefont_list = basefont.getGlyphOrder()[2:] #print(basefont_list) newfont = TTFont(font_name) newfont.saveXML('newfont.xml') newfont_list = newfont.getGlyphOrder()[2:] #print(newfont_list) coordinate_list_1 = [] for base_uni in basefont_list: base_coordinate = basefont['glyf'][base_uni].coordinates coordinate_list_1.append(base_coordinate) #print(coordinate_list_1) coordinate_list_2 = [] for new_uni in newfont_list: new_coordinate = newfont['glyf'][new_uni].coordinates coordinate_list_2.append(new_coordinate) #print(coordinate_list_2) index_2 = -1 newfont_dict = {} for name2 in coordinate_list_2: index_2 += 1 index_1 = -1 for name1 in coordinate_list_1: index_1 += 1 if self.compare(name1, name2): newfont_dict[newfont_list[index_2]] = basefont_dict[ basefont_list[index_1]] for i in newfont_dict: pattern = i.replace('uni', '&#x').lower() + ';' response = response.replace(pattern, newfont_dict[i]) return response
def tff_parse(font_parse_name): # 我这里的字体的顺序,如果你的不同,一定要修改 font_dict = [u'日', u'坚', u'无', u'竞', u'东', u'顿', u'大', u'汉', u'德', u'省', u'简', u'十', u'革', u'补', u'史', u'件', u'阿', u'注', u'网', u'极', u'络', u'经', u'敌', u'属', u'洁', u'术', u'承', u'续', u'与', u'想', u'说', u'公', u'里', u'管', u'涯', u'名', u'讲', u'黑', u'红', u'平', u'申', u'调', u'讯', u'去', u'示', u'规', u'来', u'度', u'变', u'铁', u'犯', u'酷', u'宣', u'治', u'窗', u'发', u'统', u'制', u'积', u'质', u'成', u'都', u'博', u'办', u'云', u'习', u'脸', u'字', u'复', u'地', u'兵', u'历', u'素', u'水', u'入', u'型', u'丽', u'布', u'财', u'迎', u'部', u'决', u'认', u'委', u'建', u'方', u'要', u'除', u'户', u'学', u'密', u'荣', u'页', u'狗', u'展', u'夫', u'黄', u'上', u'命', u'不', u'台', u'进', u'书', u'退', u'赋', u'范', u'产', u'多', u'点', u'企', u'宽', u'群', u'斯', u'个', u'困', u'团', u'论', u'登', u'于', u'弘', u'扬', u'责', u'家', u'载', u'移', u'准', u'程', u'鞋', u'关', u'通', u'育', u'臂', u'聚', u'务', u'端', u'转', u'当', u'明', u'失', u'胡', u'印', u'共', u'精', u'裁', u'改', u'北', u'先', u'末', u'国', u'情', u'工', u'性', u'广', u'化', u'数', u'之', u'集', u'人', u'政', u'更', u'拥', u'盒', u'华', u'李', u'膀', u'吸', u'用', u'湾', u'标', u'卫', u'电', u'周', u'践', u'吾', u'机', u'排', u'九', u'典', u'做', u'恶', u'张', u'巴', u'活', u'其', u'总', u'七', u'六', u'引', u'艺', u'的', u'思', u'信', u'首', u'本', u'新', u'步', u'请', u'代', u'传', u'锋', u'控', u'列', u'神', u'同', u'下', u'哲', u'年', u'律', u'员', u'城', u'赢', u'西', u'市', u'息', u'央', u'版', u'海', u'技', u'软', u'资', u'报', u'波', u'光', u'远', u'山', u'文', u'善', u'众', u'动', u'面', u'单', u'位', u'任', u'军', u'深', u'社', u'江', u'伦', u'款', u'浪', u'渔', u'欢', u'理', u'局', u'见', u'开', u'录', u'据', u'搜', u'舆', u'对', u'田', u'梦', u'告', u'亲', u'泽', u'勇', u'干', u'飞', u'系', u'错', u'扎', u'遗', u'全', u'涉', u'参', u'微', u'扫', u'态', u'役', u'专', u'幕', u'祠', u'何', u'指', u'修', u'事', u'强', u'园', u'克', u'淀', u'所', u'有', u'乐', u'厅', u'站', u'府', u'康', u'廉', u'期', u'听', u'选', u'届', u'境', u'主', u'抱', u'血', u'洛', u'天', u'源', u'疆', u'绩', u'我', u'层', u'协', u'计', u'行', u'毛', u'线', u'会', u'时', u'高', u'欧', u'利', u'堡', u'能', u'知', u'农', u'存', u'作', u'招', u'近', u'中', u'第', u'科', u'得', u'为', u'挂', u'体', u'热', u'具', u'义', u'舟', u'埃', u'土', u'顶', u'马', u'频', u'持', u'音', u'健', u'绿', u'权', u'外', u'生', u'正', u'牌', u'如', u'收', u'南', u'聘', u'临', u'设', u'皮', u'区', u'魂', u'担', u'凰', u'津', u'推', u'量', u'狐', u'服', u'业', u'视', u'民', u'好', u'悬', u'穿', u'腾', u'贸', u'浙', u'定', u'加', u'优', u'藏', u'坛', u'冀', u'头', u'沉', u'党', u'凤', u'百', u'荐', u'常', u'在', u'闻', u'负', u'法', u'争', u'客'] font_base = TTFont('china.ttf') font_base_order = font_base.getGlyphOrder()[4:] # 对应切字 uniE77C # font_base.saveXML('font_base.xml') # 调试用 font_parse = TTFont(font_parse_name) # font_parse.saveXML('font_parse_2.xml') # 调试用 font_parse_order = font_parse.getGlyphOrder()[4:] f_base_flag = [] for i in font_base_order: # 遍历 uniE77C 拿到字形的编码 flags = font_base['glyf'][i].flags f_base_flag.append(list(flags)) # 获取到 flags 值 并写入列表 f_flag = [] for i in font_parse_order: flags = font_parse['glyf'][i].flags f_flag.append(list(flags)) result_dict = {} for a, i in enumerate(f_base_flag): for b, j in enumerate(f_flag): if comp(i, j): key = font_parse_order[b].replace('uni', '') key = eval(r'u"\u' + str(key) + '"').lower() result_dict[key] = font_dict[a] # 对应的逻辑 字形对应上了,, 就取 baseFont 的值 return result_dict # 返回对应的字体
def returnNewDict(self): # font=TTFont(self.filename_1) # font.saveXML("F:/myTestFile/TestObject/TongChuangYuanMa/txt_file/file4.xml") # filename = "F:/myTestFile/TestObject/TongChuangYuanMa/txt_file/file.woff" # filename2 = "F:/myTestFile/TestObject/TongChuangYuanMa/txt_file/file2.woff" font1 = TTFont(self.filename_1) #打开本地字体文件01.ttf uniList1 = font1.getGlyphOrder()[2:] #获取所有编码,去除前2个 # print(uniList1) # obj_list1=font1.getGlyphNames()[1:-1] #获取所有字符的对象,去除第一个和最后一个 font2 = TTFont(self.filename_2) #打开访问网页新获得的字体文件02.ttf print("self.newXmlPath====", self.newXmlPath) font2.saveXML(self.newXmlPath) uniList2 = font2.getGlyphOrder()[2:] print("uniList1=====", uniList1) print("uniList2=====", uniList2) #手动确认编码和数字之间的对应关系,保存到字典中 oldDictList = { "uniE80C": "4", "uniE836": "7", "uniE8E7": "6", "uniEEEC": "3", "uniEF1B": "0", "uniF104": "2", "uniF424": "5", "uniF487": "1", "uniF4AE": "9", "uniF534": "8" } num = (0, 0) bianma = "" mark = [] newDictList = {} font1_dict = {} for uni1 in uniList1: # print(uni1) obj1 = font1['glyf'][uni1] ptArr1 = obj1.coordinates for uni2 in uniList2: obj2 = font2['glyf'][uni2].coordinates[0] if abs(ptArr1[0][0] - num[0]) > abs(ptArr1[0][0] - obj2[0]): num = obj2 bianma = uni2 # print("-------",oldDictList[uni1]) # print("555==",num,bianma) # print("666==",num,bianma) # print("bianma====",oldDictList[uni1]) newDictList[bianma] = oldDictList[uni1] print("bianma===", bianma) print("newDictList==={}=={}".format(newDictList[bianma], oldDictList[uni1])) return newDictList
def tran(self, text, html): url = re.findall("url\('(.*?.woff)'", html)[0] with open('人人车01.ttf', 'wb') as f: f.write(requests.get(url=url).content) font1 = TTFont('人人车.ttf') obj_list1 = font1.getGlyphNames()[1:] # 获取所有字符的对象,去除第一个和最后一个 uni_list1 = font1.getGlyphOrder()[1:] font2 = TTFont('人人车01.ttf') obj_list2 = font2.getGlyphNames()[1:] # 获取所有字符的对象,去除第一个和最后一个 uni_list2 = font2.getGlyphOrder()[1:] dict = { 'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4', 'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9' } dict1 = { 'zero': '0', 'one': '1', 'two': '2', 'four': '3', 'three': '4', 'five': '5', 'seven': '6', 'nine': '7', 'six': '8', 'eight': '9' } ''' 遍历加密的内容text,在新的ttf文件中查找每一个text的元素。如果找到,则替换''' for a in text: for uni2 in uni_list2: # print(uni2) try: id = dict[str(uni2)] # 找到unit2未加密对应的数字 except: continue id_1 = font2.getGlyphID(str(uni2)) # Z找到unit2在ttf文件中的id obj2 = font2['glyf'][uni2] # str(id) != str(id_1): # 若未加密的数字id和ttf中对应的id_1不相等,说明a加密了 if str(id) == str(a): for uni1 in uni_list1: obj1 = font1['glyf'][uni1] if obj1 == obj2: text = text.replace(a, dict1[uni1]) return text
def test_unique_glyph_names(self): font_path = self.getpath('LinLibertine_RBI.otf') font = TTFont(font_path, recalcBBoxes=False, recalcTimestamp=False) glyphOrder = font.getGlyphOrder() self.assertEqual(len(glyphOrder), len(set(glyphOrder))) self.temp_dir() save_path = os.path.join(self.tempdir, 'TestOTF.otf') font.save(save_path) font2 = TTFont(save_path) glyphOrder = font2.getGlyphOrder() self.assertEqual(len(glyphOrder), len(set(glyphOrder)))
def getNewGlyphSet(font_path, removeNames): ttfont = TTFont(font_path) glyphNames = ttfont.getGlyphOrder() smallcaps = [name for name in ttfont.getGlyphOrder() if f'.{smallCapSuffix}' in name] glyphsReplacedBySmallcaps = [name.replace(f'.{smallCapSuffix}','') for name in smallcaps if name in glyphNames] newGlyphNames = [name for name in glyphNames if name not in glyphsReplacedBySmallcaps and name not in removeNames] # newGlyphNames = [name for name in glyphNames if name not in glyphsReplacedBySmallcaps] print(" ".join(newGlyphNames))
def parse(self, response): base_font = TTFont(r"glidedsky/data/font.ttf") base_uni_list = base_font.getGlyphOrder()[1:] origin_dict = { "five": "0", "four": "1", "two": "2", "three": "3", "zero": "4", "one": "5", "nine": "6", "six": "7", "eight": "8", "seven": "9", } str2num_dict = { "zero": "0", "one": "1", "two": "2", "three": "3", "four": "4", "five": "5", "six": "6", "seven": "7", "eight": "8", "nine": "9", } num_map = dict() ttf_base64 = re.findall(r"data:font;charset=utf-8;base64,(.*?)\)", response.text)[0] base64_data = base64.decodebytes(ttf_base64.encode()) online_font = TTFont(BytesIO(base64_data)) online_uni_list = online_font.getGlyphOrder()[1:] for uni2 in online_uni_list: obj2 = online_font["glyf"][uni2] for uni1 in base_uni_list: obj1 = base_font["glyf"][uni1] if obj1 == obj2: num_map[str2num_dict[uni2]] = origin_dict[uni1] num = response.xpath("//div[@class='col-md-1']/text()").extract() numbers = [] for one in num: new_num = "" for i in one.strip(): new_num += num_map[i] numbers.append(int(new_num)) item = GlidedskyItem(numbers=numbers) yield item
async def shouldParseFont(self, bodyClass): # self.resetCookie() print(bodyClass) file = self._fontCachedPath % (bodyClass) if not os.path.exists(file): #self.resetCookie() async with aiohttp.ClientSession() as session: async with session.get(self._fontUrl % (bodyClass[0:2], bodyClass), headers=self._headers) as _resp: assert _resp.status == 200 with open(file, 'wb') as fd: while True: chunk = await _resp.content.read(1024) if not chunk: break fd.write(chunk) font = TTFont(file) #font.saveXML("./fonts/1.xml") gly_list = font.getGlyphOrder() gly_names = font.getGlyphNames() gly_list = gly_list[2:12] gly_names = gly_names[0:10] secrets = {} for i in range(10): secrets[gly_list[i]] = gly_names[i] self._redis.set(bodyClass, json.dumps(secrets)) return secrets else: return json.loads(self._redis.get(bodyClass))
def generateWoff2(self, verbosity=0): woff2_list = [] os.makedirs(self.assetdir, exist_ok=True) for subname, (subrange, unicodes) in self.urdict.items(): if verbosity == 2: print("Processing", subname) subs = Subsetter() font = TTFont(self.fontfile) subs.populate(unicodes=unicodes) subs.subset(font) cmap = font.getBestCmap() glyphcount = len(font.getGlyphOrder()) - 1 if cmap: outfile = os.path.join(self.assetdir, self.basename + "." + subname + ".woff2") font.flavor = 'woff2' font.save(outfile) woff2_list.append((outfile, subrange)) if verbosity == 1: print("Generated", outfile) elif verbosity == 2: print(" Generated", outfile) print(" Found", glyphcount, "glyphs for", len(cmap), "out of", len(unicodes), "unicodes") else: if verbosity == 2: print(" Found no glyphs for any of", len(unicodes), "unicodes") font.close() return woff2_list
def jiemi(tag): font_name = '4165b8d8.woff' font = TTFont(font_name) font1 = font.getGlyphOrder()[2:] # 解析字体 font1 = [font1[i][-4:] for i in range(len(font1))] # 取最后四位编码 font2 = [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '店', '中', '美', '家', '馆', '小', '车', '大', '市', '公', '酒', '行', '国', '品', '发', '电', '金', '心', '业', '商', '司', '超', '生', '装', '园', '场', '食', '有', '新', '限', '天', '面', '工', '服', '海', '华', '水', '房', '饰', '城', '乐', '汽', '香', '部', '利', '子', '老', '艺', '花', '专', '东', '肉', '菜', '学', '福', '饭', '人', '百', '餐', '茶', '务', '通', '味', '所', '山', '区', '门', '药', '银', '农', '龙', '停', '尚', '安', '广', '鑫', '一', '容', '动', '南', '具', '源', '兴', '鲜', '记', '时', '机', '烤', '文', '康', '信', '果', '阳', '理', '锅', '宝', '达', '地', '儿', '衣', '特', '产', '西', '批', '坊', '州', '牛', '佳', '化', '五', '米', '修', '爱', '北', '养', '卖', '建', '材', '三', '会', '鸡', '室', '红', '站', '德', '王', '光', '名', '丽', '油', '院', '堂', '烧', '江', '社', '合', '星', '货', '型', '村', '自', '科', '快', '便', '日', '民', '营', '和', '活', '童', '明', '器', '烟', '育', '宾', '精', '屋', '经', '居', '庄', '石', '顺', '林', '尔', '县', '手', '厅', '销', '用', '好', '客', '火', '雅', '盛', '体', '旅', '之', '鞋', '辣', '作', '粉', '包', '楼', '校', '鱼', '平', '彩', '上', '吧', '保', '永', '万', '物', '教', '吃', '设', '医', '正', '造', '丰', '健', '点', '汤', '网', '庆', '技', '斯', '洗', '料', '配', '汇', '木', '缘', '加', '麻', '联', '卫', '川', '泰', '色', '世', '方', '寓', '风', '幼', '羊', '烫', '来', '高', '厂', '兰', '阿', '贝', '皮', '全', '女', '拉', '成', '云', '维', '贸', '道', '术', '运', '都', '口', '博', '河', '瑞', '宏', '京', '际', '路', '祥', '青', '镇', '厨', '培', '力', '惠', '连', '马', '鸿', '钢', '训', '影', '甲', '助', '窗', '布', '富', '牌', '头', '四', '多', '妆', '吉', '苑', '沙', '恒', '隆', '春', '干', '饼', '氏', '里', '二', '管', '诚', '制', '售', '嘉', '长', '轩', '杂', '副', '清', '计', '黄', '讯', '太', '鸭', '号', '街', '交', '与', '叉', '附', '近', '层', '旁', '对', '巷', '栋', '环', '省', '桥', '湖', '段', '乡', '厦', '府', '铺', '内', '侧', '元', '购', '前', '幢', '滨', '处', '向', '座', '下', '県', '凤', '港', '开', '关', '景', '泉', '塘', '放', '昌', '线', '湾', '政', '步', '宁', '解', '白', '田', '町', '溪', '十', '八', '古', '双', '胜', '本', '单', '同', '九', '迎', '第', '台', '玉', '锦', '底', '后', '七', '斜', '期', '武', '岭', '松', '角', '纪', '朝', '峰', '六', '振', '珠', '局', '岗', '洲', '横', '边', '济', '井', '办', '汉', '代', '临', '弄', '团', '外', '塔', '杨', '铁', '浦', '字', '年', '岛', '陵', '原', '梅', '进', '荣', '友', '虹', '央', '桂', '沿', '事', '津', '凯', '莲', '丁', '秀', '柳', '集', '紫', '旗', '张', '谷', '的', '是', '不', '了', '很', '还', '个', '也', '这', '我', '就', '在', '以', '可', '到', '错', '没', '去', '过', '感', '次', '要', '比', '觉', '看', '得', '说', '常', '真', '们', '但', '最', '喜', '哈', '么', '别', '位', '能', '较', '境', '非', '为', '欢', '然', '他', '挺', '着', '价', '那', '意', '种', '想', '出', '员', '两', '推', '做', '排', '实', '分', '间', '甜', '度', '起', '满', '给', '热', '完', '格', '荐', '喝', '等', '其', '再', '几', '只', '现', '朋', '候', '样', '直', '而', '买', '于', '般', '豆', '量', '选', '奶', '打', '每', '评', '少', '算', '又', '因', '情', '找', '些', '份', '置', '适', '什', '蛋', '师', '气', '你', '姐', '棒', '试', '总', '定', '啊', '足', '级', '整', '带', '虾', '如', '态', '且', '尝', '主', '话', '强', '当', '更', '板', '知', '己', '无', '酸', '让', '入', '啦', '式', '笑', '赞', '片', '酱', '差', '像', '提', '队', '走', '嫩', '才', '刚', '午', '接', '重', '串', '回', '晚', '微', '周', '值', '费', '性', '桌', '拍', '跟', '块', '调', '糕' ] font_data = dict(map(lambda x, y: [x, y], font1, font2)) # return font_data font_list = tag font_list = [ str(i.encode("unicode_escape"))[-5:-1] if len(repr(i)) > 3 else i for i in font_list ] # 提取后四位
def __init__(self, ufo, ttFont=None, glyphSet=None, **kwargs): """ Args: ufo: an object representing a UFO (defcon.Font or equivalent) containing the features source data. ttFont: a fontTools TTFont object where the generated OpenType tables are added. If None, an empty TTFont is used, with the same glyph order as the ufo object. glyphSet: a (optional) dict containing pre-processed copies of the UFO glyphs. """ self.ufo = ufo if ttFont is None: from fontTools.ttLib import TTFont from ufo2ft.util import makeOfficialGlyphOrder ttFont = TTFont() ttFont.setGlyphOrder(makeOfficialGlyphOrder(ufo)) self.ttFont = ttFont glyphOrder = ttFont.getGlyphOrder() if glyphSet is not None: assert set(glyphOrder) == set(glyphSet.keys()) else: glyphSet = ufo self.glyphSet = OrderedDict((gn, glyphSet[gn]) for gn in glyphOrder)
def get_map(be_p1, word_list): # font2 = TTFont('text1.ttf') # window # font2 = TTFont('./luntan/text1.ttf') # linux font2 = TTFont('/home/home/mywork/font/luntan/text2.ttf') uni_list2 = font2.getGlyphOrder()[1:] on_p1 = [] for i in uni_list2: pp1 = [] p = font2['glyf'][i].coordinates for f in p: pp1.append(f) on_p1.append(pp1) n2 = 0 x_list = [] for d in on_p1: n2 += 1 n1 = 0 for a in be_p1: n1 += 1 if comp(a, d): # print(uni_list2[n2 - 1], word_list[n1 - 1]) x_list.append({"key": uni_list2[n2 - 1], "value": word_list[n1 - 1]}) # print(x_list) return x_list
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 __datas_font_chance(self, find_font): '''将新下载字体进行比对分析得出真实数字映射关系''' font_obj = self.__datas_font(find_font) uni_list_new = font_obj.getGlyphOrder()[2:] font_original = TTFont('original.woff') uni_list_original = font_original.getGlyphOrder()[2:] font_dict = { 'uniF827': '0', 'uniEF9E': '1', 'uniE69E': '2', 'uniE175': '3', 'uniE2E8': '4', 'uniE9FE': '5', 'uniF83E': '6', 'uniEC05': '7', 'uniE446': '8', 'uniF888': '9' } new_font_list = [] for uni_new in uni_list_new: obj_new = font_obj['glyf'][uni_new] for uni_original in uni_list_original: obj_original = font_original['glyf'][uni_original] if obj_original == obj_new: obj_list = {} nui_new_lower = uni_new[3:].lower() obj_list[nui_new_lower] = font_dict[uni_original] new_font_list.append(obj_list) new_font_dict = {} for i in new_font_list: for k, j in i.items(): new_font_dict[k] = j return new_font_dict
def test_check_unique_glyphnames(): """ Font contains unique glyph names? """ check = CheckTester(universal_profile, "com.google.fonts/check/unique_glyphnames") ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) assert_PASS(check(ttFont)) # Fonttools renames duplicate glyphs with #1, #2, ... on load. # Code snippet from https://github.com/fonttools/fonttools/issues/149. glyph_names = ttFont.getGlyphOrder() glyph_names[2] = glyph_names[3] # Load again, we changed the font directly. ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) ttFont.setGlyphOrder(glyph_names) ttFont['post'] # Just access the data to make fonttools generate it. _file = io.BytesIO() _file.name = ttFont.reader.file.name ttFont.save(_file) ttFont = TTFont(_file) message = assert_results_contain(check(ttFont), FAIL, "duplicated-glyph-names") assert "space" in message # Upgrade to post format 3.0 and roundtrip data to update TTF object. ttFont = TTFont(TEST_FILE("nunito/Nunito-Regular.ttf")) ttFont.setGlyphOrder(glyph_names) ttFont["post"].formatType = 3.0 _file = io.BytesIO() _file.name = ttFont.reader.file.name ttFont.save(_file) ttFont = TTFont(_file) assert_SKIP(check(ttFont))
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 font_convert(font_name): # 获取字体的映射规则 base_font = TTFont(font_name) code_list = base_font.getGlyphOrder()[2:] # 创建一张图片 用来把字体画上去 im = Image.new("RGB", (1800, 1800), (255, 255, 255)) image_draw = ImageDraw.Draw(im) base_font = ImageFont.truetype(font_name,80) # 等分成一份 count = 1 # 等分成一份 array_list = numpy.array_split(code_list, count) for i in range(len(array_list)): # 讲javascript的unicode码变成python总的unicode码 new_list = [i.replace("uni", "\\u") for i in array_list[i]] text = "".join(new_list) text = text.encode('utf-8').decode('unicode_escape') # print('text:', text) # 把反向编码的 文字写在图片上 要使用的字体 image_draw.text((0, 100 * i), text, font=base_font, fill="#000000") # im.save("font_2_str.jpg") # im.show() # im = Image.open("font_2_str.jpg") # 可以将图片保存到本地,以便于手动打开图片查看 # 识别图片中的文字 result = pytesseract.image_to_string(im) # # 去除空白及换行 result_str = result.replace(" ", "").replace("\n", "") # 将内容替换成网页的格式,准备去网页中进行替换 html_code_list = [i.replace("uni", "&#x").lower() + ";" for i in code_list] # print(len(html_code_list)) # print(len(result_str)) return dict(zip(html_code_list, list(result_str)))
def get_fonts(font_url): if font_url in fonts['woff']: return fonts['woff'][font_url] font_content = _send_request(requests.Request('GET', font_url).prepare()).content with tempfile.NamedTemporaryFile(suffix='.woff') as io: io.write(font_content) ttf = TTFont(io.name) code_list = ttf.getGlyphOrder()[2:] code_list = [chr(int(i.replace('uni', ''), 16)) for i in code_list] code_list_chinese = code_list[10:] font_size = 40 font_width = 42 font_height = 52 column = 30 line_count = math.ceil(len(code_list_chinese) / column) image = Image.new('RGB', ((column + 1) * font_width, (line_count + 1) * font_height), (255, 255, 255)) image_draw = ImageDraw.Draw(image) image_font = ImageFont.truetype(font=io.name, size=font_size) for line_index, line in enumerate( itertools.zip_longest(*([iter(code_list_chinese)] * column), fillvalue='')): image_draw.text((font_width, (line_index + 0.5) * font_height), ''.join(line), font=image_font, fill='#000000') # image.save('font.jpg') result = pytesseract.image_to_string(image, lang='chi_sim') result = re.sub(r'[ \n]', '', result) result = dict( zip(code_list, [*[str(i) for i in range(1, 10)], '0', *result])) fonts['woff'][font_url] = result return result
def parse_ziti(self, class_name, datas): if class_name == 'shopNum': # 评论数, 人均消费, 口味环境服务分数 woff_name = 'ebb40305.woff' elif class_name == 'tagName': # 店铺分类,哪个商圈 woff_name = '9b3f551f.woff' else: woff_name = '1d742900.woff' # 店铺具体地址 # 评分 font_data = TTFont(woff_name) font_data.saveXML(woff_name) # 保存xml便于做分析 words = '1234567890店中美家馆小车大市公酒行国品发电金心业商司超生装园场食有新限天面工服海华水房饰城乐汽香部利子老艺花专东肉菜学福饭人百餐茶务通味所山区门药银农龙停尚安广鑫一容动南具源兴鲜记时机烤文康信果阳理锅宝达地儿衣特产西批坊州牛佳化五米修爱北养卖建材三会鸡室红站德王光名丽油院堂烧江社合星货型村自科快便日民营和活童明器烟育宾精屋经居庄石顺林尔县手厅销用好客火雅盛体旅之鞋辣作粉包楼校鱼平彩上吧保永万物教吃设医正造丰健点汤网庆技斯洗料配汇木缘加麻联卫川泰色世方寓风幼羊烫来高厂兰阿贝皮全女拉成云维贸道术运都口博河瑞宏京际路祥青镇厨培力惠连马鸿钢训影甲助窗布富牌头四多妆吉苑沙恒隆春干饼氏里二管诚制售嘉长轩杂副清计黄讯太鸭号街交与叉附近层旁对巷栋环省桥湖段乡厦府铺内侧元购前幢滨处向座下臬凤港开关景泉塘放昌线湾政步宁解白田町溪十八古双胜本单同九迎第台玉锦底后七斜期武岭松角纪朝峰六振珠局岗洲横边济井办汉代临弄团外塔杨铁浦字年岛陵原梅进荣友虹央桂沿事津凯莲丁秀柳集紫旗张谷的是不了很还个也这我就在以可到错没去过感次要比觉看得说常真们但最喜哈么别位能较境非为欢然他挺着价那意种想出员两推做排实分间甜度起满给热完格荐喝等其再几只现朋候样直而买于般豆量选奶打每评少算又因情找些份置适什蛋师气你姐棒试总定啊足级整带虾如态且尝主话强当更板知己无酸让入啦式笑赞片酱差像提队走嫩才刚午接重串回晚微周值费性桌拍跟块调糕' gly_list = font_data.getGlyphOrder()[2:] # print(gly_list) # ['unie8a0', 'unie910', 'unif6a4', 'unif3d3', 'unie2f4', 'unie7a6', 'uniea32', 'unif0f9', 'unie2ac'] new_dict = {} for index, value in enumerate(words): new_dict[gly_list[index]] = value print(new_dict) rel = '' for j in datas: if j.startswith('u'): rel += new_dict[j] else: rel += j return rel
def font_name(name): ''' 通过手敲的映射关系,解析字体文件 ''' number_map = { 'eight': '8', 'five': '5', 'one': '1', 'nine': '9', 'period': '?', 'three': '3', 'six': '6', 'two': '2', 'seven': '7', 'four': '4', 'zero': '0' } # 下载下来的font文件 font = TTFont(name) num = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] # 取出来font文件中的zero到nine,从第一个开始 font_num = font.getGlyphOrder()[1:] # print('--------------',font_num) # ['zero', 'one', 'two', 'three', 'four', 'five', 'seven', 'eight', 'six', 'nine'] dic_font = dict(zip(num, font_num)) # print('**************',dic_font) # {'0': 'zero', '1': 'one', '2': 'two', '3': 'three', '4': 'four', '5': 'five', '6': 'seven', '7': 'eight', '8': 'six', '9': 'nine'} dict_num = {} for k, v in dic_font.items(): for x, y in number_map.items(): if dic_font[k] == x: dict_num[y] = k return dict_num
def create_ttf_xml(html): tmp = {} fontstr = html.xpath('//style[1]/text()')[0] pattern = re.compile('^.*?base64,(.*?)\).*?format', re.S) result = re.match(pattern, fontstr) str = result.group(1) dic = fontface.createTtfAndXml(str, False) if dic['ttf'] == '': raise RuntimeError('字体文件不存在!') fonts = TTFont(dic['ttf']) uni_list = fonts.getGlyphOrder()[1:] redis = StrictRedis(host='localhost', port=6379, db=1) for i in uni_list: onlineGlyph = fonts['glyf'][i] newobjstr = create_new_obj(onlineGlyph) if newobjstr == '': continue hashstr = hashlib.md5(newobjstr.encode("utf-8")).hexdigest() try: tmp[i.lower()] = redis.get(hashstr).decode('utf-8') except Exception as ex: # print(ex) tmp[i.lower()] = "unknown code" # if os.path.exists(dic['ttf']): # os.remove(dic['ttf']) return tmp
def test_non_BMP_text_arg_input(self): _, fontpath = self.compile_font( self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") text = tostr(u"A\U0001F6D2", encoding='utf-8') subset.main([fontpath, "--text=%s" % text, "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) self.assertEqual(subsetfont['maxp'].numGlyphs, 3) self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2'])
def compare_bounds(): font1_path = sys.argv[1] font2_path = sys.argv[2] font1 = TTFont(font1_path, fontNumber=0) font2 = TTFont(font2_path, fontNumber=0) for gname in font1.getGlyphOrder(): bounds1 = calc_bounds(font1, gname, BoundsPen) bounds2 = calc_bounds(font2, gname, BoundsPen) if bounds1 is None or bounds2 is None: if bounds1 is not None or bounds2 is not None: print "[{}] {} {}".format(gname, bounds1, bounds2) elif bounds_differ(bounds1, bounds2): print "[{}] {} {}".format(gname, bounds1, bounds2)
def run(self): font = TTFont(self.in_font) gs = font.getGlyphSet() for gname in font.getGlyphOrder(): g = gs[gname]._glyph g.decompile() print("[{}]".format(gname)) operands = [] for b in g.program: if isinstance(b, int): operands.append(b) else: print(" [{}] << {} >>".format(", ".join(map(lambda v: str(v), operands)), b)) operands = [] print(" -----")
def test_non_BMP_text_file_input(self): _, fontpath = self.compile_font( self.getpath("TestTTF-Regular_non_BMP_char.ttx"), ".ttf") subsetpath = self.temp_path(".ttf") text = tobytes(u"A\U0001F6D2", encoding='utf-8') with tempfile.NamedTemporaryFile(delete=False) as tmp: tmp.write(text) try: subset.main([fontpath, "--text-file=%s" % tmp.name, "--output-file=%s" % subsetpath]) subsetfont = TTFont(subsetpath) finally: os.remove(tmp.name) self.assertEqual(subsetfont['maxp'].numGlyphs, 3) self.assertEqual(subsetfont.getGlyphOrder(), ['.notdef', 'A', 'u1F6D2'])
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 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 decompress(ttFont, **kwargs): """ Use the FontTools Subsetter to desubroutinize the font's CFF table. Any keyword arguments are passed on as options to the Subsetter. Skip if the font contains no subroutines. """ if not has_subrs(ttFont): log.debug('No subroutines found; skip decompress') return from fontTools import subset # The FontTools subsetter modifies many tables by default; here # we only want to desubroutinize, so we run the subsetter on a # temporary copy and extract the resulting CFF table from it make_temp = kwargs.pop('make_temp', True) if make_temp: from io import BytesIO from fontTools.ttLib import TTFont, newTable stream = BytesIO() ttFont.save(stream, reorderTables=None) stream.flush() stream.seek(0) tmpfont = TTFont(stream) else: tmpfont = ttFont # run subsetter on the original font options = subset.Options(**kwargs) options.desubroutinize = True options.notdef_outline = True subsetter = subset.Subsetter(options=options) subsetter.populate(glyphs=tmpfont.getGlyphOrder()) subsetter.subset(tmpfont) if make_temp: # copy modified CFF table to original font data = tmpfont['CFF '].compile(tmpfont) table = newTable('CFF ') table.decompile(data, ttFont) ttFont['CFF '] = table tmpfont.close()
def dump_closure_map(fontfile, outputfolder): """Takes closure of each glyph in the font and dump them into the two seperate files. Index file used to locate a glyph in data file. Data file contains closure lists """ font = TTFont(fontfile) closurer = ClosureTaker(font) glyph_metadata = Dumper(outputfolder + "/closure_idx") glyph_data = Dumper(outputfolder + "/closure_data") bigEndian = ">" fmt_offset = ">l" # offset - length fmt_size = ">H" fmt_elem = "H" elem_size = struct.calcsize(fmt_elem) offset = 0 for g in font.getGlyphOrder(): closurer.clear() closurer.add_glyph_names([g]) glyphsClosure = closurer.closure() id = closurer.glyph_name_to_id[g] if len(glyphsClosure) == 1 and id in glyphsClosure: # recording not needed glyph_metadata.dump_fmt(-1, fmt_offset) glyph_metadata.dump_fmt(0, fmt_size) else: size = elem_size * len(glyphsClosure) glyph_data.dump_array(glyphsClosure, fmt_elem, bigEndian) glyph_metadata.dump_fmt(offset, fmt_offset) glyph_metadata.dump_fmt(size, fmt_size) # print id, g, glyphsClosure offset += size font.close() glyph_data.close() glyph_metadata.close()
def test(): # Test program. Takes first argument font file path, optional second argument = glyph name. # use form "cid0769" for CID keys references. from fontTools.ttLib import TTFont path = sys.argv[1] ttFont = TTFont(path) if len(sys.argv) > 2: glyphNames = sys.argv[2:] else: glyphNames = ttFont.getGlyphOrder() cffTable = ttFont["CFF "] topDict = cffTable.cff.topDictIndex[0] charStrings = topDict.CharStrings removeHints = 0 for glyphName in glyphNames: print print glyphName t2CharString = charStrings[glyphName] bezString, hasHints, t2Width = convertT2GlyphToBez(t2CharString, removeHints) #print bezString t2Program = convertBezToT2(bezString) if t2Width != None: t2Program.insert(0,t2Width)
def make_font(feature_source, fea_type='fea'): """Return font with GSUB compiled from given source. Adds a bunch of filler tables so the font can be saved if needed, for debugging purposes. """ # copied from fontTools' feaLib/builder_test. glyphs = """ .notdef space slash fraction semicolon period comma ampersand quotedblleft quotedblright quoteleft quoteright zero one two three four five six seven eight nine zero.oldstyle one.oldstyle two.oldstyle three.oldstyle four.oldstyle five.oldstyle six.oldstyle seven.oldstyle eight.oldstyle nine.oldstyle onequarter onehalf threequarters onesuperior twosuperior threesuperior ordfeminine ordmasculine A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z A.sc B.sc C.sc D.sc E.sc F.sc G.sc H.sc I.sc J.sc K.sc L.sc M.sc N.sc O.sc P.sc Q.sc R.sc S.sc T.sc U.sc V.sc W.sc X.sc Y.sc Z.sc A.alt1 A.alt2 A.alt3 B.alt1 B.alt2 B.alt3 C.alt1 C.alt2 C.alt3 a.alt1 a.alt2 a.alt3 a.end b.alt c.mid d.alt d.mid e.begin e.mid e.end m.begin n.end s.end z.end Eng Eng.alt1 Eng.alt2 Eng.alt3 A.swash B.swash C.swash D.swash E.swash F.swash G.swash H.swash I.swash J.swash K.swash L.swash M.swash N.swash O.swash P.swash Q.swash R.swash S.swash T.swash U.swash V.swash W.swash X.swash Y.swash Z.swash f_l c_h c_k c_s c_t f_f f_f_i f_f_l f_i o_f_f_i s_t f_i.begin a_n_d T_h T_h.swash germandbls ydieresis yacute breve grave acute dieresis macron circumflex cedilla umlaut ogonek caron damma hamza sukun kasratan lam_meem_jeem noon.final noon.initial by feature lookup sub table """.split() font = TTFont() font.setGlyphOrder(glyphs) glyph_order = font.getGlyphOrder() font['cmap'] = cmap = newTable('cmap') table = cmap_format_4(4) table.platformID = 3 table.platEncID = 1 table.language = 0 table.cmap = {AGL2UV[n]: n for n in glyph_order if n in AGL2UV} cmap.tableVersion = 0 cmap.tables = [table] font['glyf'] = glyf = newTable('glyf') glyf.glyphs = {} glyf.glyphOrder = glyph_order for name in glyph_order: pen = TTGlyphPen(None) glyf[name] = pen.glyph() font['head'] = head = newTable('head') head.tableVersion = 1.0 head.fontRevision = 1.0 head.flags = head.checkSumAdjustment = head.magicNumber =\ head.created = head.modified = head.macStyle = head.lowestRecPPEM =\ head.fontDirectionHint = head.indexToLocFormat =\ head.glyphDataFormat =\ head.xMin = head.xMax = head.yMin = head.yMax = 0 head.unitsPerEm = 1000 font['hhea'] = hhea = newTable('hhea') hhea.tableVersion = 0x00010000 hhea.ascent = hhea.descent = hhea.lineGap =\ hhea.caretSlopeRise = hhea.caretSlopeRun = hhea.caretOffset =\ hhea.reserved0 = hhea.reserved1 = hhea.reserved2 = hhea.reserved3 =\ hhea.metricDataFormat = hhea.advanceWidthMax = hhea.xMaxExtent =\ hhea.minLeftSideBearing = hhea.minRightSideBearing =\ hhea.numberOfHMetrics = 0 font['hmtx'] = hmtx = newTable('hmtx') hmtx.metrics = {} for name in glyph_order: hmtx[name] = (600, 50) font['loca'] = newTable('loca') font['maxp'] = maxp = newTable('maxp') maxp.tableVersion = 0x00005000 maxp.numGlyphs = 0 font['post'] = post = newTable('post') post.formatType = 2.0 post.extraNames = [] post.mapping = {} post.glyphOrder = glyph_order post.italicAngle = post.underlinePosition = post.underlineThickness =\ post.isFixedPitch = post.minMemType42 = post.maxMemType42 =\ post.minMemType1 = post.maxMemType1 = 0 if fea_type == 'fea': addOpenTypeFeaturesFromString(font, feature_source) elif fea_type == 'mti': font['GSUB'] = mtiLib.build(UnicodeIO(feature_source), font) return font
class ShapeDiffFinder: """Provides methods to report diffs in glyph shapes between OT Fonts.""" def __init__( self, file_a, file_b, stats, ratio_diffs=False, diff_threshold=0): self.path_a = file_a self.font_a = TTFont(self.path_a) self.glyph_set_a = self.font_a.getGlyphSet() self.gdef_a = {} if 'GDEF' in self.font_a: self.gdef_a = self.font_a['GDEF'].table.GlyphClassDef.classDefs self.path_b = file_b self.font_b = TTFont(self.path_b) self.glyph_set_b = self.font_b.getGlyphSet() self.gdef_b = {} if 'GDEF' in self.font_b: self.gdef_b = self.font_b['GDEF'].table.GlyphClassDef.classDefs for stat_type in ( 'compared', 'untested', 'unmatched', 'unicode_mismatch', 'gdef_mark_mismatch', 'zero_width_mismatch', 'input_mismatch'): if stat_type not in stats: stats[stat_type] = [] self.stats = stats self.ratio_diffs = ratio_diffs self.diff_threshold = diff_threshold self.basepath = os.path.basename(file_a) def find_area_diffs(self): """Report differences in glyph areas.""" self.build_names() pen_a = GlyphAreaPen(self.glyph_set_a) pen_b = GlyphAreaPen(self.glyph_set_b) mismatched = {} for name in self.names: self.glyph_set_a[name].draw(pen_a) area_a = pen_a.pop() self.glyph_set_b[name].draw(pen_b) area_b = pen_b.pop() if area_a != area_b: mismatched[name] = (area_a, area_b) stats = self.stats['compared'] calc = self._calc_ratio if self.ratio_diffs else self._calc_diff for name, areas in mismatched.items(): stats.append((calc(areas), name, self.basepath, areas[0], areas[1])) def find_rendered_diffs(self, font_size=128, render_path=None): """Find diffs of glyphs as rendered by harfbuzz.""" hb_input_generator_a = hb_input.HbInputGenerator(self.font_a) hb_input_generator_b = hb_input.HbInputGenerator(self.font_b) if render_path: font_name, _ = os.path.splitext(self.basepath) render_path = os.path.join(render_path, font_name) if not os.path.exists(render_path): os.makedirs(render_path) self.build_names() diffs = [] for name in self.names: class_a = self.gdef_a.get(name, GDEF_UNDEF) class_b = self.gdef_b.get(name, GDEF_UNDEF) if GDEF_MARK in (class_a, class_b) and class_a != class_b: self.stats['gdef_mark_mismatch'].append(( self.basepath, name, GDEF_LABELS[class_a], GDEF_LABELS[class_b])) continue width_a = self.glyph_set_a[name].width width_b = self.glyph_set_b[name].width zwidth_a = width_a == 0 zwidth_b = width_b == 0 if zwidth_a != zwidth_b: self.stats['zero_width_mismatch'].append(( self.basepath, name, width_a, width_b)) continue hb_args_a = hb_input_generator_a.input_from_name(name, pad=zwidth_a) hb_args_b = hb_input_generator_b.input_from_name(name, pad=zwidth_b) if hb_args_a != hb_args_b: self.stats['input_mismatch'].append(( self.basepath, name, hb_args_a, hb_args_b)) continue # ignore unreachable characters if not hb_args_a: self.stats['untested'].append((self.basepath, name)) continue features, text = hb_args_a # ignore null character if unichr(0) in text: continue img_file_a = StringIO.StringIO(subprocess.check_output([ 'hb-view', '--font-size=%d' % font_size, '--features=%s' % ','.join(features), self.path_a, text])) img_file_b = StringIO.StringIO(subprocess.check_output([ 'hb-view', '--font-size=%d' % font_size, '--features=%s' % ','.join(features), self.path_b, text])) img_a = Image.open(img_file_a) img_b = Image.open(img_file_b) width_a, height_a = img_a.size width_b, height_b = img_b.size data_a = img_a.getdata() data_b = img_b.getdata() img_file_a.close() img_file_b.close() width, height = max(width_a, width_b), max(height_a, height_b) offset_ax = (width - width_a) // 2 offset_ay = (height - height_a) // 2 offset_bx = (width - width_b) // 2 offset_by = (height - height_b) // 2 diff = 0 for y in range(height): for x in range(width): ax, ay = x - offset_ax, y - offset_ay bx, by = x - offset_bx, y - offset_by if (ax < 0 or bx < 0 or ax >= width_a or bx >= width_b or ay < 0 or by < 0 or ay >= height_a or by >= height_b): diff += 1 else: diff += abs(data_a[ax + ay * width_a] - data_b[bx + by * width_b]) / 255 if self.ratio_diffs: diff /= (width * height) if render_path and diff > self.diff_threshold: img_cmp = Image.new('RGB', (width, height)) data_cmp = list(img_cmp.getdata()) self._project(data_a, width_a, height_a, data_cmp, width, height, 1) self._project(data_b, width_b, height_b, data_cmp, width, height, 0) for y in range(height): for x in range(width): i = x + y * width r, g, b = data_cmp[i] assert b == 0 data_cmp[i] = r, g, min(r, g) img_cmp.putdata(data_cmp) img_cmp.save(self._rendered_png(render_path, name)) diffs.append((name, diff)) mismatched = {} for name, diff in diffs: if diff > self.diff_threshold: mismatched[name] = diff stats = self.stats['compared'] for name, diff in mismatched.items(): stats.append((diff, name, self.basepath)) def _project( self, src_data, src_width, src_height, dst_data, width, height, channel): """Project a single-channel image onto a channel of an RGB image.""" offset_x = (width - src_width) // 2 offset_y = (height - src_height) // 2 for y in range(src_height): for x in range(src_width): src_i = x + y * src_width dst_i = x + offset_x + (y + offset_y) * width pixel = list(dst_data[dst_i]) pixel[channel] = src_data[src_i] dst_data[dst_i] = tuple(pixel) def find_shape_diffs(self): """Report differences in glyph shapes, using BooleanOperations.""" self.build_names() area_pen = GlyphAreaPen(None) pen = PointToSegmentPen(area_pen) mismatched = {} for name in self.names: glyph_a = Glyph() glyph_b = Glyph() self.glyph_set_a[name].draw( Qu2CuPen(glyph_a.getPen(), self.glyph_set_a)) self.glyph_set_b[name].draw( Qu2CuPen(glyph_b.getPen(), self.glyph_set_b)) booleanOperations.xor(list(glyph_a), list(glyph_b), pen) area = abs(area_pen.pop()) if area: mismatched[name] = (area) stats = self.stats['compared'] for name, area in mismatched.items(): stats.append((area, name, self.basepath)) def find_area_shape_diff_products(self): """Report product of differences in glyph areas and glyph shapes.""" self.find_area_diffs() old_compared = self.stats['compared'] self.stats['compared'] = [] self.find_shape_diffs() new_compared = {n: d for d, n, _ in self.stats['compared']} for i, (diff, name, font, area_a, area_b) in enumerate(old_compared): if font != self.basepath: continue new_diff = diff * new_compared.get(name, 0) old_compared[i] = new_diff, name, font, area_a, area_b self.stats['compared'] = old_compared def build_names(self): """Build a list of glyph names shared between the fonts.""" if hasattr(self, 'names'): return stats = self.stats['unmatched'] names_a = set(self.font_a.getGlyphOrder()) names_b = set(self.font_b.getGlyphOrder()) if names_a != names_b: stats.append((self.basepath, names_a - names_b, names_b - names_a)) self.names = names_a & names_b stats = self.stats['unicode_mismatch'] reverse_cmap_a = hb_input.build_reverse_cmap(self.font_a) reverse_cmap_b = hb_input.build_reverse_cmap(self.font_b) mismatched = {} for name in self.names: unival_a = reverse_cmap_a.get(name) unival_b = reverse_cmap_b.get(name) if unival_a != unival_b: mismatched[name] = (unival_a, unival_b) if mismatched: stats.append((self.basepath, mismatched.items())) self.names -= set(mismatched.keys()) @staticmethod def dump(stats, whitelist, out_lines, include_vals, multiple_fonts): """Return the results of run diffs. Args: stats: List of tuples with diff data which is sorted and printed. whitelist: Names of glyphs to exclude from report. out_lines: Number of diff lines to print. include_vals: Include the values that have been diffed in report. multiple_fonts: Designates whether stats have been accumulated from multiple fonts, if so then font names will be printed as well. """ report = [] compared = sorted( s for s in stats['compared'] if s[1] not in whitelist) compared.reverse() fmt = '%s %s' if include_vals: fmt += ' (%s vs %s)' if multiple_fonts: fmt = '%s ' + fmt report.append('%d differences in glyph shape' % len(compared)) for line in compared[:out_lines]: # print <font> <glyph> <vals>; stats are sorted in reverse priority line = tuple(reversed(line[:3])) + tuple(line[3:]) # ignore font name if just one pair of fonts was compared if not multiple_fonts: line = line[1:] report.append(fmt % line) report.append('') for font, set_a, set_b in stats['unmatched']: report.append("Glyph coverage doesn't match in %s" % font) report.append(' in A but not B: %s' % sorted(set_a)) report.append(' in B but not A: %s' % sorted(set_b)) report.append('') for font, mismatches in stats['unicode_mismatch']: report.append("Glyph unicode values don't match in %s" % font) for name, univals in sorted(mismatches): univals = [(('0x%04X' % v) if v else str(v)) for v in univals] report.append(' %s: %s in A, %s in B' % (name, univals[0], univals[1])) report.append('') ShapeDiffFinder._add_simple_report( report, stats['gdef_mark_mismatch'], '%s: Mark class mismatch for %s (%s vs %s)') ShapeDiffFinder._add_simple_report( report, stats['zero_width_mismatch'], '%s: Zero-width mismatch for %s (%d vs %d)') ShapeDiffFinder._add_simple_report( report, stats['input_mismatch'], '%s: Harfbuzz input mismatch for %s (%s vs %s)') ShapeDiffFinder._add_simple_report( report, stats['untested'], '%s: %s not tested (unreachable?)') return '\n'.join(report) @staticmethod def _add_simple_report(report, stats, fmt): for stat in sorted(stats): report.append(fmt % stat) if stats: report.append('') def _calc_diff(self, vals): """Calculate an area difference.""" a, b = vals return abs(a - b) def _calc_ratio(self, vals): """Calculate an area ratio.""" a, b = vals if not (a or b): return 0 if abs(a) > abs(b): a, b = b, a return 1 - a / b def _rendered_png(self, render_path, glyph_name): glyph_filename = re.sub(r'([A-Z_])', r'\1_', glyph_name) + '.png' return os.path.join(render_path, glyph_filename)
class OTFPostProcessor(object): """Does some post-processing operations on a compiled OpenType font, using info from the source UFO where necessary. """ def __init__(self, otf, ufo): self.ufo = ufo stream = BytesIO() otf.save(stream) stream.seek(0) self.otf = TTFont(stream) def process(self, useProductionNames=True, optimizeCff=True): if useProductionNames: self._rename_glyphs_from_ufo() if optimizeCff and 'CFF ' in self.otf: from compreffor import Compreffor comp = Compreffor(self.otf) comp.compress() return self.otf def _rename_glyphs_from_ufo(self): """Rename glyphs using glif.lib.public.postscriptNames in UFO.""" rename_map = { g.name: self._build_production_name(g) for g in self.ufo} # .notdef may not be present in the original font rename_map[".notdef"] = ".notdef" rename = lambda names: [rename_map[n] for n in names] self.otf.setGlyphOrder(rename(self.otf.getGlyphOrder())) if 'CFF ' in self.otf: cff = self.otf['CFF '].cff.topDictIndex[0] char_strings = cff.CharStrings.charStrings cff.CharStrings.charStrings = { rename_map.get(n, n): v for n, v in char_strings.items()} cff.charset = rename(cff.charset) def _build_production_name(self, glyph): """Build a production name for a single glyph.""" # use name from Glyphs source if available production_name = glyph.lib.get('public.postscriptName') if production_name: return production_name # use name derived from unicode value unicode_val = glyph.unicode if glyph.unicode is not None: return '%s%04X' % ( 'u' if unicode_val > 0xffff else 'uni', unicode_val) # use production name + last (non-script) suffix if possible parts = glyph.name.rsplit('.', 1) if len(parts) == 2 and parts[0] in self.ufo: return '%s.%s' % ( self._build_production_name(self.ufo[parts[0]]), parts[1]) # use ligature name, making sure to look up components with suffixes parts = glyph.name.split('.', 1) if len(parts) == 2: liga_parts = ['%s.%s' % (n, parts[1]) for n in parts[0].split('_')] else: liga_parts = glyph.name.split('_') if len(liga_parts) > 1 and all(n in self.ufo for n in liga_parts): unicode_vals = [self.ufo[n].unicode for n in liga_parts] if all(v and v <= 0xffff for v in unicode_vals): return 'uni' + ''.join('%04X' % v for v in unicode_vals) return '_'.join( self._build_production_name(self.ufo[n]) for n in liga_parts) return glyph.name
class Font(LayoutEngine): def __init__(self, path, glyphClass=None): super(Font, self).__init__() self.path = path self._glyphs = {} if isinstance(path, TTFont): self.source = path else: self.source = TTFont(path) self.loadGlyphSet() self.loadCMAP() self.loadFeatures() self.loadInfo() if glyphClass is None: glyphClass = Glyph self.glyphClass = glyphClass def __del__(self): del self._glyphs self.source.close() del self.source # -------------- # initialization # -------------- def loadCMAP(self): cmap = extractCMAP(self.source) self.setCMAP(cmap) def loadGlyphSet(self): self.glyphSet = self.source.getGlyphSet() # the glyph order will be needed later # to assign the proper glyph index to # glyph objects. order = self.source.getGlyphOrder() self._glyphOrder = {} for index, glyphName in enumerate(order): self._glyphOrder[glyphName] = index def loadInfo(self): self.info = info = Info() head = self.source["head"] hhea = self.source["hhea"] os2 = self.source["OS/2"] info.unitsPerEm = head.unitsPerEm info.ascender = hhea.ascent info.descender = hhea.descent info.xHeight = os2.sxHeight info.capHeight = os2.sCapHeight # names nameIDs = {} for nameRecord in self.source["name"].names: nameID = nameRecord.nameID platformID = nameRecord.platformID platEncID = nameRecord.platEncID langID = nameRecord.langID nameIDs[nameID, platformID, platEncID, langID] = nameRecord.toUnicode() # to retrieve the family and style names, first start # with the preferred name entries and progress to less # specific entries until something is found. familyPriority = [(16, 1, 0, 0), (16, 1, None, None), (16, None, None, None), (1, 1, 0, 0), (1, 1, None, None), (1, None, None, None)] familyName = self._skimNameIDs(nameIDs, familyPriority) stylePriority = [(17, 1, 0, 0), (17, 1, None, None), (17, None, None, None), (2, 1, 0, 0), (2, 1, None, None), (2, None, None, None)] styleName = self._skimNameIDs(nameIDs, stylePriority) if familyName is None or styleName is None: raise CompositorError("Could not extract name data from name table.") self.info.familyName = familyName self.info.styleName = styleName # stylistic set names self.stylisticSetNames = {} if self.gsub: for featureRecord in self.gsub.FeatureList.FeatureRecord: params = featureRecord.Feature.FeatureParams if hasattr(params, "UINameID"): ssNameID = params.UINameID namePriority = [(ssNameID, 1, 0, 0), (ssNameID, 1, None, None), (ssNameID, 3, 1, 1033), (ssNameID, 3, None, None)] ssName = self._skimNameIDs(nameIDs, namePriority) if ssName: self.stylisticSetNames[featureRecord.FeatureTag] = ssName def _skimNameIDs(self, nameIDs, priority): for (nameID, platformID, platEncID, langID) in priority: for (nID, pID, pEID, lID), text in nameIDs.items(): if nID != nameID: continue if pID != platformID and platformID is not None: continue if pEID != platEncID and platEncID is not None: continue if lID != langID and langID is not None: continue return text def loadFeatures(self): gdef = None if "GDEF" in self.source: gdef = self.source["GDEF"] gsub = None if "GSUB" in self.source: gsub = self.source["GSUB"] gpos = None if "GPOS" in self.source: gpos = self.source["GPOS"] self.setFeatureTables(gdef, gsub, gpos) # ------------- # dict behavior # ------------- def keys(self): return self.glyphSet.keys() def __contains__(self, name): return name in self.glyphSet def __getitem__(self, name): if name not in self._glyphs: if name not in self.glyphSet: name = self.fallbackGlyph glyph = self.glyphSet[name] index = self._glyphOrder[name] glyph = self.glyphClass(name, index, glyph, self) self._glyphs[name] = glyph return self._glyphs[name] # ----------------- # string processing # ----------------- def stringToGlyphNames(self, string): glyphNames = [] for c in string: c = unicode(c) v = ord(c) if v in self.cmap: glyphNames.append(self.cmap[v]) elif self.fallbackGlyph is not None: glyphNames.append(self.fallbackGlyph) return glyphNames def stringToGlyphRecords(self, string): return [GlyphRecord(glyphName) for glyphName in self.stringToGlyphNames(string)] def didProcessingGSUB(self, glyphRecords): for glyphRecord in glyphRecords: glyphRecord.advanceWidth += self[glyphRecord.glyphName].width # ------------- # Miscellaneous # ------------- def getGlyphOrder(self): return self.source.getGlyphOrder()
def TTFAutohint(sourcePath, destinationPath, options=dict()): """ Options: --debug print debugging information -f, --latin-fallback set fallback script to latin -G, --hinting-limit=N switch off hinting above this PPEM value (default: 200); value 0 means no limit -h, --help display this help and exit -i, --ignore-restrictions override font license restrictions -l, --hinting-range-min=N the minimum PPEM value for hint sets (default: 8) -n --no-info don't add ttfautohint info to the version string(s) in the `name' table -p, --pre-hinting apply original hints in advance -r, --hinting-range-max=N the maximum PPEM value for hint sets (default: 50) -s, --symbol input is symbol font -v, --verbose show progress information -V, --version print version information and exit -w, --strong-stem-width=S use strong stem width routine for modes S, where S is a string of up to three letters with possible values `g' for grayscale, `G' for GDI ClearType, and `D' for DirectWrite ClearType (default: G) -x, --increase-x-height=N increase x height for sizes in the range 6<=PPEM<=N; value 0 switches off this feature (default: 14) -X, --x-height-snapping-exceptions=STRING specify a comma-separated list of x-height snapping exceptions """ updateWithDefaultValues(options, defaultOptions) hintRangeMinimum = str(options["hintRangeMinimum"]) hintRangeMaximum = str(options["hintRangeMaximum"]) fallbackScript = options["fallbackScript"] hintingLimit = options["hintingLimit"] noHintingLimit = options["noHintingLimit"] if noHintingLimit: hintingLimit = 0 hintingLimit = str(hintingLimit) xHeightIncreaseLimit = options["xHeightIncreaseLimit"] noXHeightIncreaseLimit = options["noXHeightIncreaseLimit"] if noXHeightIncreaseLimit: xHeightIncreaseLimit = 0 xHeightIncreaseLimit = str(xHeightIncreaseLimit) preHinting = options["preHinting"] symbolFont = options["symbolFont"] if not symbolFont: f = TTFont(sourcePath) symbolFont = "o" not in f.getGlyphOrder() f.close() addTTFAutoHintInfo = options["addTTFAutoHintInfo"] overRideFontLicense = options["overRideFontLicense"] grayScale = options["grayScale"] if grayScale: grayScale = "g" else: grayScale = "" gdiClearType = options["gdiClearType"] if gdiClearType: gdiClearType = "G" else: gdiClearType = "" dwClearType = options["dwClearType"] if dwClearType: dwClearType = "D" else: dwClearType = "" cmd = [ttfautohint] cmd.extend(["-G", hintingLimit]) cmd.extend(["-l", hintRangeMinimum]) cmd.extend(["-r", hintRangeMaximum]) cmd.extend(["-x", xHeightIncreaseLimit]) if fallbackScript: cmd.append("-f") if not addTTFAutoHintInfo: cmd.append("-n") if preHinting: cmd.append("-p") if symbolFont: cmd.append("-s") if not overRideFontLicense: cmd.append("-i") cmd.extend(["-w", grayScale + gdiClearType + dwClearType]) cmd.extend([sourcePath, destinationPath]) result = executeCommand(cmd) return result
class CffSerializer(object): """Serializes 'cff' table for given font file """ # formats fmt_CffTable = '>HH' # flags NONE = 0 HAS_HMTX = 1 << 0 HAS_VMTX = 1 << 1 CFF_FONT = 1 << 2 CMP_NONE = NONE CMP_GZIP = (1 << 2) + NONE CMP_BROTLI = (1 << 2) + (1 << 2) CMP_LZMA = (1 << 2) + (1 << 3) CLEAN = NONE DIRTY = (1 << 6) def __init__(self, fontfile): self.font = TTFont(fontfile) assert 'CFF ' in self.font self.cffTableOffset = self.font.reader.tables['CFF '].offset cffTable = self.font['CFF '].cff assert len(cffTable.fontNames) == 1 charStringOffset = cffTable[cffTable.fontNames[0]].rawDict['CharStrings'] inner_file = self.font.reader.file inner_file.seek(self.cffTableOffset + charStringOffset) self.rawIndexFile = Index(inner_file) def __determine_mtx_fmt(self): self.fmt_mtx = '' self.has_hmtx_ = CffSerializer.HAS_HMTX if ('hmtx' in self.font)\ else CffSerializer.NONE if self.has_hmtx_: self.fmt_mtx += 'h' self.HMTX = self.font['hmtx'] self.has_vmtx_ = CffSerializer.HAS_VMTX if ('vmtx' in self.font)\ else CffSerializer.NONE if self.has_vmtx_: self.fmt_mtx += 'h' self.VMTX = self.font['vmtx'] def prepare_cff(self): """Prepare Cff table and table entries along with Cff CharString data """ self.__determine_mtx_fmt() self.fmt_CffEntry = '>H' + self.fmt_mtx + 'LH' assert 'maxp' in self.font numGlyphs = self.font['maxp'].numGlyphs self.CffTable = ( pack( CffSerializer.fmt_CffTable, ( self.has_hmtx_ | self.has_vmtx_ | CffSerializer.CLEAN | CffSerializer.CFF_FONT), numGlyphs)) self.glyphs_info = [] self.glyphs_data = [] glyphOrder = self.font.getGlyphOrder() cff_data_table_start = self.rawIndexFile.offsetBase offset_table = self.rawIndexFile.offsets for i in xrange(numGlyphs): offset = offset_table[i] length = offset_table[i + 1] - offset_table[i] self.glyphs_data.append(self.rawIndexFile[i]) args = [i] if self.has_hmtx_: args.append(self.HMTX.metrics[glyphOrder[i]][1]) if self.has_vmtx_: args.append(self.VMTX.metrics[glyphOrder[i]][1]) args.append(offset) args.append(length) # print i, glyphOrder[i], offset, length self.glyphs_info.append(pack(self.fmt_CffEntry, *args)) self.cffReady = True def serialize_cff(self, output_idx, output_data): """Dump the Glyf data to the file """ if self.cffReady: dumper = Dumper(output_idx) dumper.dump(self.CffTable) dumper.dump_for_each(self.glyphs_info) dumper.close() dumper = Dumper(output_data) dumper.dump_for_each(self.glyphs_data) dumper.close() def close(self): self.font.close()
class FontFile(object): """Representation of font metadata. Provides high-level API on top of FontTools library that is used by report generators. """ NAME_CODES = {'Copyright': 0, 'Family': 1, 'Subfamily': 2, 'Full Name': 4, 'Version': 5, 'PostScrpt Name': 6, 'Trademark': 7, 'Manufacturer': 8, 'Designer': 9, 'Description': 10, 'Vendor URL': 11, 'Designer URL': 12, 'License': 13, 'License URL': 14, 'Sample Text': 19} def __init__(self, filename): self.filename = filename self.ttf = TTFont(filename, fontNumber=-1, lazy=False) self._names = {} self.chars = {} self._glyphsmap = {} self.glyphs = [] self.features = {} self.caret_list = {} self.substitutes = set() self.caret_list = {} self._ParseNames() self._ParseCmap() self._ParseGSUB() self._ParseGlyphs() def _ParseNames(self): if 'name' in self.ttf: for name in self.ttf['name'].names: if name.isUnicode(): text = name.string.decode('utf-16be') else: text = name.string.decode('latin1') if name.nameID not in self._names: self._names[name.nameID] = text def GetTables(self): return sorted(self.ttf.reader.keys()) def GetTitle(self): title = self.GetName('Full Name') if not title: title = self.GetName('Family') + ' ' + self.GetName('Subfamily') return title def GetAuthor(self): author = self.GetName('Designer') manufacturer = self.GetName('Manufacturer') if author and manufacturer and author != manufacturer: return '%s (%s)' % (author, manufacturer) elif author: return author elif manufacturer: return manufacturer else: return 'Author is not set' def GetFeaturesByTable(self): mapping = {} for key, scripts in self.features.items(): feature, tables = key for table in tables: if table not in mapping: mapping[table] = set() mapping[table].add((feature, tuple(sorted(scripts)))) return mapping def _ParseCmap(self): if 'cmap' in self.ttf: for table in self.ttf['cmap'].tables: if table.isUnicode(): for code, name in table.cmap.items(): self.chars[code] = name def _ParseGSUB(self): if 'GSUB' not in self.ttf: return scripts = [set() for unused_x in range(self.ttf['GSUB'].table.FeatureList.FeatureCount)] # Find scripts defined in a font for script in self.ttf['GSUB'].table.ScriptList.ScriptRecord: if script.Script.DefaultLangSys: for idx in script.Script.DefaultLangSys.FeatureIndex: scripts[idx].add(script.ScriptTag) for lang in script.Script.LangSysRecord: for idx in lang.LangSys.FeatureIndex: scripts[idx].add(script.ScriptTag + '-' + lang.LangSysTag) # Find all featrures defined in a font for idx, feature in enumerate( self.ttf['GSUB'].table.FeatureList.FeatureRecord): key = (feature.FeatureTag, tuple(feature.Feature.LookupListIndex)) if key not in self.features: self.features[key] = set() self.features[key].update(scripts[idx]) for idx, lookup in enumerate(self.ttf['GSUB'].table.LookupList.Lookup): for sub in lookup.SubTable: if sub.LookupType == 1: for k, v in sub.mapping.items(): self.substitutes.add(((k,), ((v,),), idx, 1)) elif sub.LookupType == 2: for k, v in sub.mapping.items(): self.substitutes.add(((k,), (tuple(v),), idx, 1)) elif sub.LookupType == 3: for k, v in sub.alternates.items(): self.substitutes.add(((k,), tuple((x,) for x in v), idx, 3)) elif sub.LookupType == 4: for key, value in sub.ligatures.items(): for component in value: sequence = tuple([key] + component.Component) glyph = component.LigGlyph self.substitutes.add((sequence, ((glyph,),), idx, 4)) else: print('Lookup table %d: type %s not yet supported.' % ( idx, sub.LookupType)) def _ParseGlyphs(self): """Fetch available glyphs.""" class_defs = {} class_names = {2: 'ligature', 3: 'mark', 4: 'component'} metrics = {} if 'GDEF' in self.ttf: class_defs = self.ttf['GDEF'].table.GlyphClassDef.classDefs caret_list = self.ttf['GDEF'].table.LigCaretList if caret_list: carets = [tuple(str(x.Coordinate) for x in y.CaretValue) for y in caret_list.LigGlyph] self.caret_list = dict(zip(caret_list.Coverage.glyphs, carets)) if 'hmtx' in self.ttf: metrics = self.ttf['hmtx'].metrics for idx, name in enumerate(self.ttf.getGlyphOrder()): glyph = Glyph(name) glyph.index = idx glyph.advance_width, glyph.lsb = metrics.get(name, [None, None]) glyph.class_def = class_defs.get(name, 0) glyph.class_name = class_names.get(glyph.class_def, None) self.glyphs.append(glyph) self._glyphsmap[name] = glyph for k, v in self.chars.items(): try: self._glyphsmap[v].chars.append(k) except KeyError: print('%s is mapped to non-existent glyph %s' % (k, v)) def GetName(self, name, default=None): return self._names.get(self.NAME_CODES[name], default) def GetNames(self): return ['%d: %s' % (k, v) for k, v in sorted(self._names.items())] def GetGlyph(self, name): return self._glyphsmap[name]
class Font(object): def __init__(self, path, glyphClass=None): self.path = path self.fallbackGlyph = ".notdef" self._glyphs = {} if isinstance(path, TTFont): self.source = path else: self.source = TTFont(path) self.loadGlyphSet() self.loadInfo() self.loadCMAP() self.loadFeatures() if glyphClass is None: glyphClass = Glyph self.glyphClass = glyphClass def __del__(self): del self._glyphs self.source.close() del self.source # -------------- # initialization # -------------- def loadCMAP(self): self.cmap = extractCMAP(self.source) self.reversedCMAP = reverseCMAP(self.cmap) def loadGlyphSet(self): self.glyphSet = self.source.getGlyphSet() # the glyph order will be needed later # to assign the proper glyph index to # glyph objects. order = self.source.getGlyphOrder() self._glyphOrder = {} for index, glyphName in enumerate(order): self._glyphOrder[glyphName] = index def loadInfo(self): self.info = info = Info() head = self.source["head"] hhea = self.source["hhea"] os2 = self.source["OS/2"] info.unitsPerEm = head.unitsPerEm info.ascender = hhea.ascent info.descender = hhea.descent info.xHeight = os2.sxHeight info.capHeight = os2.sCapHeight # names nameIDs = {} for nameRecord in self.source["name"].names: nameID = nameRecord.nameID platformID = nameRecord.platformID platEncID = nameRecord.platEncID langID = nameRecord.langID text = nameRecord.string nameIDs[nameID, platformID, platEncID, langID] = text # to retrive the family and style names, first start # with the preferred name entries and progress to less # specific entries until something is found. familyPriority = [(16, 1, 0, 0), (16, 1, None, None), (16, None, None, None), (1, 1, 0, 0), (1, 1, None, None), (1, None, None, None)] familyName = self._skimNameIDs(nameIDs, familyPriority) stylePriority = [(17, 1, 0, 0), (17, 1, None, None), (17, None, None, None), (2, 1, 0, 0), (2, 1, None, None), (2, None, None, None)] styleName = self._skimNameIDs(nameIDs, stylePriority) if familyName is None or styleName is None: raise CompositorError("Could not extract name data from name table.") self.info.familyName = familyName self.info.styleName = styleName def _skimNameIDs(self, nameIDs, priority): for (nameID, platformID, platEncID, langID) in priority: for (nID, pID, pEID, lID), text in nameIDs.items(): if nID != nameID: continue if pID != platformID and platformID is not None: continue if pEID != platEncID and platEncID is not None: continue if lID != langID and langID is not None: continue # make sure there are no endian issues # XXX right way to do this? text = "".join([i for i in text if i != "\x00"]) return text def loadFeatures(self): self.gsub = None self.gpos = None self.gdef = None if self.source.has_key("GDEF"): self.gdef = GDEF().loadFromFontTools(self.source["GDEF"]) if self.source.has_key("GSUB"): self.gsub = GSUB().loadFromFontTools(self.source["GSUB"], self.reversedCMAP, self.gdef) if self.source.has_key("GPOS"): self.gpos = GPOS().loadFromFontTools(self.source["GPOS"], self.reversedCMAP, self.gdef) # ------------- # dict behavior # ------------- def keys(self): return self.glyphSet.keys() def __contains__(self, name): return self.glyphSet.has_key(name) def __getitem__(self, name): if name not in self._glyphs: glyph = self.glyphSet[name] index = self._glyphOrder[name] glyph = self.glyphClass(name, index, glyph, self) self._glyphs[name] = glyph return self._glyphs[name] # ----------------- # string processing # ----------------- def stringToGlyphNames(self, string): glyphNames = [] for c in string: c = unicode(c) v = ord(c) if v in self.cmap: glyphNames.append(self.cmap[v]) elif self.fallbackGlyph is not None: glyphNames.append(self.fallbackGlyph) return glyphNames def stringToGlyphRecords(self, string): return [GlyphRecord(glyphName) for glyphName in self.stringToGlyphNames(string)] def glyphListToGlyphRecords(self, glyphList): glyphRecords = [] for glyphName in glyphList: if glyphName not in self: if self.fallbackGlyph is None: continue glyphName = self.fallbackGlyph record = GlyphRecord(glyphName) glyphRecords.append(record) return glyphRecords def process(self, stringOrGlyphList, script="latn", langSys=None, rightToLeft=False, case="unchanged", logger=None): if isinstance(stringOrGlyphList, basestring): stringOrGlyphList = self.stringToGlyphNames(stringOrGlyphList) if case != "unchanged": l = langSys if l is not None: l = l.strip() stringOrGlyphList = convertCase(case, stringOrGlyphList, self.cmap, self.reversedCMAP, l, self.fallbackGlyph) glyphRecords = self.glyphListToGlyphRecords(stringOrGlyphList) if rightToLeft: glyphRecords.reverse() if logger: logger.logStart() glyphNames = [r.glyphName for r in glyphRecords] logger.logMainSettings(glyphNames, script, langSys) if self.gsub is not None: if logger: logger.logTableStart(self.gsub) glyphRecords = self.gsub.process(glyphRecords, script=script, langSys=langSys, logger=logger) if logger: logger.logResults(glyphRecords) logger.logTableEnd() advancedRecords = [] for glyphRecord in glyphRecords: glyphRecord.advanceWidth = self[glyphRecord.glyphName].width advancedRecords.append(glyphRecord) glyphRecords = advancedRecords if self.gpos is not None: if logger: logger.logTableStart(self.gpos) glyphRecords = self.gpos.process(glyphRecords, script=script, langSys=langSys, logger=logger) if logger: logger.logResults(glyphRecords) logger.logTableEnd() if logger: logger.logEnd() return glyphRecords # ------------------ # feature management # ------------------ def getScriptList(self): gsub = [] gpos = [] if self.gsub is not None: gsub = self.gsub.getScriptList() if self.gpos is not None: gpos = self.gpos.getScriptList() return sorted(set(gsub + gpos)) def getLanguageList(self): gsub = [] gpos = [] if self.gsub is not None: gsub = self.gsub.getLanguageList() if self.gpos is not None: gpos = self.gpos.getLanguageList() return sorted(set(gsub + gpos)) def getFeatureList(self): gsub = [] gpos = [] if self.gsub is not None: gsub = self.gsub.getFeatureList() if self.gpos is not None: gpos = self.gpos.getFeatureList() return sorted(set(gsub + gpos)) def getFeatureState(self, featureTag): gsubState = None gposState = None if self.gsub is not None: if featureTag in self.gsub: gsubState = self.gsub.getFeatureState(featureTag) if self.gpos is not None: if featureTag in self.gpos: gposState = self.gpos.getFeatureState(featureTag) if gsubState is not None and gposState is not None: if gsubState != gposState: raise CompositorError("Inconsistently applied feature: %s" % featureTag) if gsubState is not None: return gsubState if gposState is not None: return gposState raise CompositorError("Feature %s is is not contained in GSUB or GPOS" % featureTag) def setFeatureState(self, featureTag, state): if self.gsub is not None: if featureTag in self.gsub: self.gsub.setFeatureState(featureTag, state) if self.gpos is not None: if featureTag in self.gpos: self.gpos.setFeatureState(featureTag, state) # ------------- # Miscellaneous # ------------- def getGlyphOrder(self): return self.source.getGlyphOrder()
class Font(object) : def __init__(self, fontfile) : self.glyphs = [] self.psnames = {} self.canons = {} self.gdls = {} self.anchors = {} self.ligs = {} self.subclasses = {} self.points = {} self.classes = {} self.aliases = {} self.rules = {} self.posRules = {} if fontfile : self.font = TTFont(fontfile) for i, n in enumerate(self.font.getGlyphOrder()) : self.addGlyph(i, n) else : self.font = None def __len__(self) : return len(self.glyphs) # [] syntax returns the indicated element of the glyphs array. def __getitem__(self, y) : try : return self.glyphs[y] except IndexError : return None def glyph(self, name) : return self.psnames.get(name, None) def alias(self, s) : return self.aliases.get(s, s) def emunits(self) : return 0 def initGlyphs(self, nGlyphs) : #print "Font::initGlyphs",nGlyphs self.glyphs = [None] * nGlyphs self.numRealGlyphs = nGlyphs # does not include pseudo-glyphs self.psnames = {} self.canons = {} self.gdls = {} self.classes = {} def addGlyph(self, index = None, psName = None, gdlName = None, factory = Glyph) : #print "Font::addGlyph",index,psName,gdlName if psName in self.psnames : return self.psnames[psName] if index is not None and index < len(self.glyphs) and self.glyphs[index] : g = self.glyphs[index] return g g = factory(psName, index) # create a new glyph of the given class self.renameGlyph(g, psName, gdlName) if index is None : # give it the next available index index = len(self.glyphs) self.glyphs.append(g) elif index >= len(self.glyphs) : self.glyphs.extend([None] * (len(self.glyphs) - index + 1)) self.glyphs[index] = g return g def renameGlyph(self, g, name, gdlName = None) : if g.psname != name : for n in g.parseNames() : del self.psnames[n.psname] del self.canons[n.canonical()] if gdlName : self.setGDL(g, gdlName) else : self.setGDL(g, g.GDLName()) for n in g.parseNames() : if n is None : break self.psnames[n.psname] = g self.canons[n.canonical()] = (n, g) def setGDL(self, glyph, name) : if not glyph : return n = glyph.GDLName() if n != name and n in self.gdls : del self.gdls[n] if name and name in self.gdls and self.gdls[name] is not glyph : count = 1 index = -2 name = name + "_1" while name in self.gdls : if self.gdls[name] is glyph : break count = count + 1 name = name[0:index] + "_" + str(count) if count == 10 : index = -3 if count == 100 : index = -4 self.gdls[name] = glyph glyph.setGDL(name) def addClass(self, name, elements, fname = None, lineno = 0, generated = False, editable = False) : if name : self.classes[name] = FontClass(elements, fname, lineno, generated, editable) def addGlyphClass(self, name, gid, editable = False) : if name not in self.classes : self.classes[name] = FontClass() if gid not in self.classes[name].elements : self.classes[name].append(gid) def addRules(self, rules, index) : self.rules[index] = rules def addPosRules(self, rules, index) : self.posRules[index] = rules def classUpdated(self, name, value) : c = [] if name in self.classes : for gid in self.classes[name].elements : g = self[gid] if g : g.removeClass(name) if value is None and name in classes : del self.classes[name] return for n in value.split() : g = self.gdls.get(n, None) if g : c.append(g.gid) g.addClass(name) if name in self.classes : self.classes[name].elements = c else : self.classes[name] = FontClass(c) # Return the list of classes that should be updated in the AP XML file. # This does not include classes that are auto-generated or defined in the hand-crafted GDL code. def filterAutoClasses(self, names, autoGdlFile) : res = [] for n in names : c = self.classes[n] if not c.generated and (not c.fname or c.fname == autoGdlFile) : res.append(n) return res def loadAlias(self, fname) : with open(fname) as f : for l in f.readlines() : l = l.strip() l = re.sub(ur'#.*$', '', l).strip() if not len(l) : continue try : k, v = re.split(ur'\s*[,;\s]\s*', l, 1) except ValueError : k = l v = '' self.aliases[k] = v # TODO: move this method to GraideFont, or refactor def loadAP(self, apFileName) : if not os.path.exists(apFileName) : return False etree = parse(apFileName) self.initGlyphs(len(etree.getroot())) # guess each child is a glyph i = 0 for e in etree.getroot().iterfind("glyph") : g = self.addGlyph(i, e.get('PSName')) g.readAP(e, self) i += 1 return True def saveAP(self, apFileName, autoGdlFile) : root = Element('font') root.set('upem', str(self.emunits())) root.set('producer', 'graide 1.0') root.text = "\n\n" for g in self.glyphs : if g : g.createAP(root, self, autoGdlFile) ElementTree(root).write(apFileName, encoding="utf-8", xml_declaration=True) def createClasses(self) : self.subclasses = {} for k, v in self.canons.items() : if v[0].ext : h = v[0].head() o = self.canons.get(h.canonical(), None) if o : if v[0].ext not in self.subclasses : self.subclasses[v[0].ext] = {} self.subclasses[v[0].ext][o[1].GDLName()] = v[1].GDLName() # for g in self.glyphs : # if not g : continue # for c in g.classes : # if c not in self.classes : # self.classes[c] = [] # self.classes[c].append(g.gid) def calculatePointClasses(self) : self.points = {} for g in self.glyphs : if not g : continue for apName in g.anchors.keys() : genericName = apName[:-1] # without the M or S if genericName not in self.points : self.points[genericName] = PointClass(genericName) if apName.endswith('S') : self.points[genericName].addBaseGlyph(g) else : self.points[genericName].addDiaGlyph(g) def calculateOTLookups(self) : if self.font : for t in ('GSUB', 'GPOS') : if t in self.font : self.font[t].table.LookupList.process(self) def getPointClasses(self) : if len(self.points) == 0 : self.calculatePointClasses() return self.points def ligClasses(self) : self.ligs = {} for g in self.glyphs : if not g or not g.name : continue (h, t) = g.name.split_last() if t : o = self.canons.get(h.canonical(), None) if o and o[0].ext == t.ext : t.ext = None t.cname = None tn = t.canonical(noprefix = True) if tn in self.ligs : self.ligs[tn].append((g.GDLName(), o[0].GDL())) else : self.ligs[tn] = [(g.GDLName(), o[0].GDL())] def outGDL(self, fh, args) : munits = self.emunits() fh.write('table(glyph) {MUnits = ' + str(munits) + '};\n') nglyphs = 0 for g in self.glyphs : if not g or not g.psname : continue if g.psname == '.notdef' : fh.write(g.GDLName() + ' = glyphid(0)') else : fh.write(g.GDLName() + ' = postscript("' + g.psname + '")') outs = [] if len(g.anchors) : for a in g.anchors.keys() : v = g.anchors[a] outs.append(a + "=point(" + str(int(v[0])) + "m, " + str(int(v[1])) + "m)") for (p, v) in g.gdl_properties.items() : outs.append("%s=%s" % (p, v)) if len(outs) : fh.write(" {" + "; ".join(outs) + "}") fh.write(";\n") nglyphs += 1 fh.write("\n") fh.write("\n/* Point Classes */\n") for p in sorted(self.points.values(), key=lambda x: x.name) : if not p.hasDias() : continue n = p.name + "Dia" self.outclass(fh, "c" + n, p.classGlyphs(True)) self.outclass(fh, "cTakes" + n, p.classGlyphs(False)) self.outclass(fh, 'cn' + n, filter(lambda x : p.isNotInClass(x, True), self.glyphs)) self.outclass(fh, 'cnTakes' + n, filter(lambda x : p.isNotInClass(x, False), self.glyphs)) fh.write("\n/* Classes */\n") for c in sorted(self.classes.keys()) : # c = class name, l = class object if c not in self.subclasses and not self.classes[c].generated : # don't output the class to the AP file if it was autogenerated self.outclass(fh, c, self.classes[c].elements) for p in self.subclasses.keys() : ins = [] outs = [] for k, v in self.subclasses[p].items() : ins.append(k) outs.append(v) n = p.replace('.', '_') self.outclass(fh, 'cno_' + n, ins) self.outclass(fh, 'c' + n, outs) fh.write("/* Ligature Classes */\n") for k in sorted(self.ligs.keys()) : self.outclass(fh, "clig" + k, map(lambda x: self.gdls[x[0]], self.ligs[k])) self.outclass(fh, "cligno_" + k, map(lambda x: self.gdls[x[1]], self.ligs[k])) fh.write("\nendtable;\n") fh.write("/* Substitution Rules */\n") for k, v in sorted(self.rules.items(), key=lambda x:map(int,x[0].split('_'))) : fh.write('\n// lookup ' + k + '\n') fh.write('// ' + "\n// ".join(v) + "\n") fh.write("\n/* Positioning Rules */\n") for k, v in sorted(self.posRules.items(), key=lambda x:map(int,x[0].split('_'))) : fh.write('\n// lookup ' + k + '\n') fh.write('// ' + "\n// ".join(v) + "\n") fh.write("\n\n#define MAXGLYPH %d\n\n" % (nglyphs - 1)) if args.include : fh.write("#include \"%s\"\n" % args.include) def outPosRules(self, fh, num) : fh.write(""" #ifndef opt2 #define opt(x) [x]? #define opt2(x) [opt(x) x]? #define opt3(x) [opt2(x) x]? #define opt4(x) [opt3(x) x]? #endif #define posrule(x) c##x##Dia {attach{to=@1; at=x##S; with=x##M}} / cTakes##x##Dia opt4(cnTakes##x##Dia) _; table(positioning); pass(%d); """ % num) for p in self.points.values() : if p.hasDias() : fh.write("posrule(%s);\n" % p.name) fh.write("endpass;\nendtable;\n") def outclass(self, fh, name, glyphs) : fh.write(name + " = (") count = 1 sep = "" for g in glyphs : if not g : continue if isinstance(g, basestring) : fh.write(sep + g) else : if g.GDLName() is None : print "Can't output " + str(g.gid) + " to class " + name else : fh.write(sep + g.GDLName()) if count % 8 == 0 : sep = ',\n ' else : sep = ', ' count += 1 fh.write(');\n\n')
def instantiateVariableFont(varfont, location, inplace=False): """ Generate a static instance from a variable TTFont and a dictionary defining the desired location along the variable font's axes. The location values must be specified as user-space coordinates, e.g.: {'wght': 400, 'wdth': 100} By default, a new TTFont object is returned. If ``inplace`` is True, the input varfont is modified and reduced to a static font. """ if not inplace: # make a copy to leave input varfont unmodified stream = BytesIO() varfont.save(stream) stream.seek(0) varfont = TTFont(stream) fvar = varfont['fvar'] axes = {a.axisTag:(a.minValue,a.defaultValue,a.maxValue) for a in fvar.axes} loc = normalizeLocation(location, axes) if 'avar' in varfont: maps = varfont['avar'].segments loc = {k: piecewiseLinearMap(v, maps[k]) for k,v in loc.items()} # Quantize to F2Dot14, to avoid surprise interpolations. loc = {k:floatToFixedToFloat(v, 14) for k,v in loc.items()} # Location is normalized now log.info("Normalized location: %s", loc) if 'gvar' in varfont: log.info("Mutating glyf/gvar tables") gvar = varfont['gvar'] glyf = varfont['glyf'] # get list of glyph names in gvar sorted by component depth glyphnames = sorted( gvar.variations.keys(), key=lambda name: ( glyf[name].getCompositeMaxpValues(glyf).maxComponentDepth if glyf[name].isComposite() else 0, name)) for glyphname in glyphnames: variations = gvar.variations[glyphname] coordinates,_ = _GetCoordinates(varfont, glyphname) origCoords, endPts = None, None for var in variations: scalar = supportScalar(loc, var.axes) if not scalar: continue delta = var.coordinates if None in delta: if origCoords is None: origCoords,control = _GetCoordinates(varfont, glyphname) endPts = control[1] if control[0] >= 1 else list(range(len(control[1]))) delta = iup_delta(delta, origCoords, endPts) coordinates += GlyphCoordinates(delta) * scalar _SetCoordinates(varfont, glyphname, coordinates) else: glyf = None if 'cvar' in varfont: log.info("Mutating cvt/cvar tables") cvar = varfont['cvar'] cvt = varfont['cvt '] deltas = {} for var in cvar.variations: scalar = supportScalar(loc, var.axes) if not scalar: continue for i, c in enumerate(var.coordinates): if c is not None: deltas[i] = deltas.get(i, 0) + scalar * c for i, delta in deltas.items(): cvt[i] += otRound(delta) if 'CFF2' in varfont: log.info("Mutating CFF2 table") glyphOrder = varfont.getGlyphOrder() CFF2 = varfont['CFF2'] topDict = CFF2.cff.topDictIndex[0] vsInstancer = VarStoreInstancer(topDict.VarStore.otVarStore, fvar.axes, loc) interpolateFromDeltas = vsInstancer.interpolateFromDeltas interpolate_cff2_PrivateDict(topDict, interpolateFromDeltas) CFF2.desubroutinize(varfont) interpolate_cff2_charstrings(topDict, interpolateFromDeltas, glyphOrder) interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc) del topDict.rawDict['VarStore'] del topDict.VarStore if 'MVAR' in varfont: log.info("Mutating MVAR table") mvar = varfont['MVAR'].table varStoreInstancer = VarStoreInstancer(mvar.VarStore, fvar.axes, loc) records = mvar.ValueRecord for rec in records: mvarTag = rec.ValueTag if mvarTag not in MVAR_ENTRIES: continue tableTag, itemName = MVAR_ENTRIES[mvarTag] delta = otRound(varStoreInstancer[rec.VarIdx]) if not delta: continue setattr(varfont[tableTag], itemName, getattr(varfont[tableTag], itemName) + delta) log.info("Mutating FeatureVariations") for tableTag in 'GSUB','GPOS': if not tableTag in varfont: continue table = varfont[tableTag].table if not hasattr(table, 'FeatureVariations'): continue variations = table.FeatureVariations for record in variations.FeatureVariationRecord: applies = True for condition in record.ConditionSet.ConditionTable: if condition.Format == 1: axisIdx = condition.AxisIndex axisTag = fvar.axes[axisIdx].axisTag Min = condition.FilterRangeMinValue Max = condition.FilterRangeMaxValue v = loc[axisTag] if not (Min <= v <= Max): applies = False else: applies = False if not applies: break if applies: assert record.FeatureTableSubstitution.Version == 0x00010000 for rec in record.FeatureTableSubstitution.SubstitutionRecord: table.FeatureList.FeatureRecord[rec.FeatureIndex].Feature = rec.Feature break del table.FeatureVariations if 'GDEF' in varfont and varfont['GDEF'].table.Version >= 0x00010003: log.info("Mutating GDEF/GPOS/GSUB tables") gdef = varfont['GDEF'].table instancer = VarStoreInstancer(gdef.VarStore, fvar.axes, loc) merger = MutatorMerger(varfont, loc) merger.mergeTables(varfont, [varfont], ['GDEF', 'GPOS']) # Downgrade GDEF. del gdef.VarStore gdef.Version = 0x00010002 if gdef.MarkGlyphSetsDef is None: del gdef.MarkGlyphSetsDef gdef.Version = 0x00010000 if not (gdef.LigCaretList or gdef.MarkAttachClassDef or gdef.GlyphClassDef or gdef.AttachList or (gdef.Version >= 0x00010002 and gdef.MarkGlyphSetsDef)): del varfont['GDEF'] addidef = False if glyf: for glyph in glyf.glyphs.values(): if hasattr(glyph, "program"): instructions = glyph.program.getAssembly() # If GETVARIATION opcode is used in bytecode of any glyph add IDEF addidef = any(op.startswith("GETVARIATION") for op in instructions) if addidef: break if addidef: log.info("Adding IDEF to fpgm table for GETVARIATION opcode") asm = [] if 'fpgm' in varfont: fpgm = varfont['fpgm'] asm = fpgm.program.getAssembly() else: fpgm = newTable('fpgm') fpgm.program = ttProgram.Program() varfont['fpgm'] = fpgm asm.append("PUSHB[000] 145") asm.append("IDEF[ ]") args = [str(len(loc))] for a in fvar.axes: args.append(str(floatToFixed(loc[a.axisTag], 14))) asm.append("NPUSHW[ ] " + ' '.join(args)) asm.append("ENDF[ ]") fpgm.program.fromAssembly(asm) # Change maxp attributes as IDEF is added if 'maxp' in varfont: maxp = varfont['maxp'] if hasattr(maxp, "maxInstructionDefs"): maxp.maxInstructionDefs += 1 else: setattr(maxp, "maxInstructionDefs", 1) if hasattr(maxp, "maxStackElements"): maxp.maxStackElements = max(len(loc), maxp.maxStackElements) else: setattr(maxp, "maxInstructionDefs", len(loc)) if 'name' in varfont: log.info("Pruning name table") exclude = {a.axisNameID for a in fvar.axes} for i in fvar.instances: exclude.add(i.subfamilyNameID) exclude.add(i.postscriptNameID) varfont['name'].names[:] = [ n for n in varfont['name'].names if n.nameID not in exclude ] if "wght" in location and "OS/2" in varfont: varfont["OS/2"].usWeightClass = otRound( max(1, min(location["wght"], 1000)) ) if "wdth" in location: wdth = location["wdth"] for percent, widthClass in sorted(OS2_WIDTH_CLASS_VALUES.items()): if wdth < percent: varfont["OS/2"].usWidthClass = widthClass break else: varfont["OS/2"].usWidthClass = 9 if "slnt" in location and "post" in varfont: varfont["post"].italicAngle = max(-90, min(location["slnt"], 90)) log.info("Removing variable tables") for tag in ('avar','cvar','fvar','gvar','HVAR','MVAR','VVAR','STAT'): if tag in varfont: del varfont[tag] return varfont
def main(inPath, outPath): if VERBOSE: print "Dieting %s..." % (os.path.basename(inPath)) try: ttx = TTFont(inPath) except TTLibError: print "Cannot open %s" % inPath sys.exit(2) cmap = ttx["cmap"].getcmap(3,10) if not cmap: cmap = ttx["cmap"].getcmap(3,1) if cmap: umap = dict((u,n) for u, n in cmap.cmap.iteritems() ) # nmap = dict((umap[k], k) for k in umap) # glyph may be associated with more than one u!!! nmap = {} for k in umap: nmap.setdefault(umap[k],[]).append(k) else: if VERBOSE: print "'cmap' table misses a Windows-platform subtable." return def getDecompositionData(u,missingMarks): # inside so we can use umap, nmap ... udec = None try: dec = unicodedata.decomposition(unichr(u)) if len(dec) > 1: if not dec[:1] == "<": udec = [int(s, 16) for s in dec.split()] decall = 0 for ud in udec: if ud in SKIP_MARKS_FINAL: # if mark is in SKIP_MARKS_FINAL we don't want to do any decomposition return 0 if ud in umap: decall += 1 else: if ud not in SKIP_MARKS_FINAL \ and ud in MARK_GLYPH_CODEPOINT_RANGE: missingMarks += [unicodeIntToHexstr(ud)] # if decall == len(udec) and decall == 1: # print "SAME:",umap[u],[umap[ud] for ud in udec] if decall == len(udec) and decall > 1: # the last condition may go for the sake of allowing reference to same-shape glyphs return umap[u],[umap[ud] for ud in udec],udec[0] # last one is the one to check next except ValueError: return 0 return 0 markGlyphs = getMarkGlyphs(ttx) if not testFont(ttx,umap,nmap,markGlyphs): if VERBOSE: print "This font is useless. Ignoring it." return glyphOrder = ttx.getGlyphOrder() glyphs_removeOutlinesAndInstructions = [] ccmpSubsDict = {} linesDict = {} missingMarks = [] if umap: for u in umap: decomp = getDecompositionData(u,missingMarks) decompLast = deepcopy(decomp) while decomp: decomp = getDecompositionData(decomp[2],missingMarks) # check if the base char is a composed one too! # cf: 01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1; if decomp: decompLast[1][0:1] = deepcopy(decomp[1]) if decompLast: ccmpSubsDict[decompLast[0]] = decompLast[1] linesDict[ decompLast[0]] = " sub %s by %s;" % (decompLast[0], " ".join(decompLast[1])) glyphs_removeOutlinesAndInstructions += [decompLast[0]] # sort by glyph order! ccmpSubs = [] lines = [] for g in glyphOrder: try: ccmpSubs += [(g,ccmpSubsDict[g])] except: pass try: lines += [ linesDict[g] ] except: pass if len(ccmpSubsDict) != len(ccmpSubs): if VERBOSE: print "(Lost substitutions when creating ccmpSubs.)" if len(linesDict) != len(lines): if VERBOSE: print "(Lost substitutions when creating lines.)" # report missing marks: missingMarks = cleanUpList(missingMarks) missingMarks.sort() if missingMarks: if VERBOSE: print "For more effective decomposition you might add the following marks:" if VERBOSE: print " ".join(missingMarks) # output AFDKO syntax .fea file: if len(lines): ccmpfea = [ "lookup ccmpUnicodeDecomp {" ] ccmpfea += lines ccmpfea += [ "} ccmpUnicodeDecomp;" ] if SAVE_FEA_FILE: saveFile("\n".join(ccmpfea),os.path.splitext(outPath)[0]+".ccmp.fea") else: if VERBOSE: print "Nothing there to decompose." if REMOVE_PRECOMPOSED_OUTLINES and lines: # only makes sense if there's something to decompose removeOutlines(ttx,glyphs_removeOutlinesAndInstructions) if REMOVE_PRECOMPOSED_FROM_GPOS_KERN and lines: # only makes sense if there's something to decompose removeGPOSkern(ttx,glyphs_removeOutlinesAndInstructions) if REMOVE_ALL_BUT_WIN_CMAP_SUBTABLES: removeAllButWinCmapSubtable(ttx) if REMOVE_ALL_BUT_WIN_NAME_RECORDS \ or RENAME_FONT: # enforce removing non-Win records when renaming the font! removeAllButWinNameRecords( ttx) if RENAME_FONT: renameFont( ttx) if ADD_DUMMY_DSIG: addDummyDSIG( ttx) if DECOMPOSE_PRECOMPOSED_IN_CCMP and lines:# only makes sense if there's something to decompose addCcmpLookup( ttx,ccmpSubs) if REMOVE_POST_GLYPHNAMES: removePostNames( ttx) tempPath = outPath+"temp" if VERBOSE: print "Saving %s..." % (outPath) ttx.save( outPath ) ttx.close() ttx = None inSize = os.path.getsize(inPath) outSize = os.path.getsize(outPath) if VERBOSE: print "Diet efficiency: %s%% (from %s to %s bytes)" % (float(int((1 - float(outSize)/inSize) * 10000))/100, inSize, outSize) # validate: if OTS_SANITISE: error = 0 try: p = Popen([r"%s" % OTS_PATH_OR_COMMAND, r"%s" % outPath, r"%s" % tempPath ],stderr=PIPE) except: p = 0 if VERBOSE and OTS_SANITISE: print "ot-sanitise not found. Install https://github.com/khaledhosny/ots" if p: stdoutdata, stderrdata = p.communicate() if stderrdata: # if no problems are found, ot-sanitise doesn't output anything error = 1 if OTS_SANITISE: print "ot-sanitise did not validate the simplified font. ", if OTS_SANITISE > 1: print "Deleting it." else: if VERBOSE: print "ot-sanitise validated this font." for line in stderrdata.strip().replace("\r\n","\n").replace("\r","\n").split("\n"): if VERBOSE: print " %s" % line.strip() p=None; stderrdata=None; stdoutdata=None if error: # delete ot-sanitise file # and the font file since it is invalid anyway: if os.path.exists( tempPath ): os.remove( tempPath ) if os.path.exists( outPath ) and OTS_SANITISE > 1: os.remove( outPath ) else: # always delete ot-sanitise file: if os.path.exists( tempPath ): os.remove( tempPath ) outPath=None; tempPath=None
class GlyfSerializer(object): """Serializes 'glyf' table for given font file """ # formats fmt_TOC = '>4sHH' fmt_TOCEntry = '>4sLL' fmt_GlyphTable = '>HH' # flags NONE = 0 HAS_HMTX = 1 << 0 HAS_VMTX = 1 << 1 CMP_NONE = NONE CMP_GZIP = (1 << 2) + NONE CMP_BROTLI = (1 << 2) + (1 << 2) CMP_LZMA = (1 << 2) + (1 << 3) CLEAN = NONE DIRTY = (1 << 6) def __init__(self, fontfile): self.font = TTFont(fontfile) def prepare_TOC(self): """Prepare TOC header and entries as data """ self.TOC = pack(GlyfSerializer.fmt_TOC, self.font.reader.sfntVersion, 0, self.font.reader.numTables) self.TOCEntries = [] tags = sorted(self.font.reader.keys()) assert len( tags) == self.font.reader.numTables, 'Unexpected number of tables' for tag in tags: entry = self.font.reader.tables[tag] TOCEntry = ( pack( GlyfSerializer.fmt_TOCEntry, tag, entry.offset, entry.length)) self.TOCEntries.append(TOCEntry) self.tocReady = True def __determine_mtx_fmt(self): self.fmt_mtx = '' self.has_hmtx_ = GlyfSerializer.HAS_HMTX if ('hmtx' in self.font)\ else GlyfSerializer.NONE if self.has_hmtx_: self.fmt_mtx += 'h' self.HMTX = self.font['hmtx'] self.has_vmtx_ = GlyfSerializer.HAS_VMTX if ('vmtx' in self.font)\ else GlyfSerializer.NONE if self.has_vmtx_: self.fmt_mtx += 'h' self.VMTX = self.font['vmtx'] def prepare_glyf(self): """Prepare Glyf table and table entries along with Glyf data """ self.__determine_mtx_fmt() self.fmt_GlyphEntry = '>H' + self.fmt_mtx + 'LH' assert 'maxp' in self.font numGlyphs = self.font['maxp'].numGlyphs self.GlyphTable = ( pack( GlyfSerializer.fmt_GlyphTable, ( self.has_hmtx_ | self.has_vmtx_ | GlyfSerializer.CLEAN), numGlyphs)) self.glyphs_info = [] self.glyphs_data = [] glyphOrder = self.font.getGlyphOrder() assert 'loca' in self.font glyf_table_start = self.font.reader.tables['glyf'].offset offset_table = self.font['loca'].locations for i in xrange(numGlyphs): offset = offset_table[i] length = offset_table[i + 1] - offset self.font.reader.file.seek(glyf_table_start + offset) self.glyphs_data.append(self.font.reader.file.read(length)) args = [i] if self.has_hmtx_: args.append(self.HMTX.metrics[glyphOrder[i]][1]) if self.has_vmtx_: args.append(self.VMTX.metrics[glyphOrder[i]][1]) args.append(offset) args.append(length) glyph_info = pack(self.fmt_GlyphEntry, *args) self.glyphs_info.append(glyph_info) self.glyfReady = True def serialize_TOC(self, output_idx, output_data): """Dump the TOC data to the file """ if self.tocReady: dumper = Dumper(output_idx) dumper.dump(self.TOC) dumper.close() dumper = Dumper(output_data) dumper.dumpForEach(self.TOCEntries) dumper.close() def serialize_glyf(self, output_idx, output_data): """Dump the Glyf data to the file """ if self.glyfReady: dumper = Dumper(output_idx) dumper.dump(self.GlyphTable) dumper.dump_for_each(self.glyphs_info) dumper.close() dumper = Dumper(output_data) dumper.dump_for_each(self.glyphs_data) dumper.close() def close(self): self.font.close()
for root, dirs, files in os.walk(os.argv[1]): for filename in files: if os.path.splitext(filename)[1] == '.otf': fname = os.path.join(root, filename) print "Handling %s" % filename # decompress print("\tDecompressing...") font = TTFont(fname) orig_subrs = sum_subrs(font) orig_size = os.path.getsize(fname) options = subset.Options() options.decompress = True subsetter = subset.Subsetter(options=options) subsetter.populate(glyphs=font.getGlyphOrder()) subsetter.subset(font) name_parts = os.path.splitext(fname) new_fname = name_parts[0] + '-decomp' + name_parts[1] font.save(new_fname) full_size = os.path.getsize(new_fname) print("\tSubroutinizing...") print("----") start_time = time.time() cxxCompressor.main(filename=new_fname, verbose=True) times.append(time.time() - start_time) print("----") print("\tTabulating results...") comp_fname = name_parts[0] + '-decomp.compressed' + name_parts[1]