Пример #1
0
    def test_varlib_main_ttf(self):
        """Mostly for testing varLib.main()
        """
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ufo_dir = self.get_test_input('master_ufo')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
        os.makedirs(ttf_dir)
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, ttf_dir)

        ds_copy = os.path.join(self.tempdir, 'BuildMain.designspace')
        shutil.copy2(ds_path, ds_copy)
        varLib_main([ds_copy])

        varfont_path = os.path.splitext(ds_copy)[0] + '-VF' + suffix
        varfont = TTFont(varfont_path)
        tables = [
            table_tag for table_tag in varfont.keys() if table_tag != 'head'
        ]
        expected_ttx_path = self.get_test_output('BuildMain.ttx')
        self.expect_ttx(varfont, expected_ttx_path, tables)
Пример #2
0
    def test_varlib_mutator_ttf(self):
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ufo_dir = self.get_test_input('master_ufo')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, self.tempdir)

        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace(
            '.ufo', suffix)
        varfont, _, _ = build(ds_path, finder)
        varfont_name = 'Mutator'
        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
        varfont.save(varfont_path)

        args = [varfont_path, 'wght=500', 'cntr=50']
        mutator(args)

        instfont_path = os.path.splitext(
            varfont_path)[0] + '-instance' + suffix
        instfont = TTFont(instfont_path)
        tables = [
            table_tag for table_tag in instfont.keys() if table_tag != 'head'
        ]
        expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
        self.expect_ttx(instfont, expected_ttx_path, tables)
    def test_varlib_interpolate_layout_main_ttf(self):
        """Mostly for testing varLib.interpolate_layout.main()
        """
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ufo_dir = self.get_test_input('master_ufo')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
        os.makedirs(ttf_dir)
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, ttf_dir)

        finder = lambda s: s.replace(ufo_dir, ttf_dir).replace('.ufo', suffix)
        varfont, _, _ = build(ds_path, finder)
        varfont_name = 'InterpolateLayoutMain'
        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
        varfont.save(varfont_path)

        ds_copy = os.path.splitext(varfont_path)[0] + '.designspace'
        shutil.copy2(ds_path, ds_copy)
        args = [ds_copy, 'weight=500', 'contrast=50']
        interpolate_layout_main(args)

        instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix
        instfont = TTFont(instfont_path)
        tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head']
        expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
        self.expect_ttx(instfont, expected_ttx_path, tables)
def generateFont(font, outfile):
    flags  = ("opentype", "dummy-dsig", "round", "omit-instructions")

    font.selection.all()
    font.correctReferences()
    font.selection.none()

    # fix some common font issues
    validateGlyphs(font)

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()
    os.remove(tmpfile)
Пример #5
0
    def test_varlib_mutator_ttf(self):
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ufo_dir = self.get_test_input('master_ufo')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, self.tempdir)

        finder = lambda s: s.replace(ufo_dir, self.tempdir).replace('.ufo', suffix)
        varfont, _, _ = build(ds_path, finder)
        varfont_name = 'Mutator'
        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
        varfont.save(varfont_path)

        args = [varfont_path, 'wght=500', 'cntr=50']
        mutator(args)

        instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix
        instfont = TTFont(instfont_path)
        tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head']
        expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
        self.expect_ttx(instfont, expected_ttx_path, tables)
Пример #6
0
    def test_varlib_interpolate_layout_main_ttf(self):
        """Mostly for testing varLib.interpolate_layout.main()
        """
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ufo_dir = self.get_test_input('master_ufo')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
        os.makedirs(ttf_dir)
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, ttf_dir)

        finder = lambda s: s.replace(ufo_dir, ttf_dir).replace('.ufo', suffix)
        varfont, _, _ = build(ds_path, finder)
        varfont_name = 'InterpolateLayoutMain'
        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)
        varfont.save(varfont_path)

        ds_copy = os.path.splitext(varfont_path)[0] + '.designspace'
        shutil.copy2(ds_path, ds_copy)
        args = [ds_copy, 'weight=500', 'contrast=50']
        interpolate_layout_main(args)

        instfont_path = os.path.splitext(
            varfont_path)[0] + '-instance' + suffix
        instfont = TTFont(instfont_path)
        tables = [
            table_tag for table_tag in instfont.keys() if table_tag != 'head'
        ]
        expected_ttx_path = self.get_test_output(varfont_name + '.ttx')
        self.expect_ttx(instfont, expected_ttx_path, tables)
Пример #7
0
def generateFont(font, outfile):
    flags = ("opentype", "dummy-dsig", "round", "omit-instructions")

    font.selection.all()
    font.correctReferences()
    font.selection.none()

    # fix some common font issues
    validateGlyphs(font)

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()
    os.remove(tmpfile)
Пример #8
0
def getAxes():
    """
    returns a dict of all ttfs
    {tag, min, def, max}
    """
    fonts = {}
    for ttf in glob.glob("fonts/**.ttf"):
        ftf = TTFont(ttf)
        fontname = os.path.basename(ttf)[:-4]
        axes = []
        # print(fontname)
        if 'fvar' in ftf.keys():
            axes = []
            for axis in ftf['fvar'].axes:
                a = {}
                a['tag'] = axis.axisTag
                a['min'] = axis.minValue
                a['def'] = axis.defaultValue
                a['max'] = axis.maxValue
                a['name'] = ftf['name'].getDebugName(axis.axisNameID)
                axes.append(a)
        if axes:
            fonts[fontname] = axes
    # d = open("axes", "w+")
    # d.write(str(fonts))
    # d.close()
    return fonts
Пример #9
0
    def test_varlib_mutator_iup_ttf(self):
        suffix = '.ttf'
        ufo_dir = self.get_test_input('master_ufo')
        ttx_dir = self.get_test_input('master_ttx_varfont_ttf')

        self.temp_dir()
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'Mutator_IUP')
        for path in ttx_paths:
            self.compile_font(path, suffix, self.tempdir)

        varfont_name = 'Mutator_IUP'
        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)

        args = [varfont_path, 'wdth=80', 'ASCN=628']
        mutator(args)

        instfont_path = os.path.splitext(
            varfont_path)[0] + '-instance' + suffix
        instfont = TTFont(instfont_path)
        tables = [
            table_tag for table_tag in instfont.keys() if table_tag != 'head'
        ]
        expected_ttx_path = self.get_test_output(varfont_name +
                                                 '-instance.ttx')
        self.expect_ttx(instfont, expected_ttx_path, tables)
Пример #10
0
class FontSize(object):
    def __init__(self, filepath):
        self.ttfont = TTFont(filepath)
        self.filepath = filepath
        self.total_glyphs = 0
        self.encoded_glyphs = 0
        self.total_bytes = 0
        self.per_enc_glyph_bytes = 0.0
        self.per_total_glyph_bytes = 0.0
        self.tables = {}
        self._calculate_sizes()

    def _calculate_sizes(self):
        self.total_bytes = os.path.getsize(self.filepath)
        self._calc_per_encoded_glyph_size()
        self._calc_per_total_glyph_size()
        self._calc_table_sizes()

    def _calc_per_encoded_glyph_size(self):
        self.encoded_glyphs = len(self.ttfont.getBestCmap())
        self.per_enc_glyph_bytes = self.total_bytes / self.encoded_glyphs

    def _calc_per_total_glyph_size(self):
        self.total_glyphs = len(self.ttfont.getGlyphSet())
        self.per_total_glyph_bytes = self.total_bytes / self.total_glyphs

    def _calc_table_sizes(self):
        tables = self.get_table_tags()
        for table in tables:
            self.tables[table] = self.ttfont.reader.tables[table].length

    def get_table_tags(self):
        return [tag for tag in self.ttfont.keys() if tag != "GlyphOrder"]
