Пример #1
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, [])
Пример #2
0
    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
Пример #3
0
    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
Пример #4
0
    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
Пример #5
0
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
Пример #6
0
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
Пример #7
0
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")
Пример #8
0
    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"])
Пример #9
0
    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)
Пример #10
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, [])
Пример #11
0
    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
Пример #12
0
    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"])
Пример #13
0
    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)
Пример #14
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
Пример #15
0
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  # 返回对应的字体
Пример #16
0
    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
Пример #17
0
    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
Пример #18
0
    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))
Пример #20
0
 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
Пример #21
0
    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))
Пример #22
0
 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
Пример #23
0
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
    ]  # 提取后四位
Пример #24
0
    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)
Пример #25
0
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
Пример #26
0
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
Пример #27
0
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
Пример #28
0
    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
Пример #29
0
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))
Пример #30
0
        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)
Пример #31
0
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)))
Пример #32
0
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
Пример #33
0
    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
Пример #34
0
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
Пример #35
0
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
Пример #36
0
    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'])
Пример #37
0
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)
Пример #38
0
 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("  -----")
Пример #39
0
    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'])
Пример #40
0
 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)
Пример #41
0
        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)
Пример #42
0
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()
Пример #43
0
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()
Пример #44
0
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)
Пример #45
0
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
Пример #46
0
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)
Пример #47
0
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
Пример #48
0
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()
Пример #49
0
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
Пример #50
0
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()
Пример #51
0
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]
Пример #52
0
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()
Пример #53
0
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')
Пример #54
0
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
Пример #55
0
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
Пример #56
0
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()
Пример #57
0
   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]