Пример #11
0
def getSFNTData(pathOrFile,
                unsortGlyfLoca=False,
                glyphBBox="",
                alt255UInt16=False):
    font = TTFont(pathOrFile)
    tableChecksums = {}
    tableData = {}
    tableOrder = [i for i in sorted(font.keys()) if len(i) == 4]
    if unsortGlyfLoca:
        assert "loca" in tableOrder
        loca = tableOrder.index("loca")
        glyf = tableOrder.index("glyf")
        tableOrder.insert(glyf, tableOrder.pop(loca))
    for tag in tableOrder:
        tableChecksums[tag] = font.reader.tables[tag].checkSum
        tableData[tag] = transformTable(font,
                                        tag,
                                        glyphBBox=glyphBBox,
                                        alt255UInt16=alt255UInt16)
    totalData = "".join([tableData[tag][1] for tag in tableOrder])
    compData = brotli.compress(totalData, brotli.MODE_FONT)
    if len(compData) >= len(totalData):
        compData = totalData
    font.close()
    del font
    return tableData, compData, tableOrder, tableChecksums
Пример #12
0
def cleanTTF(ttfFile, outfile):
    # now open in fontTools
    ftfont = TTFont(ttfFile)

    # the ttf contains NAME table IDs with platformID="1", these should be removed
    name = ftfont['name']
    names = []
    for record in name.names:
        if record.platformID == 1:
            continue
        names.append(record)
    name.names = names

    # remove non-standard 'FFTM' the FontForge time stamp table
    del ftfont['FFTM']

    # issue #40 let Adobe InDesign show the font in the Arabic section of the font menu.
    # This essentially says the only codepage that "is considered functional" is Arabic.
    # https://www.microsoft.com/typography/otspec/os2.htm#cpr
    ftfont['OS/2'].ulCodePageRange1 = 64
    ftfont['OS/2'].ulCodePageRange2 = 0

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()
Пример #13
0
def _sniff_cff_table_format(otf: ttLib.TTFont) -> CFFTableTag:
    cff_tag = next(
        (CFFTableTag(tag)
         for tag in otf.keys() if tag in CFFTableTag.__members__.values()),
        None,
    )
    if not cff_tag:
        raise Error("Invalid OTF: no 'CFF ' or 'CFF2' tables found")
    return cff_tag
Пример #14
0
Файл: sfnt.py Проект: w3c/woff
def getSFNTData(pathOrFile):
    font = TTFont(pathOrFile)
    # checksums
    tableChecksums = {}
    for tag, entry in font.reader.tables.items():
        tableChecksums[tag] = entry.checkSum
    # data
    tableData = {}
    for tag in font.keys():
        if len(tag) != 4:
            continue
        origData = font.getTableData(tag)
        compData = zlib.compress(origData)
        if len(compData) >= len(origData) or tag == "head":
            compData = origData
        tableData[tag] = (origData, compData)
    # order
    tableOrder = [i for i in font.keys() if len(i) == 4]
    font.close()
    del font
    return tableData, tableOrder, tableChecksums
Пример #15
0
def get_cmap(font_text):
    # with open('ChcCQ1sUz0yANyKDAABj8LPX4oE56..ttf', 'wb') as f:
    #     f.write(font_text)
    font = TTFont('ChcCQ1sUz0yANyKDAABj8LPX4oE56..ttf')
    font.saveXML('ChcCQ1sUz0yANyKDAABj8LPX4oE56..xml')
    bestcmap = TTFont("ChcCQ1sUz0yANyKDAABj8LPX4oE56..ttf")['cmap'].getBestCmap()
    newmap = dict()
    for key in bestcmap.keys():
        value = bestcmap[key] if bestcmap[key] not in list(base_mapping.keys()) else base_mapping[bestcmap[key]]
        # 类型判断,手机号改为int
        # if isinstance(value, str):
        newmap[hex(key)] = value
    return newmap
Пример #16
0
    def test_varlib_main_ttf(self):
        """Mostly for testing varLib.main()
        """
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
        os.makedirs(ttf_dir)
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, ttf_dir)

        ds_copy = os.path.join(self.tempdir, 'BuildMain.designspace')
        shutil.copy2(ds_path, ds_copy)

        # by default, varLib.main finds master TTFs inside a
        # 'master_ttf_interpolatable' subfolder in current working dir
        cwd = os.getcwd()
        os.chdir(self.tempdir)
        try:
            varLib_main([ds_copy])
        finally:
            os.chdir(cwd)

        varfont_path = os.path.splitext(ds_copy)[0] + '-VF' + suffix
        self.assertTrue(os.path.exists(varfont_path))

        # try again passing an explicit --master-finder
        os.remove(varfont_path)
        finder = "%s/master_ttf_interpolatable/{stem}.ttf" % self.tempdir
        varLib_main([ds_copy, "--master-finder", finder])
        self.assertTrue(os.path.exists(varfont_path))

        # and also with explicit -o output option
        os.remove(varfont_path)
        varfont_path = os.path.splitext(varfont_path)[0] + "-o" + suffix
        varLib_main([ds_copy, "-o", varfont_path, "--master-finder", finder])
        self.assertTrue(os.path.exists(varfont_path))

        varfont = TTFont(varfont_path)
        tables = [
            table_tag for table_tag in varfont.keys() if table_tag != 'head'
        ]
        expected_ttx_path = self.get_test_output('BuildMain.ttx')
        self.expect_ttx(varfont, expected_ttx_path, tables)
Пример #17
0
def makeWeb(infile, outfile):
    """If we are building a web version then try to minimise file size"""

    # "short-post" generates a post table without glyph names to save some KBs
    # since glyph names are only needed for PDF's as readers use them to
    # "guess" characters when copying text, which is of little use in web fonts.
    flags = ("opentype", "short-post", "omit-instructions")

    fontforge.setPrefs("PreserveTables", "COLR,CPAL")

    font = fontforge.open(infile)
    font.encoding = "UnicodeBmp" # avoid a crash if compact was set

    # removed compatibility glyphs that of little use on the web
    compat_ranges = (
            (0xfb50, 0xfbb1),
            (0xfbd3, 0xfd3d),
            (0xfd50, 0xfdf9),
            (0xfdfc, 0xfdfc),
            (0xfe70, 0xfefc),
            )

    for glyph in font.glyphs():
        for i in compat_ranges:
            start = i[0]
            end = i[1]
            if start <= glyph.unicode <= end:
                font.removeGlyph(glyph)
                break

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()

    os.remove(tmpfile)
Пример #18
0
def makeWeb(infile, outfile):
    """If we are building a web version then try to minimise file size"""

    # "short-post" generates a post table without glyph names to save some KBs
    # since glyph names are only needed for PDF's as readers use them to
    # "guess" characters when copying text, which is of little use in web fonts.
    flags = ("opentype", "short-post", "omit-instructions")

    fontforge.setPrefs("PreserveTables", "COLR,CPAL")

    font = fontforge.open(infile)
    font.encoding = "UnicodeBmp"  # avoid a crash if compact was set

    # removed compatibility glyphs that of little use on the web
    compat_ranges = (
        (0xfb50, 0xfbb1),
        (0xfbd3, 0xfd3d),
        (0xfd50, 0xfdf9),
        (0xfdfc, 0xfdfc),
        (0xfe70, 0xfefc),
    )

    for glyph in font.glyphs():
        for i in compat_ranges:
            start = i[0]
            end = i[1]
            if start <= glyph.unicode <= end:
                font.removeGlyph(glyph)
                break

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()

    os.remove(tmpfile)
Пример #19
0
    def test_varlib_main_ttf(self):
        """Mostly for testing varLib.main()
        """
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
        os.makedirs(ttf_dir)
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, ttf_dir)

        ds_copy = os.path.join(self.tempdir, 'BuildMain.designspace')
        shutil.copy2(ds_path, ds_copy)

        # by default, varLib.main finds master TTFs inside a
        # 'master_ttf_interpolatable' subfolder in current working dir
        cwd = os.getcwd()
        os.chdir(self.tempdir)
        try:
            varLib_main([ds_copy])
        finally:
            os.chdir(cwd)

        varfont_path = os.path.splitext(ds_copy)[0] + '-VF' + suffix
        self.assertTrue(os.path.exists(varfont_path))

        # try again passing an explicit --master-finder
        os.remove(varfont_path)
        finder = "%s/master_ttf_interpolatable/{stem}.ttf" % self.tempdir
        varLib_main([ds_copy, "--master-finder", finder])
        self.assertTrue(os.path.exists(varfont_path))

        # and also with explicit -o output option
        os.remove(varfont_path)
        varfont_path = os.path.splitext(varfont_path)[0] + "-o" + suffix
        varLib_main([ds_copy, "-o", varfont_path, "--master-finder", finder])
        self.assertTrue(os.path.exists(varfont_path))

        varfont = TTFont(varfont_path)
        tables = [table_tag for table_tag in varfont.keys() if table_tag != 'head']
        expected_ttx_path = self.get_test_output('BuildMain.ttx')
        self.expect_ttx(varfont, expected_ttx_path, tables)
Пример #20
0
    def test_varlib_mutator_getvar_ttf(self):
        suffix = '.ttf'
        ttx_dir = self.get_test_input('master_ttx_getvar_ttf')

        self.temp_dir()
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'Mutator_Getvar')
        for path in ttx_paths:
            self.compile_font(path, suffix, self.tempdir)

        varfont_name = 'Mutator_Getvar'
        varfont_path = os.path.join(self.tempdir, varfont_name + suffix)

        args = [varfont_path, 'wdth=80', 'ASCN=628']
        mutator(args)

        instfont_path = os.path.splitext(varfont_path)[0] + '-instance' + suffix
        instfont = TTFont(instfont_path)
        tables = [table_tag for table_tag in instfont.keys() if table_tag != 'head']
        expected_ttx_path = self.get_test_output(varfont_name + '-instance.ttx')
        self.expect_ttx(instfont, expected_ttx_path, tables)
Пример #21
0
def getSFNTData(pathOrFile, unsortGlyfLoca=False, glyphBBox="", alt255UInt16=False):
    font = TTFont(pathOrFile)
    tableChecksums = {}
    tableData = {}
    tableOrder = [i for i in sorted(font.keys()) if len(i) == 4]
    if unsortGlyfLoca:
        assert "loca" in tableOrder
        loca = tableOrder.index("loca")
        glyf = tableOrder.index("glyf")
        tableOrder.insert(glyf, tableOrder.pop(loca))
    for tag in tableOrder:
        tableChecksums[tag] = font.reader.tables[tag].checkSum
        tableData[tag] = transformTable(font, tag, glyphBBox=glyphBBox, alt255UInt16=alt255UInt16)
    totalData = "".join([tableData[tag][1] for tag in tableOrder])
    compData = brotli.compress(totalData, brotli.MODE_FONT)
    if len(compData) >= len(totalData):
        compData = totalData
    font.close()
    del font
    return tableData, compData, tableOrder, tableChecksums
Пример #22
0
def generateFont(font, outfile):
    flags  = ("opentype", "dummy-dsig", "round", "omit-instructions")

    font.selection.all()
    font.correctReferences()
    font.selection.none()

    # fix some common font issues
    validateGlyphs(font)

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)


    # the ttf contains NAME table IDs with platformID="1", these should be removed
    name = ftfont['name']
    names = []
    for record in name.names:
        if record.platformID == 1:
            continue;
        names.append(record)
    name.names = names

    # remove non-standard 'FFTM' the FontForge time stamp table
    del ftfont['FFTM'];


    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()
    os.remove(tmpfile)
Пример #23
0
def analysis_font(font_url: str, mode=None) -> dict:
    ''' analysis font '''
    if (not os.path.exists(base_font)
            or not os.path.exists(base_pkl)) and not mode:
        print('base file not exist!!!')
        return

    suffix = font_url.split('.')[-1]
    font = requests.get(font_url, headers=header, timeout=30)
    font_name = '%sfont.%s' % (data_dir, suffix)
    with codecs.open(font_name, 'wb') as f:
        f.write(font.content)
    font_map = TTFont(font_name).getBestCmap()
    ''' prepare base '''
    if not mode is None:
        char_list = [
            hex(ii).upper().replace('0X', '&#x') + ';'
            for ii in font_map.keys()
        ]
        base_unicode = [
            int(mode[ii]) if ii in mode else '.' for ii in char_list
        ]
        pickle.dump(base_unicode, codecs.open(base_pkl, 'wb'))
        with codecs.open(base_font, 'wb') as f:
            f.write(font.content)
        return {}

    base_unicode = pickle.load(open(base_pkl, 'rb'))

    base_map = TTFont(base_font).getBestCmap()
    font_dict = {
        jj: base_unicode[ii]
        for ii, jj in enumerate(base_map.values())
    }
    num_dict = {
        hex(ii).upper().replace('0X', '&#x') + ';': str(font_dict[jj])
        for ii, jj in font_map.items()
    }
    return num_dict
Пример #24
0
def generateFont(font, outfile):
    flags = ("opentype", "dummy-dsig", "round", "omit-instructions")

    font.selection.all()
    font.correctReferences()
    font.selection.none()

    # fix some common font issues
    validateGlyphs(font)

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)

    # the ttf contains NAME table IDs with platformID="1", these should be removed
    name = ftfont['name']
    names = []
    for record in name.names:
        if record.platformID == 1:
            continue
        names.append(record)
    name.names = names

    # remove non-standard 'FFTM' the FontForge time stamp table
    del ftfont['FFTM']

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()
    os.remove(tmpfile)
Пример #25
0
def cleanTTF(ttfFile, outfile):
    # now open in fontTools
    ftfont = TTFont(ttfFile)

    # the ttf contains NAME table IDs with platformID="1", these should be removed
    name = ftfont['name']
    names = []
    for record in name.names:
        if record.platformID == 1:
            continue
        names.append(record)
    name.names = names

    # remove non-standard 'FFTM' the FontForge time stamp table
    del ftfont['FFTM']

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()
Пример #26
0
    def test_varlib_main_ttf(self):
        """Mostly for testing varLib.main()
        """
        suffix = '.ttf'
        ds_path = self.get_test_input('Build.designspace')
        ufo_dir = self.get_test_input('master_ufo')
        ttx_dir = self.get_test_input('master_ttx_interpolatable_ttf')

        self.temp_dir()
        ttf_dir = os.path.join(self.tempdir, 'master_ttf_interpolatable')
        os.makedirs(ttf_dir)
        ttx_paths = self.get_file_list(ttx_dir, '.ttx', 'TestFamily-')
        for path in ttx_paths:
            self.compile_font(path, suffix, ttf_dir)

        ds_copy = os.path.join(self.tempdir, 'BuildMain.designspace')
        shutil.copy2(ds_path, ds_copy)
        varLib_main([ds_copy])

        varfont_path = os.path.splitext(ds_copy)[0] + '-VF' + suffix
        varfont = TTFont(varfont_path)
        tables = [table_tag for table_tag in varfont.keys() if table_tag != 'head']
        expected_ttx_path = self.get_test_output('BuildMain.ttx')
        self.expect_ttx(varfont, expected_ttx_path, tables)
def makeWeb(infile, outfile):
    """If we are building a web version then try to minimise file size"""

    # "short-post" generates a post table without glyph names to save some KBs
    # since glyph names are only needed for PDF's as readers use them to
    # "guess" characters when copying text, which is of little use in web fonts.
    flags = ("opentype", "short-post", "omit-instructions")

    font = fontforge.open(infile)
    font.encoding = "UnicodeBmp" # avoid a crash if compact was set

    # removed compatibility glyphs that of little use on the web
    compat_ranges = (
            (0xfb50, 0xfbb1),
            (0xfbd3, 0xfd3d),
            (0xfd50, 0xfdf9),
            (0xfdfc, 0xfdfc),
            (0xfe70, 0xfefc),
            )

    for glyph in font.glyphs():
        for i in compat_ranges:
            start = i[0]
            end = i[1]
            if start <= glyph.unicode <= end:
                font.removeGlyph(glyph)
                break

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)

    # our 'name' table is a bit bulky, and of almost no use in for web fonts,
    # so we strip all unnecessary entries.
    name = ftfont['name']
    names = []
    for record in name.names:
        platID = record.platformID
        langID = record.langID
        nameID = record.nameID

        # we keep only en_US entries in Windows and Mac platform id, every
        # thing else is dropped
        if (platID == 1 and langID == 0) or (platID == 3 and langID == 1033):
            if nameID == 13:
                # the full OFL text is too much, replace it with a simple
                # string
                if platID == 3:
                    # MS strings are UTF-16 encoded
                    text = 'OFL v1.1'.encode('utf_16_be')
                else:
                    text = 'OFL v1.1'
                record.string = text
                names.append(record)
            # keep every thing else except Descriptor, Sample Text
            elif nameID not in (10, 19):
                names.append(record)

    name.names = names

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()

    os.remove(tmpfile)
Пример #28
0
def getWOFFCollectionData(pathOrFiles, MismatchGlyfLoca=False):
    from defaultData import defaultTestData

    tableChecksums = []
    tableData = []
    tableOrder = []
    collectionDirectory = []
    locaIndices = []

    for i, pathOrFile in enumerate(pathOrFiles):
        font = TTFont(pathOrFile)

        # Make the name table unique
        name = font["name"]
        for namerecord in name.names:
            nameID = namerecord.nameID
            string = namerecord.toUnicode()
            if nameID == 1:
                namerecord.string = "%s %d" % (string, i)
            elif nameID == 4:
                namerecord.string = string.replace("Regular", "%d Regular" % i)
            elif nameID == 6:
                namerecord.string = string.replace("-", "%d-" % i)

        tags = [i for i in sorted(font.keys()) if len(i) == 4]
        if "glyf" in tags:
            glyf = tags.index("glyf")
            loca = tags.index("loca")
            tags.insert(glyf + 1, tags.pop(loca))
        tableIndices = OrderedDict()
        for tag in tags:
            data = transformTable(font, tag)
            if MismatchGlyfLoca and tag in ("glyf", "loca"):
                tableData.append([tag, data])
                tableChecksums.append([tag, font.reader.tables[tag].checkSum])
                tableOrder.append(tag)
                tableIndex = len(tableData) - 1
                tableIndices[tag] = tableIndex
                if tag == "loca":
                    locaIndices.append(tableIndex)
            else:
                if [tag, data] not in tableData:
                    tableData.append([tag, data])
                    tableChecksums.append(
                        [tag, font.reader.tables[tag].checkSum])
                    tableOrder.append(tag)
                tableIndices[tag] = tableData.index([tag, data])
        collectionDirectory.append(
            dict(numTables=len(tableIndices),
                 flavor=font.sfntVersion,
                 index=tableIndices))
        font.close()
        del font

    if MismatchGlyfLoca:
        locaIndices.reverse()
        for i, entry in enumerate(collectionDirectory):
            entry["index"]["loca"] = locaIndices[i]
    totalData = "".join([data[1][1] for data in tableData])
    compData = brotli.compress(totalData, brotli.MODE_FONT)
    if len(compData) >= len(totalData):
        compData = totalData

    directory = [
        dict(tag=tag, origLength=0, transformLength=0) for tag in tableOrder
    ]

    header, directory, collectionHeader, collectionDirectory, tableData = defaultTestData(
        directory=directory,
        tableData=tableData,
        compressedData=compData,
        collectionDirectory=collectionDirectory)

    data = packTestHeader(header)
    data += packTestDirectory(directory, isCollection=True)
    data += packTestCollectionHeader(collectionHeader)
    data += packTestCollectionDirectory(collectionDirectory)
    data += tableData

    data = padData(data)

    return data
def main():
    args = parser.parse_args()
    projectVersion = getVersion()

    for font_path in args.fonts:

        # open font path as a font object, for manipulation
        ttfont = TTFont(font_path)

        # check for gvar table to see whether it's a variable font
        if 'gvar' not in ttfont.keys():
            fontIsStatic = True
            print("\n-------------------------------------------\nFont is static.")
        else:
            fontIsStatic = False
            print("\n-------------------------------------------\nFont is variable.")


        # GET NAME ID 17, typographic style name, to use in name ID 6
        styleName = getFontNameID(ttfont, 17)
        print(styleName)
        styleNames = str(styleName).split(' ')

        # UPDATE NAME ID 16, typographic family name
        famName = getFontNameID(ttfont, 16)

       
        newFamName = f"{famName} {projectVersion}st"
        newFamName = abbreviateName(newFamName, styleNames)
        setFontNameID(ttfont, 16, newFamName)

        

        

        # UPDATE NAME ID 6
        # replace last part of postScript font name, e.g. "LinearA" from "RecursiveMono-LinearA"

        if fontIsStatic:
            psName = str(getFontNameID(ttfont, 6))
            # psStyle = psName.split("-")[-1]
            psFam = psName.split("-")[0]
            newPsName = psName.replace(psFam, f"{psFam}{projectVersion.replace(' ','').replace('1.','')}st")

            if 'Beta' in newPsName:
                newPsName = newPsName.replace('Beta', NAME_ABBR['Beta'])

            newPsName = abbreviateName(newPsName, styleNames)
        else:
            print("Variable font")
            psName = str(getFontNameID(ttfont, 6))
            psFam = psName.split("-")[0]
            newPsName = psName.replace(psFam, psFam + projectVersion.replace(' ','').replace('1.',''))

        # set new ps name
        setFontNameID(ttfont, 6, newPsName)


        # VERSION, ID 5 (e.g. "Version 1.005")

        if "Beta" in projectVersion:
            newVersion = f"Version {projectVersion.replace('Beta ','')}"
        else:
            newVersion = f"Version {projectVersion}"

        setFontNameID(ttfont, 5, newVersion)

        # FULL FONT NAME, ID 4

        if fontIsStatic:

            newFamName = f"{famName} {projectVersion}st"

            newFamName = newFamName + ' ' + styleName
            styleName = styleName.replace('Linear ','').replace('Casual ','')

            # if 'Linear' in styleName:
            #     newFamName = newFamName + ' ' + styleName
            #     styleName = styleName.replace('Linear ','')

            # if 'Casual' in styleName:
            #     newFamName = newFamName + ' ' + styleName
            #     styleName = styleName.replace('Casual ','')

            newFamName = newFamName.replace(' Italic','').replace('Italic','')
        else:
            newFamName = f"{famName} {projectVersion}"

        newFamName = abbreviateName(newFamName, styleNames)

        if fontIsStatic:
            completeName = newFamName
            if 'Italic' in styleName:
                completeName = abbreviateName(newFamName + 'Italic', styleNames)
            setFontNameID(ttfont, 4, completeName)
        else:
            newFamName = abbreviateName(newFamName, styleNames)
            setFontNameID(ttfont, 4, newFamName)


        # UNIQUE FONT NAME, ID 3 (e.g. 1.005;ARRW;RecursiveSans-LinearA)

        oldUniqueID = str(getFontNameID(ttfont, 3))
        oldUniqueIDParts = oldUniqueID.split(";")
        newUniqueID = f"{projectVersion.replace('Beta ','')};{oldUniqueIDParts[1]};{newPsName}"
        setFontNameID(ttfont, 3, newUniqueID)

        # UPDATE BASIC FONT NAME, id 1

        legalStyleNames = ['Regular', 'Italic', 'Bold', 'Bold Italic']
        # TODO: 
        if styleName not in legalStyleNames and 'Italic' in styleName:
            styleName = 'Italic'
        if styleName not in legalStyleNames and 'Italic' not in styleName:
            styleName = 'Regular'

        setFontNameID(ttfont, 2, styleName)
        setFontNameID(ttfont, 1, newFamName)

        # SAVE FONT
        if args.inplace:
            ttfont.save(font_path)
        else:
            ttfont.save(font_path + '.fix')
Пример #30
0
def makeWeb(infile, outfile):
    """If we are building a web version then try to minimise file size"""

    # "short-post" generates a post table without glyph names to save some KBs
    # since glyph names are only needed for PDF's as readers use them to
    # "guess" characters when copying text, which is of little use in web fonts.
    flags = ("opentype", "short-post", "omit-instructions")

    font = fontforge.open(infile)
    font.encoding = "UnicodeBmp"  # avoid a crash if compact was set

    # removed compatibility glyphs that of little use on the web
    compat_ranges = (
        (0xfb50, 0xfbb1),
        (0xfbd3, 0xfd3d),
        (0xfd50, 0xfdf9),
        (0xfdfc, 0xfdfc),
        (0xfe70, 0xfefc),
    )

    for glyph in font.glyphs():
        for i in compat_ranges:
            start = i[0]
            end = i[1]
            if start <= glyph.unicode <= end:
                font.removeGlyph(glyph)
                break

    tmpfile = mkstemp(suffix=os.path.basename(outfile))[1]
    font.generate(tmpfile, flags=flags)
    font.close()

    # now open in fontTools
    from fontTools.ttLib import TTFont
    ftfont = TTFont(tmpfile)

    # our 'name' table is a bit bulky, and of almost no use in for web fonts,
    # so we strip all unnecessary entries.
    name = ftfont['name']
    names = []
    for record in name.names:
        platID = record.platformID
        langID = record.langID
        nameID = record.nameID

        # we keep only en_US entries in Windows and Mac platform id, every
        # thing else is dropped
        if (platID == 1 and langID == 0) or (platID == 3 and langID == 1033):
            if nameID == 13:
                # the full OFL text is too much, replace it with a simple
                # string
                if platID == 3:
                    # MS strings are UTF-16 encoded
                    text = 'OFL v1.1'.encode('utf_16_be')
                else:
                    text = 'OFL v1.1'
                record.string = text
                names.append(record)
            # keep every thing else except Descriptor, Sample Text
            elif nameID not in (10, 19):
                names.append(record)

    name.names = names

    # force compiling tables by fontTools, saves few tens of KBs
    for tag in ftfont.keys():
        if hasattr(ftfont[tag], "compile"):
            ftfont[tag].compile(ftfont)

    ftfont.save(outfile)
    ftfont.close()

    os.remove(tmpfile)
Пример #31
0
class TTF: #####################################################################################
    tt = None
    cmap = None
    
    width = 0
    linegap = 0
    
    ##### INITIATION #####======================================================================
    def __init__(self, fname):  # Loads TrueType Font from fname. Returns success
        self.tt = TTFont(fname)
        
        #        print self.tt.getGlyphNames()
        self.getCMAP()
        
        print self.tt.keys()
        
        #        print self.tt['head']
        self.width = .8*self.tt['hhea'].advanceWidthMax
        self.linegap = self.tt['hhea'].lineGap
        
        print self.width, self.linegap
    #        print self.tt[ 'hdmx' ].hdmx
    #        print self.tt[ 'kern' ]
    #
    #        print self.tt.tables['kern']
    
    def __getitem__(self, i):
        if type(i) is str:
            i = ord(i)
        #self.tt.getGlyphNames()[i]
        #        print self.getCMAP().cmap[i]
        return self.shapeFromGlyph( self.tt[ 'glyf' ][ self.getCMAP().cmap[i] ] )
    
    def getCMAP(self):
        if self.cmap == None:
            for table in self.tt['cmap'].tables:
                print table
                if isinstance(table, fontTools.ttLib.tables._c_m_a_p.cmap_format_0) or isinstance(table, fontTools.ttLib.tables._c_m_a_p.cmap_format_4):
#                print table
#                print table.cmap
#                print "\n"
#                print sorted(table.cmap.items())
                    self.cmap = table
                    break
        return self.cmap
    
    def shapeFromGlyph(self, glyph):    # Save loaded glyphs for speed
        toReturn = Shape([],[])
        
        if glyph.isComposite():
            print "Warning: This case is broken!, it does not transform..."
            #            print "COMPOSITE"
            for component in glyph.components:
                print component
                print component.getComponentInfo()
                [name, transform] = component.getComponentInfo()
                print transform
                print type(transform)
                m = Matrix(transform)
                print m
                print self.tt['glyf'][name]
                toReturn.add(self.shapeFromGlyph(self.tt['glyf'][name]))
#                for item in self.getCMAP().cmap.items():
#                    if item[1] == name:
#                        toReturn.add(self.shapeFromGlyph(self.tt[ 'glyf' ][item[0]]))
#                        break
        else:
            
#            print "NOT COMPOSITE"
            last = 0
            for i in range(glyph.numberOfContours):
                toAdd = Polyline([], True)
                prevV = None
                prevOn = None
                prevprevV = None
                prevprevOn = None
                
                firstV = None
                firstOn = None
                secondV = None
                secondOn = None
                for j in range(last, glyph.endPtsOfContours[i] + 1):
                    v = Vector(glyph.coordinates[j][0], glyph.coordinates[j][1])/float(self.width)  # This is a temporary fix!
                    on = glyph.flags[j] & 0x01
                    
                    if firstOn == None:
                        firstOn = on
                        firstV = v
                    elif secondOn == None:
                        secondOn = on
                        secondV = v
                    
#                    print v
#                    print prevprevOn, prevOn, on
#                    print prevprevV, prevV, v

                    if prevOn and on:
                        if prevprevOn == None:
                            toAdd.add(prevV)
                        toAdd.add(v)
                    elif prevOn != None and prevprevOn != None:
                        if prevprevOn and not prevOn:
                            if on:          # 101
                                toAdd.add(qBezier(prevprevV, prevV, v))
                            elif not on:    # 100
#                                print (prevV + v)
#                                print (prevV + v)/2
                                toAdd.add(qBezier(prevprevV, prevV, (prevV + v)/2))
                        elif not prevprevOn and not prevOn:
                            if on:          # 001
                                toAdd.add(qBezier((prevprevV + prevV)/2, prevV, v))
                            elif not on:    # 000
                                toAdd.add(qBezier((prevprevV + prevV)/2, prevV, (prevV + v)/2))
                                
                    prevprevOn = prevOn
                    prevprevV = prevV
                    prevOn = on
                    prevV = v
#                    print "toAdd: "
#                    print toAdd
#                    print "First Here"
#                    print prevOn, on, firstOn
#                    print prevprevV, v, firstV

#                toAdd.add(Vector(0,0))

                if not on:
                    if firstOn != None and not firstOn:
                        if prevprevOn:
                            toAdd.add(qBezier(prevprevV, v, (v + firstV)/2))
                        else:
                            toAdd.add(qBezier((prevprevV + v)/2, v, (v + firstV)/2))
                    if firstOn != None and firstOn:
                        if prevprevOn:
                            toAdd.add(qBezier(prevprevV, v, firstV))
                        else:
                            toAdd.add(qBezier((prevprevV + v)/2, v, firstV))
                                    
#                if firstOn != None and secondOn != None:
#                    print "First Second Here"
#                    print on, firstOn, secondOn
##                    if on and firstOn and secondOn:                # 111
#                    if on and not firstOn and secondOn:             # 101
#                        toAdd.add(qBezier(v, firstV, secondV))
#                    if on and not firstOn and not secondOn:         # 100
#                        toAdd.add(qBezier(v, firstV, (firstV + secondV)/2))
#                    if not on and not firstOn and secondOn:         # 001
#                        toAdd.add(qBezier((v + firstV)/2, firstV, secondV))
#                    if not on and not firstOn and not secondOn:     # 000
#                        toAdd.add(qBezier((v + firstV)/2, firstV, (firstV + secondV)/2))

#                toAdd.add(v)
                last = glyph.endPtsOfContours[i] + 1
#                print toAdd.area()
#                toAdd.add(Vector(0,0))
                toReturn.add(toAdd)
#                print "toReturn: "
#                print toReturn

            if len(toReturn.polylines) > 1:
                k = 0
    
                for i in range(0, toReturn.size):
#                    print toReturn.polylines[i].area()
                    if abs(toReturn.polylines[i].area()) > abs(toReturn.polylines[k].area()):   # Save area for speed
                        k = i
                
                finList = []
                    
                for i in range(0, toReturn.size):
                    if toReturn.polylines[i].area() < 0:
#                        print k, i
#                        print toReturn.polylines[i]

#                        print toReturn.polylines[k]

#                        toReturn.polylines[i].points.reverse()

                        m = 0 #toReturn.polylines[i].size-1
                            
                        j = 0
                        norm = (toReturn.polylines[k].points[0] - toReturn.polylines[i].points[m]).norm2()    # Possible error if empty
                            
                        toReturn.polylines[k].sizeCalc()
                                
                        for l in range(0, toReturn.polylines[k].size):
#                            print norm, (toReturn.polylines[k].points[l] - toReturn.polylines[i].points[m]).norm2()
                            if (toReturn.polylines[k].points[l] - toReturn.polylines[i].points[m]).norm2() < norm:
                                j = l
                                norm = (toReturn.polylines[k].points[l] - toReturn.polylines[i].points[m]).norm2()
                                    
#                        print j
# toReturn.polylines[k].points[j],
                        toReturn.polylines[k].points = toReturn.polylines[k].points[0:j+1] + toReturn.polylines[i].points + [toReturn.polylines[i].points[0]] +  toReturn.polylines[k].points[j:]
#                        toReturn.polylines[k].points = toReturn.polylines[k].points[0:j+1] + [toReturn.polylines[i].points[toReturn.polylines[i].size-1]] + toReturn.polylines[i].points + toReturn.polylines[k].points[j:]
                        toReturn.polylines[k].sizeCalc()
                    else:
                        finList += [ toReturn.polylines[i] ]
                            
                toReturn = Shape(finList)
#                        toReturn.polylines.remove(i)
#                finList = [toReturn.polylines[k]]
#        
#                for polyline in toReturn.polylines:
#                    print polyline
#                    if polyline != toReturn.polylines[k]:
#                        print polyline.area()
#                        if polyline.area() < 0:
#                    
#                            highest = polyline.points[0]
#                            print "HIGHEST: ", highest
#                            j = 0
#                            
#                            for i in range(0, polyline.size):
#                                if polyline.points[i].y > highest.y:
#                                    highest = polyline.points[i]
#                                    j = i
#                    
#                            polyline.points.insert(i, highest)
#                            polyline.points.insert(i, highest + Vector(0,2))
#                            polyline.points.insert(i, highest)
#                    
#                        else:
#                            print "extended"
#                            finList.extend(polyline)
#                    
#                    toReturn.polylines[k].union(polyline)
#
#                return Shape(finList,[])

        return toReturn

    def shapeFromString(self, string):
        toReturn = Shape([],[])
        #        dv = Vector(1. + self.linegap/self.width,0)
        v = Vector(0,0)
        w = Vector(0,0)
        
        for char in string:
            
#            plt.plot([v.x, v.x, v.x + wid, v.x + wid, v.x], [v.y, v.y + hgt, v.y + hgt, v.y, v.y])

#            toReturn.polylines.extend( [(letter + v - Vector(shift, 0))] )
            if char != '\n':
                letter = self[char]
                bb = letter.getBoundingBox()
                if bb != None:
                    wid = bb[1].x - bb[0].x
                    hgt = bb[1].y - bb[0].y
                    shift = bb[0].x
                else:
                    wid = 1
                    hgt = 1
                    shift = 0
                for polyline in letter.polylines:
                    toReturn.polylines.extend( [(polyline + v - Vector(shift, 0))] )
    #            polyline = self[char]
    #            (polyline + v).plot()
                v += Vector(wid + .2, 0)
            else:
                v = Vector(0,v.y-hgt - .6)
#
#            polyline = font2[char]
#            (polyline + w).plot()
#            w += dv

        toReturn.sizeCalc()
        
        return toReturn
            
    def shapeFromStringBoundedCentered(self, string, w=0, h=0):
        shape = self.shapeFromString(string)
        
        [bbll, bbur] = shape.getBoundingBox()
        c = (bbll + bbur)/2
        
        if w < 0 and h < 0:
            return shape - c
        elif w < 0:
            w = h
        elif h < 0:
            h = w
        
        numlines = 1;
        for char in string:
            if char == '\n':
                numlines += 1;
    
        h *= (numlines*1.6 - .6);
        
        sw = (bbur-bbll).x
        sh = (bbur-bbll).y
        
        if sw < 0 or sh < 0:
            print "something went horribly wrong..."
        
        ratioShape = sw/sh
        ratioBound = w/h
        
        if ratioBound > ratioShape:
            return (shape - c)*(w/sw)
        elif ratioBound <= ratioShape:
            return (shape - c)*(h/sh)
Пример #32
0
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from __future__ import print_function
from fontTools.ttLib import TTFont, sfnt
from os.path import splitext
import sys

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print("usage: python %s filename" % sys.argv[0], file=sys.stderr)
        sys.exit(1)
    filename = sys.argv[1]
    basename = splitext(filename)[0]

    sfnt.USE_ZOPFLI = True
    for flavor in ["woff", "woff2"]:
        outfilename = "%s.%s" % (basename, flavor)
        print("Processing %s => %s" % (filename, outfilename))
        font = TTFont(filename, recalcBBoxes=False, recalcTimestamp=False)
        for t in font.keys():
            if hasattr(font[t], "compile"):
                font[t].compile(font)
        font.flavor = flavor
        font.save(outfilename, reorderTables=False)
Пример #33
0
class DFont(TTFont):
    """Container font for ttfont, freetype and hb fonts"""
    def __init__(self, path=None, lazy=False, size=1500):
        self.path = path
        self.ttfont = TTFont(self.path)
        self._src_ttfont = TTFont(self.path)
        self.glyphset = None
        self.recalc_glyphset()
        self.axis_order = None
        self.instance_coordinates = self._get_dflt_instance_coordinates()
        self.instances_coordinates = self._get_instances_coordinates()
        self.glyphs = self.marks = self.mkmks = self.kerns = \
            self.glyph_metrics = self.names = self.attribs = None

        self.ftfont = freetype.Face(self.path)
        self.ftslot = self.ftfont.glyph

        self.size = size
        self.ftfont.set_char_size(self.size)

        with open(self.path, 'rb') as fontfile:
            self._fontdata = fontfile.read()
        self.hbface = hb.Face.create(self._fontdata)
        self.hbfont = hb.Font.create(self.hbface)

        self.hbfont.scale = (self.size, self.size)

        if not lazy:
            self.recalc_tables()

    def _get_instances_coordinates(self):
        if self.is_variable:
            return [i.coordinates for i in self._src_ttfont["fvar"].instances]
        return None

    def _get_dflt_instance_coordinates(self):
        if self.is_variable:
            return {i.axisTag: i.defaultValue for i in self._src_ttfont['fvar'].axes}
        return None

    def glyph(self, name):
        return self.glyphset[name]

    def recalc_glyphset(self):
        if not 'cmap' in self.ttfont.keys():
            self.glyphset = []
        inputs = InputGenerator(self).all_inputs()
        self.glyphset = {g.name: g for g in inputs}

    @property
    def is_variable(self):
        if 'fvar' in self._src_ttfont:
            return True
        return False

    def set_variations(self, axes):
        """Instantiate a ttfont VF with axes vals"""
        if self.is_variable:
            font = instantiateVariableFont(self._src_ttfont, axes, inplace=False)
            self.ttfont = copy(font)
            self.axis_order = [a.axisTag for a in self._src_ttfont['fvar'].axes]
            self.instance_coordinates = {a.axisTag: a.defaultValue for a in
                                    self._src_ttfont['fvar'].axes}
            for axis in axes:
                if axis in self.instance_coordinates:
                    self.instance_coordinates[axis] = axes[axis]
                else:
                    logger.info("font has no axis called {}".format(axis))
            self.recalc_tables()

            coords = []
            for name in self.axis_order:
                coord = FT_Fixed(int(self.instance_coordinates[name]) << 16)
                coords.append(coord)
            ft_coords = (FT_Fixed * len(coords))(*coords)
            FT_Set_Var_Design_Coordinates(self.ftfont._FT_Face, len(ft_coords), ft_coords)
            self.hbface = hb.Face.create(self._fontdata)
            self.hbfont = hb.Font.create(self.hbface)
            self.hbfont.set_variations(self.instance_coordinates)
            self.hbfont.scale = (self.size, self.size)
        else:
            logger.info("Not vf")

    def set_variations_from_static(self, dfont):
        """Set the variations of a variable font using the vals from a
        static font"""
        variations = {}
        if self.is_variable:
            variations["wght"] = dfont.ttfont["OS/2"].usWeightClass
            # TODO (M Foley) add wdth, slnt axes
            self.set_variations(variations)

    def recalc_tables(self):
        """Recalculate DFont tables"""
        self.recalc_glyphset()
        anchors = DumpAnchors(self)
        self.glyphs = dump_glyphs(self)
        self.marks = anchors.marks_table
        self.mkmks = anchors.mkmks_table
        self.glyph_metrics = dump_glyph_metrics(self)
        self.attribs = dump_attribs(self)
        self.names = dump_nametable(self)
        self.kerns = dump_kerning(self)
        self.metrics = dump_glyph_metrics(self)
Пример #34
0
import base64
from fontTools.ttLib import TTFont

font_face = "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"
b = base64.b64decode(font_face)

f = open('58.ttf', 'wb')
f.write(b)
f.close()

fo = TTFont("58.ttf")
print(fo.keys())

fo.saveXML('test.xml')
Пример #35
0
def test_ensureDecompiled(lazy):
    # test that no matter the lazy value, ensureDecompiled decompiles all tables
    font = TTFont()
    font.importXML(os.path.join(DATA_DIR, "TestTTF-Regular.ttx"))
    # test font has no OTL so we add some, as an example of otData-driven tables
    addOpenTypeFeaturesFromString(
        font,
        """
        feature calt {
            sub period' period' period' space by ellipsis;
        } calt;

        feature dist {
            pos period period -30;
        } dist;
        """
    )
    # also add an additional cmap subtable that will be lazily-loaded
    cm = CmapSubtable.newSubtable(14)
    cm.platformID = 0
    cm.platEncID = 5
    cm.language = 0
    cm.cmap = {}
    cm.uvsDict = {0xFE00: [(0x002e, None)]}
    font["cmap"].tables.append(cm)

    # save and reload, potentially lazily
    buf = io.BytesIO()
    font.save(buf)
    buf.seek(0)
    font = TTFont(buf, lazy=lazy)

    # check no table is loaded until/unless requested, no matter the laziness
    for tag in font.keys():
        assert not font.isLoaded(tag)

    if lazy is not False:
        # additional cmap doesn't get decompiled automatically unless lazy=False;
        # can't use hasattr or else cmap's maginc __getattr__ kicks in...
        cm = next(st for st in font["cmap"].tables if st.__dict__["format"] == 14)
        assert cm.data is not None
        assert "uvsDict" not in cm.__dict__
        # glyf glyphs are not expanded unless lazy=False
        assert font["glyf"].glyphs["period"].data is not None
        assert not hasattr(font["glyf"].glyphs["period"], "coordinates")

    if lazy is True:
        # OTL tables hold a 'reader' to lazily load when lazy=True
        assert "reader" in font["GSUB"].table.LookupList.__dict__
        assert "reader" in font["GPOS"].table.LookupList.__dict__

    font.ensureDecompiled()

    # all tables are decompiled now
    for tag in font.keys():
        assert font.isLoaded(tag)
    # including the additional cmap
    cm = next(st for st in font["cmap"].tables if st.__dict__["format"] == 14)
    assert cm.data is None
    assert "uvsDict" in cm.__dict__
    # expanded glyf glyphs lost the 'data' attribute
    assert not hasattr(font["glyf"].glyphs["period"], "data")
    assert hasattr(font["glyf"].glyphs["period"], "coordinates")
    # and OTL tables have read their 'reader'
    assert "reader" not in font["GSUB"].table.LookupList.__dict__
    assert "Lookup" in font["GSUB"].table.LookupList.__dict__
    assert "reader" not in font["GPOS"].table.LookupList.__dict__
    assert "Lookup" in font["GPOS"].table.LookupList.__dict__
Пример #36
0
class DFont(TTFont):
    """Container font for ttfont, freetype and hb fonts"""
    def __init__(self,
                 path=None,
                 lazy=False,
                 size=1500,
                 ft_load_glyph_flags=FTHintMode.UNHINTED):
        self.path = path
        self.ttfont = TTFont(self.path)

        has_outlines = self.ttfont.has_key("glyf") or self.ttfont.has_key(
            "CFF ")
        if not has_outlines:
            # Create faux empty glyf table with empty glyphs to make
            # it a valid font, e.g. for old-style CBDT/CBLC fonts
            logger.warning(
                "No outlines present, treating {} as bitmap font".format(
                    self.path))
            self.ttfont["glyf"] = newTable("glyf")
            self.ttfont["glyf"].glyphs = {}
            pen = TTGlyphPen({})
            for name in self.ttfont.getGlyphOrder():
                self.ttfont["glyf"].glyphs[name] = pen.glyph()

        self._src_ttfont = TTFont(self.path)
        self.glyphset = None
        self.recalc_glyphset()
        self.axis_order = None
        self.instance_coordinates = self._get_dflt_instance_coordinates()
        self.instances_coordinates = self._get_instances_coordinates()
        self.glyphs = self.marks = self.mkmks = self.kerns = \
            self.glyph_metrics = self.names = self.attribs = None

        self.ftfont = freetype.Face(self.path)
        self.ftslot = self.ftfont.glyph
        self.ft_load_glyph_flags = ft_load_glyph_flags

        self.size = size
        if self.ftfont.is_scalable:
            self.ftfont.set_char_size(self.size)

        with open(self.path, 'rb') as fontfile:
            self._fontdata = fontfile.read()
        self.hbface = hb.Face.create(self._fontdata)
        self.hbfont = hb.Font.create(self.hbface)

        self.hbfont.scale = (self.size, self.size)

        if not lazy:
            self.recalc_tables()

    def _get_instances_coordinates(self):
        results = {}
        if self.is_variable:
            for inst in self._src_ttfont['fvar'].instances:
                inst_name = self._src_ttfont['name'].getName(
                    inst.subfamilyNameID, 3, 1, 1033).toUnicode()
                results[inst_name] = inst.coordinates
        return results

    def _get_dflt_instance_coordinates(self):
        if self.is_variable:
            return {
                i.axisTag: i.defaultValue
                for i in self._src_ttfont['fvar'].axes
            }
        return None

    def glyph(self, name):
        return self.glyphset[name]

    def recalc_glyphset(self):
        if not 'cmap' in self.ttfont.keys():
            self.glyphset = []
        inputs = InputGenerator(self).all_inputs()
        self.glyphset = {g.name: g for g in inputs}

    @property
    def is_variable(self):
        if 'fvar' in self._src_ttfont:
            return True
        return False

    def set_variations(self, axes):
        """Instantiate a ttfont VF with axes vals"""
        logger.debug("Setting variations to {}".format(axes))
        if self.is_variable:
            font = instantiateVariableFont(self._src_ttfont,
                                           axes,
                                           inplace=False)
            self.ttfont = copy(font)
            self.axis_order = [
                a.axisTag for a in self._src_ttfont['fvar'].axes
            ]
            self.instance_coordinates = {
                a.axisTag: a.defaultValue
                for a in self._src_ttfont['fvar'].axes
            }
            for axis in axes:
                if axis in self.instance_coordinates:
                    self.instance_coordinates[axis] = axes[axis]
                else:
                    logger.info("font has no axis called {}".format(axis))
            self.recalc_tables()

            coords = []
            for name in self.axis_order:
                coord = FT_Fixed(int(self.instance_coordinates[name]) << 16)
                coords.append(coord)
            ft_coords = (FT_Fixed * len(coords))(*coords)
            FT_Set_Var_Design_Coordinates(self.ftfont._FT_Face, len(ft_coords),
                                          ft_coords)
            self.hbface = hb.Face.create(self._fontdata)
            self.hbfont = hb.Font.create(self.hbface)
            self.hbfont.set_variations(self.instance_coordinates)
            self.hbfont.scale = (self.size, self.size)
        else:
            logger.info("Not vf")

    def set_variations_from_static(self, static_font):
        """Set VF font variations so they match a static font."""
        if not self.is_variable:
            raise Exception("Not a variable font")

        # Use an fvar instance if its name matches the static font's
        # typographic subfamily name or subfamily name
        subfamilyname = static_font.ttfont['name'].getName(2, 3, 1, 1033)
        typosubfamilyname = static_font.ttfont['name'].getName(17, 3, 1, 1033)

        anysubfamilyname = typosubfamilyname or subfamilyname
        subfamilyname = anysubfamilyname.toUnicode(
        ) if anysubfamilyname else ""

        # The Google Fonts v1 api can only handle the wght axis. For families
        # which have widths, we have to release them as a seperate family,
        # https://fonts.google.com/?query=condensed
        # To distinguish the width family from the normal family, we append
        # the width to the family name e.g Roboto Condensed
        filename = os.path.basename(static_font.path)
        family_name = filename.split("-")[0]
        family_name_width = find_token(family_name,
                                       list(WIDTH_NAME_TO_FVAR.keys()))
        if family_name_width:
            subfamilyname = f"{family_name_width} {subfamilyname}"

        if subfamilyname in self.instances_coordinates:
            logger.debug(
                f"Instance name '{subfamilyname}' matches static font "
                "subfamily names. Setting variations using this instance.")
            variations = self.instances_coordinates[subfamilyname]
            self.set_variations(variations)
            return

        # if the font doesn't contain an instance name which matches the
        # static font, infer the correct values
        logger.debug(
            f"Font does not contain an instance name which matches "
            f"static font subfamily names '{subfamilyname}'. Inferring "
            "values instead.")
        variations = {}
        # wght
        parsed_weight = find_token(os.path.basename(static_font.path),
                                   list(WEIGHT_NAME_TO_FVAR.keys()))
        if parsed_weight:
            variations["wght"] = WEIGHT_NAME_TO_FVAR[parsed_weight]
        else:
            logger.debug(
                f"Couldn't parse weight value from {static_font.path}")
            weight_class = static_font.ttfont["OS/2"].usWeightClass
            # Google Fonts used to set the usWeightClass of Thin static
            # fonts to 250 and the ExtraLight to 275. Override these
            # values with 100 and 200.
            if weight_class == 250:
                weight_class = 100
            if weight_class == 275:
                weight_class = 200
            variations["wght"] = weight_class

        # wdth
        # We cannot simply use OS/2.usWidthClass since Google Fonts
        # releases Condensed styles as new families. These new families
        # have a usWidthClass of 5 (Normal).
        parsed_width = find_token(os.path.basename(static_font.path),
                                  list(WIDTH_NAME_TO_FVAR.keys()))
        if parsed_width:
            variations["wdth"] = WIDTH_NAME_TO_FVAR[parsed_width]
        else:
            logger.debug(
                f"Couldn't parse weight value from {static_font.path}")
            width_class = static_font.ttfont["OS/2"].usWidthClass
            variations["wdth"] = WIDTH_CLASS_TO_FVAR[width_class]
        # TODO (M Foley) add slnt axes
        self.set_variations(variations)

    def recalc_tables(self):
        """Recalculate DFont tables"""
        self.recalc_glyphset()
        anchors = DumpAnchors(self)
        self.glyphs = dump_glyphs(self)
        self.marks = anchors.marks_table
        self.mkmks = anchors.mkmks_table
        self.attribs = dump_attribs(self)
        self.names = dump_nametable(self)
        self.kerns = dump_kerning(self)
        self.metrics = dump_glyph_metrics(self)
        self.gdef_base, self.gdef_mark = dump_gdef(self)