Пример #1
1
def convertToOTF(ttfPath, dest, report):
    temp = tempfile.mkstemp(suffix=".otf")[1]

    font = RFont(ttfPath, document=False, showInterface=False)
    font.kerning.clear()
    for attr in font.info.asDict().keys():
        if attr not in defaultFontInfoAttributes:
            setattr(font.info, attr, None)

    result = font.generate(path=temp, format="otf", decompose=False, checkOutlines=False, autohint=False, releaseMode=True, glyphOrder=font.glyphOrder)
    if not font.hasInterface():
        font.close()
    report.write(result)

    sourceFont = TTFont(temp)
    sourceFontWithTables = TTFont(ttfPath)
    for table in ["loca", "OS/2", "cmap", "name", "GSUB", "GPOS", "GDEF", "kern"]:
        if table in sourceFontWithTables:
            sourceFont[table] = sourceFontWithTables[table]

    sourceFont.save(dest)

    result = OTFAutohint(dest)
    report.writeItems(result)

    os.remove(temp)
Пример #2
0
def test_ttcompile_timestamp_calcs(inpath, outpath1, outpath2, tmpdir):
    inttx = os.path.join("Tests", "ttx", "data", inpath)
    outttf1 = tmpdir.join(outpath1)
    outttf2 = tmpdir.join(outpath2)
    options = ttx.Options([], 1)
    # build with default options = do not recalculate timestamp
    ttx.ttCompile(inttx, str(outttf1), options)
    # confirm that font was built
    assert outttf1.check(file=True)
    # confirm that timestamp is same as modified time on ttx file
    mtime = os.path.getmtime(inttx)
    epochtime = timestampSinceEpoch(mtime)
    ttf = TTFont(str(outttf1))
    assert ttf["head"].modified == epochtime

    # reset options to recalculate the timestamp and compile new font
    options.recalcTimestamp = True
    ttx.ttCompile(inttx, str(outttf2), options)
    # confirm that font was built
    assert outttf2.check(file=True)
    # confirm that timestamp is more recent than modified time on ttx file
    mtime = os.path.getmtime(inttx)
    epochtime = timestampSinceEpoch(mtime)
    ttf = TTFont(str(outttf2))
    assert ttf["head"].modified > epochtime

    # --no-recalc-timestamp will keep original timestamp
    options.recalcTimestamp = False
    ttx.ttCompile(inttx, str(outttf2), options)
    assert outttf2.check(file=True)
    inttf = TTFont()
    inttf.importXML(inttx)
    assert inttf["head"].modified == TTFont(str(outttf2))["head"].modified
Пример #3
0
    def test_retain_gids_cff2(self):
        ttx_path = self.getpath("../../varLib/data/master_ttx_varfont_otf/TestCFF2VF.ttx")
        font, fontpath = self.compile_font(ttx_path, ".otf")

        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,
                "T",
            ]
        )
        subsetfont = TTFont(subsetpath)

        self.assertEqual(len(subsetfont.getGlyphOrder()), len(font.getGlyphOrder()[0:3]))

        hmtx = subsetfont["hmtx"]
        self.assertEqual(hmtx["glyph00001"], (  0,  0))
        self.assertEqual(hmtx["T"], (600, 41))

        subsetfont["CFF2"].cff[0].decompileAllCharStrings()
        cs = subsetfont["CFF2"].cff[0].CharStrings
        self.assertEqual(cs["glyph00001"].program, [])
        self.assertGreater(len(cs["T"].program), 0)
Пример #4
0
    def post(self):
        fontdata = self.request.POST.get('font', None)

        # Need to use isinstance as cgi.FieldStorage always evaluates to False.
        # See http://bugs.python.org/issue19097
        if not isinstance(fontdata, cgi.FieldStorage):
            self.redirect('/font_conversion.html?' +  urllib.urlencode(
                {'err_msg': 'Please select a font'}))
            return

        #TODO(bstell) make this work correctly.
        font_type = 'woff'
        name = os.path.splitext(os.path.basename(fontdata.filename))[0]

        try:
            font = TTFont(fontdata.file)
        except:
            self.redirect('/font_conversion.html?' +  urllib.urlencode(
                {'err_msg': 'failed to parse font'}))
            return

        self.response.headers['Content-Type'] = 'application/font-woff'
        self.response.headers['Content-Disposition'] = \
            'attachment; filename={0}.{1}'.format(name, font_type)
        font.flavor = font_type
        output = StringIO.StringIO()
        font.save(output)
        self.response.out.write(output.getvalue())
Пример #5
0
 def get_font_xml(self):
     """
     获取字体 xml
     :return:
     """
     font = TTFont(self.font_path)
     font.saveXML(self.xml_path)
Пример #6
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)
Пример #7
0
def makeTTFont():
    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
    """.split()
    font = TTFont()
    font.setGlyphOrder(glyphs)
    return font
Пример #8
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)
Пример #9
0
def fontToUFO(src, dst, fileType=None):
	from robofab.ufoLib import UFOWriter
	from robofab.pens.adapterPens import SegmentToPointPen
	if fileType is None:
		fileType = guessFileType(src)
		if fileType is None:
			raise ValueError, "Can't determine input file type"
	ufoWriter = UFOWriter(dst)
	if fileType == "TTF":
		from fontTools.ttLib import TTFont
		font = TTFont(src, 0)
	elif fileType == "Type 1":
		from fontTools.t1Lib import T1Font
		font = T1Font(src)
	else:
		assert 0, "unknown file type: %r" % fileType
	inGlyphSet = font.getGlyphSet()
	outGlyphSet = ufoWriter.getGlyphSet()
	for glyphName in inGlyphSet.keys():
		print "-", glyphName
		glyph = inGlyphSet[glyphName]
		def drawPoints(pen):
			pen = SegmentToPointPen(pen)
			glyph.draw(pen)
		outGlyphSet.writeGlyph(glyphName, glyph, drawPoints)
	outGlyphSet.writeContents()
	if fileType == "TTF":
		info = extractTTFFontInfo(font)
	elif fileType == "Type 1":
		info = extractT1FontInfo(font)
	ufoWriter.writeInfo(info)
Пример #10
0
def find_and_replace_ttx(ttx_path, find_string, replace_string, tables=['name']):
    count = 0

    # 1. modify 'name' table
    if 'name' in tables:
        tree  = parse(ttx_path)
        root  = tree.getroot()
        for child in root.find('name'):
            if child.text.find(find_string) != -1:
                new_text = child.text.replace(find_string, replace_string)
                child.text = new_text
                count += 1
        tree.write(ttx_path)

    # 2. modify 'CFF ' table
    if 'CFF ' in tables:
        CFF_elements = ['version', 'Notice', 'Copyright', 'FullName', 'FamilyName', 'Weight']
        tt_font = TTFont()
        tt_font.importXML(ttx_path)
        font_dict = tt_font['CFF '].cff.topDictIndex.items[0]
        for element in CFF_elements:
            text = getattr(font_dict, element)
            if text.find(find_string) != -1:
                new_text = text.replace(find_string, replace_string)
                setattr(font_dict, element, new_text)
                count += 1
        tt_font.saveXML(ttx_path)

    # done
    return count
Пример #11
0
  def __zero_cmaps(self,output):
    font = TTFont(output)
    old_cmap_method = change_method(_c_m_a_p.table__c_m_a_p, _decompile_in_table_cmap,'decompile')
    old_12_method = change_method(_c_m_a_p.cmap_format_12_or_13,_decompile_in_cmap_format_12_13, 'decompile')
    old_4_method = change_method(_c_m_a_p.cmap_format_4,_decompile_in_cmap_format_4, 'decompile')

    cmap_offset = font.reader.tables['cmap'].offset
    cmapTables = font['cmap']
    cmap12 = cmapTables.getcmap(3, 10) #format 12
    cmap4 = cmapTables.getcmap(3, 1) #format 4

    ranges_to_zero = []
    if cmap12:
      ranges_to_zero.append((cmap_offset+cmap12.offset+16,cmap12.length-16))
    #if cmap4:
    #  ranges_to_zero.append((cmap_offset+cmap4.offset+14,cmap4.length-14))

    change_method(_c_m_a_p.cmap_format_12_or_13,old_12_method,'decompile')
    change_method(_c_m_a_p.cmap_format_4,old_4_method,'decompile')
    change_method(_c_m_a_p.table__c_m_a_p,old_cmap_method,'decompile')

    font.close()

    #if len(ranges_to_zero)<2: #return if both doesn't exist
    #  return

    for block in ranges_to_zero:
      filler = Filler(output)
      filler.fill(block[0], block[1], '\x00')
      filler.close()
Пример #12
0
 def __zero_charset_fmt(self,output):
   font = TTFont(output)
   cffTableOffset = font.reader.tables['CFF '].offset
   cffTable = font['CFF '].cff
   assert len(cffTable.fontNames) == 1 #only one font should be present
   charsetOffset = cffTable[cffTable.fontNames[0]].rawDict['charset']
   numGlyphs = font['maxp'].numGlyphs
   inner_file = font.reader.file
   inner_file.seek(cffTableOffset+charsetOffset)
   format = readCard8(inner_file);
   if format != 2 and format != 1:
     return None
   record_len = 4 if format == 2 else 3
   seenGlyphCount = 0
   size = 0
   while seenGlyphCount < numGlyphs:
     inner_file.seek(2,io.SEEK_CUR)
     if format == 2:
       nLeft = readCard16(inner_file)
     else: #format 1
       nLeft = readCard8(inner_file)
     seenGlyphCount += nLeft + 1
     size += 1
   font.close()
   filler = Filler(output)
   filler.fill(cffTableOffset+charsetOffset+1, record_len*size, '\x00')
   filler.close()
Пример #13
0
def process(infile, outfile, layer):
    font = TTFont(infile)
    glyf = font["glyf"]

    glyphNamesToKeep = []
    if layer == "letters":
        for glyphName in font.getGlyphNames():
            if glyphName not in _LAYER2_GLYPHS and glyphName not in _LAYER3_GLYPHS:
                glyphNamesToKeep.append(glyphName)
    elif layer == "diacritics":
        glyphNamesToKeep = _LAYER2_GLYPHS
    elif layer == "quranic-signs":
        glyphNamesToKeep = _LAYER3_GLYPHS

    for glyphName in font.getGlyphNames():
        if glyphName not in glyphNamesToKeep:
            glyph = glyf[glyphName]
            if glyphName in ("uni0670.medi", "uni06E5.medi", "uni06E6.medi") and layer == "letters":
                # We want to keep the kashida part of those glyphs.
                components = []
                for component in glyph.components:
                    if component.glyphName != glyphName.split(".")[0]:
                        components.append(component)
                glyph.components = components
            else:
                # This will cause FontTools not to output any outlines for that
                # glyph.
                glyph.numberOfContours = 0

    font.save(outfile)
Пример #14
0
    def __init__(self, sourcePath, location, DEBUG=False):
        self.instancePath = tempfile.mkstemp()[1]
        tempPath = sourcePath.replace('.ttf', '-instance.ttf')
        
        cmds = ['python', pathToFontToolsMutator, sourcePath]
        for k, v in location.items():
            cmds.append('%s=%s' %(k, v))
        proc = subprocess.Popen(cmds, stdout=subprocess.PIPE)
        out = proc.communicate()[0]

        if DEBUG:
            print '---'
            print ' '.join(cmds)
            print '---'
            print out

        myUUID = str(uuid.uuid4()).replace('-', '')
        f = TTFont(tempPath)
        self.fontName = 'VF'+str(myUUID)
        
        f['name'].setName(self.fontName, 6, 1, 0, 0) # Macintosh
        f['name'].setName(self.fontName, 6, 3, 1, 0x409) # Windows
        
        os.remove(tempPath)
        
        f.save(self.instancePath)
Пример #15
0
def load_ttx(ttx):
    f = UnicodeIO()
    f.write(ttx)
    f.seek(0)
    font = TTFont()
    font.importXML(f)
    return font
Пример #16
0
def test_max_ctx_calc_features_ttx(file_name, max_context):
    ttx_path = os.path.join(os.path.dirname(__file__),
                            'data', '{}.ttx'.format(file_name))
    font = TTFont()
    font.importXML(ttx_path)

    assert maxCtxFont(font) == max_context
Пример #17
0
 def compile_font(self, path, suffix, temp_dir):
     ttx_filename = os.path.basename(path)
     savepath = os.path.join(temp_dir, ttx_filename.replace('.ttx', suffix))
     font = TTFont(recalcBBoxes=False, recalcTimestamp=False)
     font.importXML(path)
     font.save(savepath, reorderTables=None)
     return font, savepath
Пример #18
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)
Пример #19
0
class TestClosureTaker(unittest.TestCase):

  def setUp(self):
    """Loads font and initializes ClosureTaker
    """
    self.font = TTFont('test_data/NotoSans-Regular_subset.ttf')
    self.closureTaker = ClosureTaker(self.font)

  def test_c(self):
    """Takes closure of character 'c' Expected result is array: [3]
    """
    self.closureTaker.clear()
    self.closureTaker.add_glyph_names(['c'])
    gids = self.closureTaker.closure()
    self.assertTrue(gids == [3], 'Closure of c is [c]')

  def test_clear(self):
    """Takes closure of cleared input lists Expected result is array: []
    """
    self.closureTaker.clear()
    gids = self.closureTaker.closure()
    self.assertTrue(gids == [], 'Closure of empty is []')

  def tearDown(self):
    """Closes font
    """
    self.font.close()
Пример #20
0
def main(args):
        #assume input is .ttx file
        #TODO:input is .ttf file
        input = args[0]
        ttf = TTFont()
        ttf.importXML(input,quiet=True)

        bytecode_font = BytecodeFont()
        constructCVTTable(ttf['cvt '].values)
        # TODO:for now just analyze the font program file, later
        # should add the prep and all the glyphs
        body = Body(bytecode_font, 'fpgm', ttf)

        constructInstructions(body)
        constructSuccessor(body)
        constructPredecessor(body)
        
        current_state = Environment()
        executor = AbstractExecutor(bytecode_font)

        instruction = body.instructions[0]
        
        while instruction in body.successors:
            executor.execute(instruction, current_state)
            instruction.prettyPrint()
            instruction = body.successors[instruction][0]

        for key,value in bytecode_font.function_table.items():
            print('Function #{0}:'.format(key))
            for instruction in value:
                instruction.prettyPrint()
Пример #21
0
class MyFontTools(object):
    def __init__(self, fontfile):
        self.font = TTFont(fontfile)

    def cmap_format_gbk2utf8(self):
        cmap = self.font['cmap']
        outtables = []
        for table in cmap.tables:
            if table.format in [4, 12, 13, 14]:
                outtables.append(table)
                
            # Convert ot format4
            if table.getEncoding() in SUPPORT_CONVERT_FROM_ENCODE:
                for gbk_code in table.cmap.keys():
                    uni_code= convert_from_gbk(gbk_code)
                    if gbk_code != uni_code:
                        table.cmap[uni_code] = table.cmap.pop(gbk_code)

                newtable = CmapSubtable.newSubtable(4)
                newtable.platformID = self.to_platformID
                newtable.platEncID = self.to_platEncID
                newtable.language = table.language
                newtable.cmap = table.cmap
                outtables.append(newtable)
        cmap.tables = outtables

    def vhea_fixed(self):
        self.font['vhea'].tableVersion=1.0


    def save(self, outfile):
        import pdb;pdb.set_trace()
        self.font.save(outfile)
Пример #22
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)
Пример #23
0
 def import_from_otf(self, otfpath):
     """Import color data (CPAL/COLR) from a font file.
         otfpath: Path of the font file"""
     font = TTFont(otfpath)
     if not (font.has_key("COLR") and font.has_key("CPAL")):
         print("ERROR: No COLR and CPAL table present in %s" % otfpath)
     else:
         print("Reading palette data ...")
         cpal = table_C_P_A_L_("CPAL")
         cpal.decompile(font["CPAL"].data, font)
         for j in range(len(cpal.palettes)):
             palette = cpal.palettes[j]
             _palette = {}
             for i in range(len(palette)):
                 color = palette[i]
                 _palette[str(i)] = "#%02x%02x%02x%02x" % (
                     color.red,
                     color.green,
                     color.blue,
                     color.alpha
                 )
             self.palettes.append(_palette)
         colr = table_C_O_L_R_("COLR")
         colr.decompile(font["COLR"].data, font)
         print("Reading layer data ...")
         for glyphname in colr.ColorLayers:
             layers = colr[glyphname]
             _glyph = ColorGlyph(self)
             _glyph.basename = glyphname
             for layer in layers:
                 _glyph.layers.append(layer.name)
                 _glyph.colors.append(layer.colorID)
             self[glyphname] = _glyph
         print("Done.")
     font.close()
Пример #24
0
    def installFont(self, path):
        """
        Install a font with a given path and the postscript font name will be returned.
        The postscript font name can be used to set the font as the active font.

        Fonts are installed only for the current process.
        Fonts will not be accesible outside the scope of drawBot.

        All installed fonts will automatically be uninstalled when the script is done.

        .. showcode:: /../examples/installFont.py
        """
        success, error = self._dummyContext.installFont(path)
        self._installedFontPaths.add(path)
        self._addInstruction("installFont", path)

        from fontTools.ttLib import TTFont, TTLibError
        try:
            font = TTFont(path)
            psName = font["name"].getName(6, 1, 0)
            if psName is None:
                psName = font["name"].getName(6, 3, 1)
            font.close()
        except IOError:
            raise DrawBotError("Font '%s' does not exist." % path)
        except TTLibError:
            raise DrawBotError("Font '%s' is not a valid font." % path)
        if psName is not None:
            psName = psName.toUnicode()

        if not success:
            warnings.warn("install font: %s" % error)
        return psName
Пример #25
0
def main(args=None):
    configLogger(logger=log)

    parser = argparse.ArgumentParser()
    parser.add_argument("input", nargs='+', metavar="INPUT")
    parser.add_argument("-o", "--output")
    parser.add_argument("-e", "--max-error", type=float, default=MAX_ERR)
    parser.add_argument("--post-format", type=float, default=POST_FORMAT)
    parser.add_argument(
        "--keep-direction", dest='reverse_direction', action='store_false')
    parser.add_argument("--face-index", type=int, default=0)
    parser.add_argument("--overwrite", action='store_true')
    options = parser.parse_args(args)

    if options.output and len(options.input) > 1:
        if not os.path.isdir(options.output):
            parser.error("-o/--output option must be a directory when "
                         "processing multiple fonts")

    for path in options.input:
        if options.output and not os.path.isdir(options.output):
            output = options.output
        else:
            output = makeOutputFileName(path, outputDir=options.output,
                                        extension='.ttf',
                                        overWrite=options.overwrite)

        font = TTFont(path, fontNumber=options.face_index)
        otf_to_ttf(font,
                   post_format=options.post_format,
                   max_err=options.max_error,
                   reverse_direction=options.reverse_direction)
        font.save(output)
Пример #26
0
def main(args=None):
    if args is None:
        args = sys.argv[1:]

    if len(args) < 2:
        print("usage: merge_woff_metadata.py METADATA.xml "
              "INPUT.woff [OUTPUT.woff]", file=sys.stderr)
        return 1

    metadata_file = args[0]
    with open(metadata_file, 'rb') as f:
        metadata = f.read()

    infile = args[1]
    if len(args) > 2:
        outfile = args[2]
    else:
        filename, ext = os.path.splitext(infile)
        outfile = makeOutputFileName(filename, None, ext)

    font = TTFont(infile)

    if font.flavor not in ("woff", "woff2"):
        print("Input file is not a WOFF or WOFF2 font", file=sys.stderr)
        return 1

    data = font.flavorData

    # this sets the new WOFF metadata
    data.metaData = metadata

    font.save(outfile)
Пример #27
0
def saveFont(glyphs, outputFileName, asXML, pixelSize, descent, fontName, copyrightYear, creator, version):
    f = TTFont()

    vectorizedGlyphs = {glyph : [vectorizeGlyph(glyphs[glyph][0], pixelSize, descent), glyphs[glyph][1]] for glyph in glyphs}
    unicodes = [code for glyph in glyphs for code in glyphs[glyph][1]]

    # Populate basic tables (there are a few dependencies so order matters)
    makeTable_glyf(f, vectorizedGlyphs)
    makeTable_maxp(f)
    makeTable_loca(f)
    makeTable_head(f)
    makeTable_hmtx(f)
    makeTable_hhea(f, pixelSize, descent)
    makeTable_OS2(f, pixelSize, descent, min(unicodes), max(unicodes))
    makeTable_cmap(f, glyphs)
    makeTable_name(f, fontName, "Regular", copyrightYear, creator, version)
    makeTable_post(f, pixelSize, descent)

    if asXML:
        # We have to compile the TTFont manually when saving as TTX
        # (to auto-calculate stuff here and there)
        f["glyf"].compile(f)
        f["maxp"].compile(f)
        f["loca"].compile(f)
        f["head"].compile(f)
        f["hmtx"].compile(f)
        f["hhea"].compile(f)
        f["OS/2"].compile(f)
        f["cmap"].compile(f)
        f["name"].compile(f)
        f["post"].compile(f)
        print "PLEASE NOTE: When exporting directly to XML, the checkSumAdjustment value in the head table will be 0."
        f.saveXML(outputFileName)
    else:
        f.save(outputFileName)
Пример #28
0
    def run(self):
        font = TTFont(self.in_font)
        gs = font.getGlyphSet()
        glyf = font["glyf"]
        hmtx = font["hmtx"]
        for gname, (adw, lsb) in hmtx.metrics.items():
            # https://github.com/fonttools/fonttools/blob/master/Lib/fontTools/ttLib/tables/_g_l_y_f.py#L189-L192
            # __getitem__ internally calls table__g_l_y_f.expand which is essential to obtain xMin
            g = glyf[gname]
            # obtain recalculated xMin from glyph's control bounds
            g.recalcBounds(glyf)
            hmtx.metrics[gname] = (adw, g.xMin)
        if self.update_vmtx:
            from fontTools.pens.boundsPen import BoundsPen
            vmtx = font["vmtx"]
            for gname, (adh, tsb) in vmtx.metrics.items():
                g = glyf[gname]
                # obtain yMax
                g.recalcBounds(glyf)
                pen = BoundsPen(gs)
                g.draw(pen, glyf)
                if pen.bounds is None:
                    continue
                left, bottom, right, top = pen.bounds
                vmtx.metrics[gname] = (adh, top + tsb - g.yMax)

        font.save(self.out_font)

        return 0
Пример #29
0
def build(args):
    font, features = merge(args)

    build_encoded(font, features)

    with tempfile.NamedTemporaryFile(mode="r", suffix=args.out_file) as tmp:
        font.generate(tmp.name, flags=["round", "opentype"])
        ttfont = TTFont(tmp.name)

    try:
        builder.addOpenTypeFeatures(ttfont, features)
    except:
        with tempfile.NamedTemporaryFile(mode="w+", delete=False) as tmp:
            tmp.write(features.asFea())
            print("Failed! Inspect temporary file: %r" % tmp.name)
        raise

    # Filter-out useless Macintosh names
    ttfont["name"].names = [n for n in ttfont["name"].names if n.platformID != 1]

    # https://github.com/fontforge/fontforge/pull/3235
    # fontDirectionHint is deprecated and must be set to 2
    ttfont["head"].fontDirectionHint = 2
    # unset bits 6..10
    ttfont["head"].flags &= ~0x7e0

    # Drop useless table with timestamp
    if "FFTM" in ttfont:
        del ttfont["FFTM"]

    ttfont.save(args.out_file)
Пример #30
0
def _verifyOutput(outPath):
    f = TTFont(outPath)
    f.saveXML(outPath + ".ttx")
    with open(outPath + ".ttx") as f:
        testData = strip_VariableItems(f.read())
    refData = strip_VariableItems(getTestData(os.path.basename(outPath) + ".ttx"))
    assert refData == testData
Пример #31
0
#!/usr/bin/env python
from itertools import chain
import sys

from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode

ttf = TTFont(sys.argv[1])

chars = []
for cmap in ttf['cmap'].tables:
    if cmap.isUnicode():
        for item in cmap.cmap:
            chars.append(chr(item))

chars_unique = []
for char in chars:
    if char not in chars_unique:
        chars_unique.append(char)

with open('glyphs.txt', 'w', encoding='utf8') as f:
    for char in chars_unique:
        f.write("%s " % char)

# Use this to check if font contains the codepoint given as second argument:
#char = int(sys.argv[2], 0)
#print(Unicode[char])

ttf.close()
Пример #32
0
#!/usr/bin/env python
import sys

from fontTools.ttLib import TTFont

ttf = TTFont(sys.argv[1])
cmap = ttf.getBestCmap()

chars = [format(y[0], 'x') for y in cmap.items()]
print(','.join(f'U+{char}' for char in chars))

ttf.close()
Пример #33
0
# -*- coding: utf-8 -*-


from fontTools.ttLib import TTFont     # 导包

font = TTFont('base_font.woff')   # 打开文件
gly_list = font.getGlyphOrder()     # 获取 GlyphOrder 字段的值
for gly in gly_list:    # 前两个值不是我们要的,切片去掉
    print(gly)

font.saveXML('./1.xml')

Пример #34
0
    def save_otfs(self,
                  ufos,
                  ttf=False,
                  is_instance=False,
                  interpolatable=False,
                  use_afdko=False,
                  autohint=None,
                  subset=None,
                  use_production_names=None,
                  subroutinize=False,
                  interpolate_layout_from=None,
                  kern_writer_class=None,
                  mark_writer_class=None):
        """Build OpenType binaries from UFOs.

        Args:
            ufos: Font objects to compile.
            ttf: If True, build fonts with TrueType outlines and .ttf extension.
            is_instance: If output fonts are instances, for generating paths.
            interpolatable: If output is interpolatable, for generating paths.
            use_afdko: If True, use AFDKO to compile feature source.
            autohint: Parameters to provide to ttfautohint. If not provided, the
                autohinting step is skipped.
            subset: Whether to subset the output according to data in the UFOs.
                If not provided, also determined by flags in the UFOs.
            use_production_names: Whether to use production glyph names in the
                output. If not provided, determined by flags in the UFOs.
            subroutinize: If True, subroutinize CFF outlines in output.
            interpolate_layout_from: A designspace path to give varLib for
                interpolating layout tables to use in output.
            kern_writer_class: Class overriding ufo2ft's KernFeatureWriter.
            mark_writer_class: Class overriding ufo2ft's MarkFeatureWriter.
        """

        ext = 'ttf' if ttf else 'otf'
        fea_compiler = FDKFeatureCompiler if use_afdko else FeatureCompiler

        if kern_writer_class is None:
            kern_writer_class = KernFeatureWriter
        else:
            logger.info("Using %r", kern_writer_class.__module__)

        if mark_writer_class is None:
            mark_writer_class = MarkFeatureWriter
        else:
            logger.info("Using %r", mark_writer_class.__module__)

        if interpolate_layout_from is not None:
            master_locations, instance_locations = self._designspace_locations(
                interpolate_layout_from)
            ufod = self._output_dir('ufo', False, interpolatable)
            otfd = self._output_dir(ext, False, interpolatable)
            finder = lambda s: s.replace(ufod, otfd).replace('.ufo', '.' + ext)

        for ufo in ufos:
            name = self._font_name(ufo)
            logger.info('Saving %s for %s' % (ext.upper(), name))

            otf_path = self._output_path(ufo, ext, is_instance, interpolatable)
            if use_production_names is None:
                use_production_names = not ufo.lib.get(
                    GLYPHS_PREFIX + "Don't use Production Names")
            compiler_options = dict(featureCompilerClass=fea_compiler,
                                    kernWriterClass=kern_writer_class,
                                    markWriterClass=mark_writer_class,
                                    glyphOrder=ufo.lib.get(PUBLIC_PREFIX +
                                                           'glyphOrder'),
                                    useProductionNames=use_production_names)
            if ttf:
                font = compileTTF(ufo, convertCubics=False, **compiler_options)
            else:
                font = compileOTF(ufo,
                                  optimizeCFF=subroutinize,
                                  **compiler_options)

            if interpolate_layout_from is not None:
                loc = instance_locations[ufo.path]
                gpos_src = interpolate_layout(interpolate_layout_from,
                                              loc,
                                              finder,
                                              mapped=True)
                font['GPOS'] = gpos_src['GPOS']
                gsub_src = TTFont(
                    finder(self._closest_location(master_locations, loc)))
                font['GDEF'] = gsub_src['GDEF']
                font['GSUB'] = gsub_src['GSUB']

            font.save(otf_path)

            if subset is None:
                export_key = GLYPHS_PREFIX + 'Glyphs.Export'
                subset = ((GLYPHS_PREFIX + 'Keep Glyphs') in ufo.lib or any(
                    glyph.lib.get(export_key, True) is False for glyph in ufo))
            if subset:
                self.subset_otf_from_ufo(otf_path, ufo)

            if ttf and autohint is not None:
                hinted_otf_path = self._output_path(ufo,
                                                    ext,
                                                    is_instance,
                                                    interpolatable,
                                                    autohinted=True)
                ttfautohint(otf_path, hinted_otf_path, args=autohint)
Пример #35
0
def _generate_ttx_dump(font_path, tables=None):
    font = TTFont(font_path)
    temp_path = _get_temp_file_path()
    font.saveXML(temp_path, tables=tables)
    return temp_path
Пример #36
0
def Main():
    input_file, output_file, demo_file = sys.argv[1:]
    font = TTFont(input_file, lazy=False)

    # Rename the font.
    for name in font["name"].names:
        new_name = (name.toUnicode().replace("Fira Code",
                                             "Fira Emacs").replace(
                                                 "FiraCode", "FiraEmacs"))
        font["name"].setName(new_name, name.nameID, name.platformID,
                             name.platEncID, name.langID)

    # Get the mapping from code points to glyph names.
    cmap = font.getBestCmap()

    # Reverse the mapping.
    inv_cmap = {name: chr(code) for code, name in cmap.items()}

    # Get the list of all glyph names.
    glyph_order = font.getGlyphOrder()

    # Convert the font to XML, because I can't figure out how to make the necessary
    # updates using the fontTools API.
    sio = io.StringIO()
    font.saveXML(sio)
    root = ET.fromstring(sio.getvalue())
    del sio

    # This list will be populated with tuples of (glyph name, code point, input string)
    # representing how strings are converted to ligatures and which code point the
    # ligature glyph is assigned to.
    output_glyphs = []

    # Look at every glyph, picking out the ones that look like ligatures.
    for glyph in glyph_order:
        input_string = None
        m = re.match(r"([^.]+)\.(.*)$", glyph)
        if m:
            # We assume the name of a ligature glyph is formed from the names of
            # the component glyphs separated by underscores.
            parts = m[1].split("_")
            if all(p in inv_cmap for p in parts):
                input_string = "".join(inv_cmap[p] for p in parts)
                output_glyphs.append(
                    Glyph(name=glyph, input_string=input_string, suffix=m[2]))

    output_glyphs.sort(key=lambda g: (g.input_string, g.suffix))

    # Start with 0xE100 instead of 0xE000 because for some reason the original
    # font already assigns some glyphs in the private-use area.
    code_point = 0xE100
    for g in output_glyphs:
        g.code_point = code_point
        code_point += 1

    # Echo the list of strings which can be pasted into fira-code.el to update it.
    fill_column = 70
    prefix = ""
    with open("fira-code-data.el", "wt") as data_stream:
        data_stream.write(
            ";; -*- coding: utf-8 -*-\n(defconst fira-code--data\n  '(")
        for g in output_glyphs:
            line = prefix + '[{} {} "\\{}"]  ;   {}'.format(
                ElispLiteral(g.name),
                ElispLiteral(g.input_string),
                hex(g.code_point)[1:],
                chr(g.code_point),
            )
            prefix = "    "
            data_stream.write(line + "\n")
        data_stream.write("))\n")

    # Add extra output_glyphs from code points to glyphs in the relevant cmap variants.
    for fmt in ["12", "4"]:
        for table in root.find("cmap").findall("cmap_format_" + fmt):
            for g in output_glyphs:
                ET.SubElement(table, "map",
                              dict(code=hex(g.code_point), name=g.name))

    # Serialize the XML data and convert it to a font file.
    bio = io.BytesIO()
    ET.ElementTree(root).write(bio)
    font = TTFont()
    bio.seek(0)
    font.importXML(bio)
    font.save(output_file)
Пример #37
0
from itertools import chain
# import numpy as np
import sys

from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode

ttf = TTFont('db_hindi_mast_4.ttf')

chars = chain.from_iterable([y + (Unicode[y[0]], ) for y in x.cmap.items()]
                            for x in ttf["cmap"].tables)
chars1 = (list(chars))
for i in range(len(chars1)):
    print(chars1[i])

# Use this for just checking if the font contains the codepoint given as
# second argument:
#char = int(sys.argv[2], 0)
#print(Unicode[char])
#print(char in (x[0] for x in chars))
# array = np.array(chars1)
# print(array)

ttf.close()
Пример #38
0
 def check_ttx_dump(self, font, expected_ttx, tables, suffix):
     """Ensure the TTX dump is the same after saving and reloading the font."""
     path = self.temp_path(suffix=suffix)
     font.save(path)
     self.expect_ttx(TTFont(path), expected_ttx, tables)
Пример #39
0
    '//div[@xname="content"]//div[@class="tz-paragraph"]//text()')
content_str = ''.join(content_list)
print(content_str)

# 获取字体的连接文件
fonts_ = re.search(r",url\('(//.*\.ttf)?'\) format", response_html).group(1)

# 请求字体文件, 字体文件是动态更新的
fonts_url = 'https:' + fonts_
response = requests.get(fonts_url, headers=headers).content
# 讲字体文件保存到本地
with open('fonts.ttf', 'wb') as f:
    f.write(response)

# 解析字体库
font = TTFont('fonts.ttf')

# 读取字体的映射关系
uni_list = font['cmap'].tables[0].ttFont.getGlyphOrder()

# 转换格式
utf_list = [eval(r"u'\u" + x[3:] + "'") for x in uni_list[1:]]

# 被替换的字体的列表
word_list = [
    u'一', u'七', u'三', u'上', u'下', u'不', u'九', u'了', u'二', u'五', u'低', u'八',
    u'六', u'十', u'的', u'着', u'近', u'远', u'长', u'右', u'呢', u'和', u'四', u'地',
    u'坏', u'多', u'大', u'好', u'小', u'少', u'短', u'矮', u'高', u'左', u'很', u'得',
    u'是', u'更'
]
#遍历需要被替换的字符
Пример #40
0
def make_dict():
    font = TTFont('58.ttf')
    b = font['cmap'].tables[2].ttFont.getReverseGlyphMap()  # 编码对应的数字
    c = font['cmap'].tables[2].ttFont.tables['cmap'].tables[
        1].cmap  # 页面的十六进制数对应的编码
    return b, c
Пример #41
0
async def fontTest(letter):
    test = TTFont("resources/Roboto-Medium.ttf")
    for table in test['cmap'].tables:
        if ord(letter) in table.cmap.keys():
            return True
Пример #42
0
def var_font():
    """VF family consisting of a single font with two axes, wdth, wght"""
    return TTFont(os.path.join(TEST_DATA, "Inconsolata[wdth,wght].ttf"))
def get_font():
    #这是标签为shopNum和address的字体文件
    font = TTFont('51fbf957.woff')
    #获取所有字体的上面对应的编码
    font_names = font.getGlyphOrder()
    #下面这些就是打开FontCreator软件后解析 人均多少 那个字体文件得到 所有文字和数字,共603个:
    texts = ['','','1','2','3','4','5','6','7','8',
    '9','0','店','中','美','家','馆','小','车','大',
    '市','公','酒','行','国','品','发','电','金','心',
    '业','商','司','超','生','装','园','场','食','有',
    '新','限','天','面','工','服','海','华','水','房',
    '饰','城','乐','汽','香','部','利','子','老','艺',
    '花','专','东','肉','菜','学','福','饭','人','百',
    '餐','茶','务','通','味','所','山','区','门','药',
    '银','农','龙','停','尚','安','广','鑫','一','容',
    '动','南','具','源','兴','鲜','记','时','机','烤',
    '文','康','信','果','阳','理','锅','宝','达','地',
    '儿','衣','特','产','西','批','坊','州','牛','佳',
    '化','五','米','修','爱','北','养','卖','建','材',
    '三','会','鸡','室','红','站','德','王','光','名',
    '丽','油','院','堂','烧','江','社','合','星','货',
    '型','村','自','科','快','便','日','民','营','和',
    '活','童','明','器','烟','育','宾','精','屋','经',
    '居','庄','石','顺','林','尔','县','手','厅','销',
    '用','好','客','火','雅','盛','体','旅','之','鞋',
    '辣','作','粉','包','楼','校','鱼','平','彩','上',
    '吧','保','永','万','物','教','吃','设','医','正',
    '造','丰','健','点','汤','网','庆','技','斯','洗',
    '料','配','汇','木','缘','加','麻','联','卫','川',
    '泰','色','世','方','寓','风','幼','羊','烫','来',
    '高','厂','兰','阿','贝','皮','全','女','拉','成',
    '云','维','贸','道','术','运','都','口','博','河',
    '瑞','宏','京','际','路','祥','青','镇','厨','培',
    '力','惠','连','马','鸿','钢','训','影','甲','助',
    '窗','布','富','牌','头','四','多','妆','吉','苑',
    '沙','恒','隆','春','干','饼','氏','里','二','管',
    '诚','制','售','嘉','长','轩','杂','副','清','计',
    '黄','讯','太','鸭','号','街','交','与','叉','附',
    '近','层','旁','对','巷','栋','环','省','桥','湖',
    '段','乡','厦','府','铺','内','侧','元','购','前',
    '幢','滨','处','向','座','下','県','凤','港','开',
    '关','景','泉','塘','放','昌','线','湾','政','步',
    '宁','解','白','田','町','溪','十','八','古','双',
    '胜','本','单','同','九','迎','第','台','玉','锦',
    '底','后','七','斜','期','武','岭','松','角','纪',
    '朝','峰','六','振','珠','局','岗','洲','横','边',
    '济','井','办','汉','代','临','弄','团','外','塔',
    '杨','铁','浦','字','年','岛','陵','原','梅','进',
    '荣','友','虹','央','桂','沿','事','津','凯','莲',
    '丁','秀','柳','集','紫','旗','张','谷','的','是',
    '不','了','很','还','个','也','这','我','就','在',
    '以','可','到','错','没','去','过','感','次','要',
    '比','觉','看','得','说','常','真','们','但','最',
    '喜','哈','么','别','位','能','较','境','非','为',
    '欢','然','他','挺','着','价','那','意','种','想',
    '出','员','两','推','做','排','实','分','间','甜',
    '度','起','满','给','热','完','格','荐','喝','等',
    '其','再','几','只','现','朋','候','样','直','而',
    '买','于','般','豆','量','选','奶','打','每','评',
    '少','算','又','因','情','找','些','份','置','适',
    '什','蛋','师','气','你','姐','棒','试','总','定',
    '啊','足','级','整','带','虾','如','态','且','尝',
    '主','话','强','当','更','板','知','己','无','酸',
    '让','入','啦','式','笑','赞','片','酱','差','像',
    '提','队','走','嫩','才','刚','午','接','重','串',
    '回','晚','微','周','值','费','性','桌','拍','跟',
    '块','调','糕'
    ]
    #然后我们创建一个font_name的字典,用来装字体编码和 --> 所对应的数字、汉字
    font_name = {}
    for index, value in enumerate(texts):
        #这就是大众点评乱码的结构:由前面的三个字符‘&#x’+后面是小写字母和数字+分号‘;’
        n = font_names[index].replace('uni', '&#x').lower() + ';'
        #下面生成的这个就是乱码,他就对应刚刚字体文件的对应数字,可以运行看看
        font_name[n] = value
        
    #此刻得到的就是解码的字典
    return font_name
Пример #44
0
class Font:
    """
    Storage of font information while composing the pages.

    >>> from pagebot.fonttoolbox.objects.font import findFont
    >>> f = findFont('RobotoDelta-VF')
    >>> sorted(f.axes.keys())
    ['GRAD', 'POPS', 'PWDT', 'PWGT', 'UDLN', 'XOPQ', 'XTRA', 'YOPQ', 'YTAD', 'YTAS', 'YTDD', 'YTDE', 'YTLC', 'YTRA', 'YTUC', 'opsz', 'wdth', 'wght']
    >>> f.info.familyName
    'RobotoDelta'
    >>> len(f)
    188
    >>> f.axes['opsz']
    (8.0, 12.0, 144.0)
    >>> variables = f.variables
    >>> features = f.features
    >>> f.groups
    >>> f.designSpace
    {}
    """
    GLYPH_CLASS = Glyph
    FONTANALYZER_CLASS = FontAnalyzer

    def __init__(self,
                 path=None,
                 ttFont=None,
                 name=None,
                 opticalSize=None,
                 location=None,
                 styleName=None,
                 lazy=True):
        """Initialize the TTFont, for which Font is a wrapper.
        self.name is supported, in case the caller wants to use a different

        >>> f = Font()
        >>> f
        <Font Untitled>
        """
        if path is None and ttFont is None:
            self.ttFont = TTFont()
            self.path = '%d' % id(
                ttFont)  # In case no path, use unique id instead.
        elif ttFont is None and path is not None:
            self.ttFont = TTFont(path, lazy=lazy)
            self.path = path  # File path of the existing font file.
        elif path is None:
            self.ttFont = ttFont
            self.path = '%d' % id(
                ttFont)  # In case no path, use unique id instead.
        else:  # ttFont is not None: There is ttFont data
            self.ttFont = ttFont
            self.path = path

        # Store location, incase this was a created VF instance
        self.location = location
        # TTFont is available as lazy style.info.font
        self.info = FontInfo(self.ttFont)
        self.info.opticalSize = opticalSize  # Optional optical size, to indicate where this Variable Font is rendered for.
        self.info.location = location  # Store origina location of this instance of the font is derived from a Variable Font.
        # Stores optional custom name, otherwise use original DrawBot name.
        # Otherwise use from FontInfo.fullName
        self.name = name or self.info.fullName
        if styleName is not None:
            self.info.styleName = styleName  # Overwrite default style name in the ttFont or Variable Font location
        self._kerning = None  # Lazy reading.
        self._groups = None  # Lazy reading.
        self._glyphs = {}  # Lazy creation of self[glyphName]
        self._analyzer = None  # Lazy creation.
        self._variables = None  # Lazy creations of delta's dictionary per glyph per axis

    def __repr__(self):
        """
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Black.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> str(font)
        '<Font Roboto-Black>'
        """
        return '<Font %s>' % (path2FontName(self.path) or self.name
                              or 'Untitled')

    def __getitem__(self, glyphName):
        """Answer the glyph with glyphName.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Black.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> g = font['A']
        >>> g.name, g.width
        ('A', 1395)
        """
        if not glyphName in self._glyphs:
            self._glyphs[glyphName] = self.GLYPH_CLASS(self, glyphName)
        return self._glyphs[glyphName]

    def __len__(self):
        """Answer the number of glyphs in the font.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Black.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> len(font)
        3387
        """
        if 'glyf' in self.ttFont:
            return len(self.ttFont['glyf'])
        return 0

    def nameMatch(self, pattern):
        """Answer level of matching between pattern and the font file name or font.info.fullName.
        Pattern can be a single string or a list of string.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Black.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> font.nameMatch('Black')
        1.0
        >>> font.nameMatch('Blackish')
        0
        >>> font.nameMatch(('Roboto', 'Black'))
        1.0
        """
        fontName = path2FontName(self.path)
        if not isinstance(pattern, (list, tuple)):
            pattern = [pattern]
        for part in pattern:
            if not (part in fontName or part in self.info.fullName):
                return 0
        return 1.0

    def weightMatch(self, weight):
        """Answer level of matching for the (abbreviated) weight name or number with font, in a value between 0 and 1.
        Currently there is only no-match (0) and full-match (1). Future implementations may give a float indicator
        for the level of matching, so the caller can decide on the level of threshold.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Black.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> font.info.weightClass
        900
        >>> font.weightMatch(0) # Bad match
        0
        >>> font.weightMatch(800) # Bad match
        0
        >>> font.weightMatch(900) # Exact match
        0
        >>> font.weightMatch(0) # Bad match -
        0
        >>> font.weightMatch('Black') # Black --> Exact match on 900
        1.0
        >>> font.weightMatch('Light') # Light --> No match on 900
        0
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Regular.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> font.info.weightClass
        400
        """
        """
        TODO: Fix these tests

        >>> font.weightMatch(400) # Match
        1.0
        >>> font.weightMatch('Regular') # Match
        1.0
        >>> font.weightMatch('Condensed') # Matching with width name has no match.
        0
        """
        if isinstance(weight, (float, int)):  # Comparing by numbers
            # Compare the weight as number as max difference to what we already have.
            w = self.info.weightClass
            if w in FONT_WEIGHT_MATCHES.get(weight, []):
                return 1.0  # Exact match
        else:  # Comparing by string
            fileName = path2FontName(self.path)
            for w in FONT_WEIGHT_MATCHES.get(weight, []):
                if not isinstance(w,
                                  (float, int)) and (w in fileName or w
                                                     in self.info.styleName):
                    return 1.0  # Exacly match
        return 0  # No match

    def widthMatch(self, width):
        """Answer level of matchting for the (abbreviated) width name or number with font.
        Currently there is only no-match (0) and full-match (1). Future implementations may give a float indicator
        for the level of matching, so the caller can decide on the level of threshold.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Black.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> font.info.widthClass
        5
        >>> font.widthMatch(0) # Bad match
        0
        >>> font.widthMatch(4) # Close match fails
        0
        >>> font.widthMatch(5) # Exact match
        1.0
        >>> font.widthMatch(6) # Close match fails
        0
        >>> font.widthMatch(10) # Bad match
        0
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Bold.ttf' # We know this exists in the PageBot repository
        >>> font = Font(path)
        >>> font.info.widthClass
        5
        >>> font.widthMatch(5) # Wrong exact match --> 1000 due to wrong font.info.widthClass
        1.0
        >>> font.widthMatch('Wide') # No match on "Wide"
        0
        >>> #font.widthMatch('Cond') # Exact match on "Cond"
        1.0
        """
        if isinstance(width, (float, int)):
            # Compare the width as number as max difference to what we already have.
            w = self.info.widthClass
            if w <= 100:  # Normalize to 1000
                w *= 100
            if w in FONT_WIDTH_MATCHES.get(width, []):
                return 1.0
        else:  # Comparing by string
            fileName = path2FontName(self.path)
            for w in FONT_WIDTH_MATCHES.get(width, []):
                if not isinstance(w,
                                  (float, int)) and (w in fileName or w
                                                     in self.info.styleName):
                    return 1.0
        return 0

    def isItalic(self):
        """Answer the boolean flag if this font should be considered to be italic.
        Currently there is only no-match (0) and full-match (1). Future implementations may give a float indicator
        for the level of matching, so the caller can decide on the level of threshold.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-BlackItalic.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> font.isItalic()
        1
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Bold.ttf' # We know this exists in the PageBot repository
        >>> font = Font(path)
        >>> font.isItalic()
        0
        """
        if self.info.italicAngle:
            return 1
        for altName in FONT_ITALIC_MATCHES.keys():
            if altName in path2FontName(
                    self.path) or altName in self.info.styleName:
                return 1.0
        return 0

    def match(self, name=None, weight=None, width=None, italic=None):
        """Answer a value between 0 and 1 to the amount that self matches the defined parameters.
        Only defined values count in the matching.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> path = getTestFontsPath() + '/google/roboto/Roboto-Black.ttf' # We know this exists in the PageBot repository
        >>> font = getFont(path)
        >>> font.info.widthClass, font.info.weightClass
        (5, 900)
        >>> font.match(name='Roboto')
        1.0
        >>> font.match(name='Robo', weight='Black')
        1.0
        >>> font.match(name='Robo', weight='Light') # Only match on the name
        0.5
        >>> font.match(name='Robo', width=5)
        1.0
        """
        """
        TODO: Fix these tests

        >>> font.match(name='Robo', weight=900, width=5)
        1.0
        >>> font.match(name='Robo', weight=900, width=5, italic=True)
        0.75
        >>> font.match(name='Robo', weight='Black', width=5, italic=False)
        1.0
        >>> font.match(name='Robo', weight='Blackish', width=5, italic=False)
        0.75
        """
        matches = []
        fontName = path2FontName(self.path)
        if name is not None:
            matches.append(self.nameMatch(name))
        # Currently the matches only answer 0 or 1. In future implementations this value may vary
        # as float between 0 and 1.
        if weight is not None:
            matches.append(self.weightMatch(weight))
        if width is not None:
            matches.append(self.widthMatch(width))
        if italic is not None:
            matches.append(italic == self.isItalic())
        if not matches:
            return 0  # Avoif division by zero
        return sum(matches) / len(matches)  # Normalize to value between 0..1

    def keys(self):
        """Answer the glyph names of the font.

        >>> from pagebot.fonttoolbox.objects.font import Font
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/fontbureau/Amstelvar-Roman-VF.ttf'
        >>> f = getFont(path, lazy=False)
        >>> 'A' in f.keys()
        True
        """
        if 'glyf' in self.ttFont:
            return self.ttFont['glyf'].keys()
        return []

    def _get_cmap(self):
        """Answer the dictionary of sorted {unicode: glyphName, ...} in the font.

        >>> from pagebot.fonttoolbox.objects.font import Font
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/fontbureau/Amstelvar-Roman-VF.ttf'
        >>> f = getFont(path, lazy=False)
        >>> f.cmap[65]
        'A'
        """
        if 'cmap' in self.ttFont:
            return self.ttFont['cmap'].getBestCmap()
        return {}

    cmap = property(_get_cmap)

    def __contains__(self, glyphName):
        """Allow direct testing.

        >>> from pagebot.toolbox.transformer import *
        >>> from pagebot.fonttoolbox.objects.font import Font
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/fontbureau/Amstelvar-Roman-VF.ttf'
        >>> f = getFont(path, lazy=False)
        >>> 'A' in f
        True
        """
        return glyphName in self.keys()

    def _get_analyzer(self):
        """Answer the style/font analyzer if it exists. Otherwise create one.

        >>> from pagebot.toolbox.transformer import *
        >>> from pagebot.fonttoolbox.objects.font import Font
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/fontbureau/Amstelvar-Roman-VF.ttf'
        >>> f = getFont(path, lazy=False)
        >>> #f.analyzer.stems # TODO: Needs bezier path for pixel test.

        """
        if self._analyzer is None:
            self._analyzer = self.FONTANALYZER_CLASS(self)
        return self._analyzer

    analyzer = property(_get_analyzer)

    def _get_axes(self):
        """Answer dictionary of axes if self.ttFont is a Variable Font. Otherwise answer an empty dictioary.

        >>> from pagebot.toolbox.transformer import *
        >>> from pagebot.fonttoolbox.objects.font import findFont
        >>> f = findFont('Amstelvar-Roman-VF')
        >>> f.axes['opsz']
        (0.0, 0.0, 1.0)
         """
        try:
            # TODO: Change value to Axis dictionary instead of list
            axes = {
                a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
                for a in self.ttFont['fvar'].axes
            }
        except KeyError:
            axes = {}  # This is not a variable font.
        return axes

    axes = property(_get_axes)

    def getDefaultVarLocation(self):
        """Answer the location dictionary with the default axes values.

        >>> from pagebot.toolbox.transformer import *
        >>> from pagebot.fonttoolbox.objects.font import findFont
        >>> font = findFont('Amstelvar-Roman-VF')
        >>> len(font.getDefaultVarLocation().keys())
        1
        """
        defaultVarLocation = {}
        for axisName, axis in self.axes.items():
            defaultVarLocation[axisName] = axis[1]
        return defaultVarLocation

    def _get_rawDeltas(self):
        """Answer the list of axis dictionaries with deltas for all glyphs and axes. Answer an empty dictionary
        if the [gvar] table does not exist.

        >>> from pagebot.fonttoolbox.objects.font import Font
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/fontbureau/Amstelvar-Roman-VF.ttf'
        >>> font = Font(path)
        >>> len(font.rawDeltas['A'])
        0
        """
        try:
            return self.ttFont['gvar'].variations
        except:
            return {}

    rawDeltas = property(_get_rawDeltas)

    def _get_designSpace(self):
        """Answer the design space in case this is a variable font.

        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/fontbureau/Amstelvar-Roman-VF.ttf'
        >>> font = Font(path)
        >>> font.designSpace # Basically the "cvar" table.
        {}
        """
        try:
            designSpace = self.ttFont['cvar']
        except KeyError:
            designSpace = {}
        return designSpace

    designSpace = property(_get_designSpace)

    def _get_variables(self):
        """Answer the gvar-table (if it exists) translated into plain Python dictionaries
        of deltas per glyph and per axis if this is a Var-fonts. Otherwise answer an empty dictionary


        """
        """
        TODO We need a "stable" var-font to test on.

        >>> from pagebot.fonttoolbox.objects.font import findFont
        >>> font = findFont('Amstelvar-Roman-VF')
        >>> len(font.variables)
        592
        >>> variables = font.variables['H']
        >>> sorted(variables.keys())
        []
        >>> #['GRAD', 'XOPQ', 'XTRA', 'YOPQ', 'YTRA', 'YTSE', 'YTUC', 'opsz', 'wdth', 'wght']
        >>> axis, deltas = variables['GRAD']
        >>> axis
        {'GRAD': (0.0, 1.0, 1.0)}
        >>> deltas[:6]
        [(0, 0), None, (52, 0), None, None, (89, 0)]
        >>> font.variables.get('wrongglyphName') is None
        True
        """
        if self._variables is None:
            try:
                gvar = self.ttFont[
                    'gvar']  # Get the raw fonttools gvar table if it exists.
                self._variables = {}
                for glyphName, tupleVariations in gvar.variations.items():
                    self._variables[glyphName] = axisDeltas = {}
                    for tupleVariation in tupleVariations:
                        axisKey = '_'.join(
                            tupleVariation.axes.keys()
                        )  #{'GRAD': (0.0, 1.0, 1.0)} Make unique key, in case multiple
                        axisDeltas[
                            axisKey] = tupleVariation.axes, tupleVariation.coordinates  # ({'GRAD': (0.0, 1.0, 1.0)}, [(0, 0), None, (52, 0), None, None, (89, 0), ...])
            except KeyError:
                pass  # No gvar table, just answer the current self._variables as None.
        return self._variables

    variables = property(_get_variables)

    def getInstance(self,
                    location=None,
                    dstPath=None,
                    opticalSize=None,
                    styleName=None,
                    cached=True,
                    lazy=True,
                    kerning=None):
        """Answer the instance of self at location. If the cache file already exists, then
        just answer a Font instance to that font file.

        >>> from pagebot.fonttoolbox.objects.font import findFont
        >>> f = findFont('RobotoDelta-VF')
        >>> sorted(f.axes.keys())
        ['GRAD', 'POPS', 'PWDT', 'PWGT', 'UDLN', 'XOPQ', 'XTRA', 'YOPQ', 'YTAD', 'YTAS', 'YTDD', 'YTDE', 'YTLC', 'YTRA', 'YTUC', 'opsz', 'wdth', 'wght']
        >>> f.name
        'RobotoDelta Regular'
        >>> len(f)
        188
        >>> f.axes['wght']
        (100.0, 400.0, 900.0)
        >>> g = f['H']
        >>> g
        <PageBot Glyph H Pts:12/Cnt:1/Cmp:0>
        >>> g.points[6], g.width
        (APoint(1288,1456,On), 1458)
        >>> instance = f.getInstance(location=dict(wght=500))
        >>> instance
        <Font RobotoDelta-VF-wght500>
        >>> ig = instance['H']
        >>> ig
        <PageBot Glyph H Pts:12/Cnt:1/Cmp:0>
        >>> ig.points[6], ig.width
        (APoint(1307,1456,On), 1477)
        """
        if location is None:
            location = self.getDefaultVarLocation()
        return getInstance(self.path,
                           location=location,
                           dstPath=dstPath,
                           opticalSize=opticalSize,
                           styleName=styleName,
                           cached=cached,
                           lazy=lazy,
                           kerning=kerning)

    def _get_features(self):
        # TODO: Use TTFont for this instead.
        #return context.listOpenTypeFeatures(self.path)
        return {}

    features = property(_get_features)

    def _get_kerning(self):
        """Answer the (expanded) kerning table of the font.

        >>> from pagebot.toolbox.transformer import *
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/djr/bungee/Bungee-Regular.ttf'
        >>> f = getFont(path, lazy=False)
        >>> len(f.kerning)
        22827
        >>> f.kerning[('V','a')]
        -10
        """
        if self._kerning is None:  # Lazy read.
            self._kerning = OTFKernReader(self.path).kerningPairs
        return self._kerning

    kerning = property(_get_kerning)

    def _get_groups(self):
        """Answer the groups dictionary of the font.

        >>> from pagebot.toolbox.transformer import *
        >>> from pagebot.fonttoolbox.objects.font import Font
        >>> from pagebot.fonttoolbox.fontpaths import getTestFontsPath
        >>> fontPath = getTestFontsPath()
        >>> path = fontPath + '/djr/bungee/Bungee-Regular.ttf'
        >>> f = getFont(path, lazy=False)
        >>> g = f['A']
        >>> f.groups is None
        True
        """
        return self._groups

    groups = property(_get_groups)

    def save(self, path=None):
        """Save the font to optional path or to self.path."""
        self.ttFont.save(path or self.path)
Пример #45
0
def get_font(url):
    response = requests.get(url)
    font = TTFont(BytesIO(response.content))
    cmap = font.getBestCmap()
    font.close()
    return cmap
Пример #46
0
from fontTools.ttLib import TTFont


zt1 = TTFont("zt01.woff")

# wods列表中网页上按顺序打出来
words = ['B', '男', '王', '大', '专', 'M', '女', '吴', '硕', '赵', '黄', '李', '1', '8', '经', '2', '下', '本', '届', '5', '应', '科', '7', '中', '生', '6', 'E', '陈', '3', '以', '杨', 'A', '张', '4', '无', '0', '9', '验', '博', '技', '士', '校', '高', '刘', '周']

uni_list = zt1.getGlyphNames()[1:-1]

data_map = dict()
for index, i in enumerate(uni_list):
    temp = zt1["glyf"][i].coordinates
    x1, y1 = temp[0]
    x2, y2 = temp[1]
    new = (x2-x1, y2-y1)
    data_map[new] = words[index]
print(data_map)
Пример #47
0
def main(args=None):

    if args is None:
        import sys
        args = sys.argv[1:]

    varfilename = args[0]
    locargs = args[1:]
    outfile = os.path.splitext(varfilename)[0] + '-instance.ttf'

    loc = {}
    for arg in locargs:
        tag, val = arg.split('=')
        assert len(tag) <= 4
        loc[tag.ljust(4)] = float(val)
    print("Location:", loc)

    print("Loading variable font")
    varfont = TTFont(varfilename)

    fvar = varfont['fvar']
    axes = {
        a.axisTag: (a.minValue, a.defaultValue, a.maxValue)
        for a in fvar.axes
    }
    # TODO Apply avar
    # TODO Round to F2Dot14?
    loc = normalizeLocation(loc, axes)
    # Location is normalized now
    print("Normalized location:", loc)

    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, ot=True)
            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)

    # Interpolate cvt

    if 'cvar' in varfont:
        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] += int(round(delta))

    print("Removing variable tables")
    for tag in ('avar', 'cvar', 'fvar', 'gvar', 'HVAR', 'MVAR', 'VVAR',
                'STAT'):
        if tag in varfont:
            del varfont[tag]

    print("Saving instance font", outfile)
    varfont.save(outfile)
Пример #48
0
class DouYin(object):

    ttfond = TTFont(app_path() + "/data/iconfont_9eb9a50.woff")
    ttfond.saveXML(app_path() + "/data/iconfont_9eb9a50.xml")

    def __int__(self):
        pass

    def get_cmap_dict(self):
        """
        :return: 关系映射表
        """
        # 从本地读取关系映射表【从网站下载的woff字体文件】
        best_cmap = self.ttfond["cmap"].getBestCmap()
        # 循环关系映射表将数字替换成16进制
        best_cmap_dict = {}
        for key, value in best_cmap.items():
            best_cmap_dict[hex(key)] = value
        return best_cmap_dict  # 'num_1', '0xe604': 'num_2', '0xe605': 'num_3'

    def get_num_cmap(self):
        """
        :return: 返回num和真正的数字映射关系
        """
        num_map = {
            "x": "",
            "num_": 1,
            "num_1": 0,
            "num_2": 3,
            "num_3": 2,
            "num_4": 4,
            "num_5": 5,
            "num_6": 6,
            "num_7": 9,
            "num_8": 7,
            "num_9": 8,
        }
        return num_map

    def map_cmap_num(self, get_cmap_dict, get_num_cmap):
        new_cmap = {}
        for key, value in get_cmap_dict().items():
            key = re.sub("0", "&#", key, count=1) + ";"  # 源代码中的格式 &#xe606;
            new_cmap[key] = get_num_cmap()[value]
            # 替换后的格式
            # '&#xe602;': 1, '&#xe603;': 0, '&#xe604;': 3, '&#xe605;': 2,
        return new_cmap

    # 获取网页源码
    def get_html(self, url):
        ip = ProxyGetter(project_name='douyin').getter_server_proxy()
        proxy = {'https': 'https://' + ip}
        headers = {
            'User-Agent':
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
        }
        response = requests.get(url, headers=headers, proxies=proxy).text
        return response

    def replace_num_and_cmap(self, result, response):
        """
        将网页源代码中的&#xe603;替换成数字
        :param result:
        :param response:
        :return:
        """
        for key, value in result.items():
            if key in response:
                # print(777)
                response = re.sub(key, str(value), response)
        return response

    def manage(self, response):
        res = etree.HTML(response)
        douyin_name = res.xpath('//p[@class="nickname"]//text()')[0]
        douyin_id = 'ID:' + ''.join(
            res.xpath('//p[@class="shortid"]/i//text()')).replace(' ', '')
        guanzhu_num = ''.join(
            res.xpath('//span[@class="focus block"]//text()')).replace(
                ' ', '')
        fensi_num = ''.join(
            res.xpath('//span[@class="follower block"]//text()')).replace(
                ' ', '')
        dianzan = ''.join(
            res.xpath('//span[@class="liked-num block"]//text()')).replace(
                ' ', '')
        print(douyin_name, douyin_id, guanzhu_num, fensi_num, dianzan)
        line = douyin_name + " " + douyin_id + " " + guanzhu_num + ' ' + fensi_num + ' ' + dianzan
        with open(app_path() + '/data/author.txt', 'a+',
                  encoding='utf-8') as f:
            f.write(line + '\n')

    def read_author_id(self):
        with open(app_path() + '/data/body.txt', 'r') as f:
            lines = f.readlines()
            return lines

    def main(self):
        lines = self.read_author_id()
        for line in lines:
            line = re.sub('\'', '"', line)
            data = json.loads(line)
            author_id = data['author_id']
            url = 'https://www.iesdouyin.com/share/user/' + str(author_id)
            response = self.get_html(url)
            new_cmap = self.map_cmap_num(self.get_cmap_dict, self.get_num_cmap)
            res = self.replace_num_and_cmap(new_cmap, response)
            self.manage(res)
Пример #49
0
def openOpenTypeFile(path, outFilePath):
    # If input font is  CFF or PS, build a dummy ttFont in memory..
    # return ttFont, and flag if is a real OTF font Return flag is 0 if OTF, 1 if CFF, and 2 if PS/
    fontType = 0  # OTF
    tempPathCFF = path + kTempCFFSuffix
    try:
        ff = file(path, "rb")
        data = ff.read(10)
        ff.close()
    except (IOError, OSError):
        logMsg("Failed to open and read font file %s." % path)

    if data[:4] == "OTTO":  # it is an OTF font, can process file directly
        try:
            ttFont = TTFont(path)
        except (IOError, OSError):
            raise focusFontError(
                "Error opening or reading from font file <%s>." % path)
        except TTLibError:
            raise focusFontError("Error parsing font file <%s>." % path)

        try:
            cffTable = ttFont["CFF "]
        except KeyError:
            raise focusFontError("Error: font is not a CFF font <%s>." %
                                 fontFileName)

    else:

        # It is not an OTF file.
        if (data[0] == '\1') and (data[1] == '\0'):  # CFF file
            fontType = 1
            tempPathCFF = path
        elif not "%" in data:
            #not a PS file either
            logMsg("Font file must be a PS, CFF or OTF  fontfile: %s." % path)
            raise focusFontError("Font file must be PS, CFF or OTF file: %s." %
                                 path)

        else:  # It is a PS file. Convert to CFF.
            fontType = 2
            print "Converting Type1 font to temp CFF font file..."
            command = "tx  -cff +b -std \"%s\" \"%s\" 2>&1" % (path,
                                                               tempPathCFF)
            report = FDKUtils.runShellCmd(command)
            if "fatal" in report:
                logMsg(
                    "Attempted to convert font %s  from PS to a temporary CFF data file."
                    % path)
                logMsg(report)
                raise focusFontError(
                    "Failed to convert PS font %s to a temp CFF font." % path)

        # now package the CFF font as an OTF font.
        ff = file(tempPathCFF, "rb")
        data = ff.read()
        ff.close()
        try:
            ttFont = TTFont()
            cffModule = getTableModule('CFF ')
            cffTable = cffModule.table_C_F_F_('CFF ')
            ttFont['CFF '] = cffTable
            cffTable.decompile(data, ttFont)
        except:
            logMsg("\t%s" % (traceback.format_exception_only(
                sys.exc_type, sys.exc_value)[-1]))
            logMsg("Attempted to read font %s  as CFF." % path)
            raise focusFontError("Error parsing font file <%s>." % path)

    fontData = CFFFontData(ttFont, path, outFilePath, fontType, logMsg)
    return fontData
Пример #50
0
    def parse(self, response):
        # print(response.url)
        # ---------------------------------先下载字体文件用于破解数字----------------------------------------------
        font_url = re.findall(r'((https|http|ftp|rtsp|mms)?://[^\s]+ttf)',
                              response.css('.update style::text').extract_first())[0][0]
        font_name = response.css('.all-img-list li .update span::attr(class)').extract_first() + '.ttf'
        font_path = os.path.join(FONT_STORE, font_name)
        if os.path.exists('font_path') is False:
            download = requests.get(font_url)
            print('开始下载字体文件:' + font_name)
            with open(font_path , "wb") as file:
                file.write(download.content)
        # ---------------------------------------------------------------------------------------------------------
        # ---------------------------------将字库转换为XML后获取对应数字-------------------------------------------
        xml_path = font_path.replace('.ttf','.xml')
        font = TTFont(font_path)    # 打开文件
        font.saveXML(xml_path)     # 转换成 xml 文件并保存

        root = et.parse(xml_path).getroot()
        # 找到map那一堆标签(PyQuery)
        map_ele = root.find('cmap').find('cmap_format_12').findall('map')
        map_dict = {}
        # 把map那一堆数据存到字典中
        for map in map_ele:
            # print(help(m))
            code = map.attrib['code'].replace('0x', '')
            map_dict[code] = self.number_dict[map.attrib['name']]  # code是键, name是值
        print(map_dict)
        # ---------------------------------------------------------------------------------------------------------
        for fiction in response.css('.all-img-list li'):
            # 创建对象
            item = FictionItem()
            words_temp = ''
            # 获取小说名称
            item['title'] = fiction.css('a[data-eid=qd_B58]::text').extract_first()
            # 获取作者
            item['author'] = fiction.css('a[data-eid=qd_B59]::text').extract_first()
            # 获取小说类型
            item['type'] = fiction.css('a[data-eid=qd_B60]::text').extract_first()
            # 获取小说状态
            item['status'] = fiction.css('.author span::text').extract_first()

            # 通过查表的方式获取小说中字数(表来源于字体中提取)
            for num in str(fiction.css('.update span::text').extract_first().encode("unicode-escape")).strip(
                    '\'').strip('b\'').split(r'\\'):
                #确认有内容才取值
                if num is not "":
                    words_temp += map_dict.get(num[4:])
            item['words'] = words_temp + '万字'

            #获得小说评分(需要通过ajax获取)
            fiction_id = fiction.css('a::attr(data-bid)').extract_first()
            url = 'https://book.qidian.com/ajax/comment/index?_csrfToken=1&bookId=%s&pageSize=15' % fiction_id
            request = urllib.request.Request(url=url)
            response = urllib.request.urlopen(request)
            #将字符串转换为dict类型
            dict_data = eval(str(response.read(), encoding='utf-8'))
            # print(dict_data['data']['rate'])
            item['score'] = dict_data['data']['rate']

            # 获取小说链接
            item['fiction_urls'] = 'http:' + fiction.css('.book-mid-info a::attr(href)').extract_first()
            # 获取图片链接
            item['image_urls'] = 'http:' + fiction.css('a[data-eid=qd_B57] img::attr(src)').extract_first()

            yield item
Пример #51
0
def mada_ttFonts():
    return [TTFont(path) for path in mada_fonts]
Пример #52
0
async def fontTest(letter):
    test = TTFont("./temp/Roboto-Medium.ttf")
    for table in test["cmap"].tables:
        if ord(letter) in table.cmap.keys():
            return True
Пример #53
0
parser.add_argument('--output', metavar="PNG", help='where to write the PNG file')
args = parser.parse_args()

# Constants
WIDTH, HEIGHT, MARGIN, FRAMES = 2048, 2048, 128, 1
FONT_PATH = "sources/variable_ttf/Mekorot-VF.ttf"
AUXILIARY_FONT = "Helvetica Neue"
AUXILIARY_FONT_SIZE = 48
BIG_TEXT = FormattedString()
BIG_TEXT.append("Aa", font=FONT_PATH, fontSize=MARGIN*8, fill=(1))
BIG_TEXT_WIDTH = BIG_TEXT.size().width
BIG_TEXT_HEIGHT = BIG_TEXT.size().height
BIG_TEXT_X_POS = (WIDTH - BIG_TEXT_WIDTH) / 2 # Adjust this number to change x-axis of main text
BIG_TEXT_Y_POS = (HEIGHT - BIG_TEXT_HEIGHT - (MARGIN * 2))

ttFont = TTFont(FONT_PATH)

# Constants we will work out dynamically
MY_URL = subprocess.check_output("git remote get-url origin", shell=True).decode()
MY_HASH = subprocess.check_output("git rev-parse --short HEAD", shell=True).decode()
MY_FONT_NAME = ttFont["name"].getDebugName(4)
FONT_VERSION = "v%s" % floatToFixedToStr(ttFont["head"].fontRevision, 16)

# Draws a grid
def grid():
    stroke(1,0.1)
    strokeWidth(2)
    STEP_X, STEP_Y = 0, 0
    INCREMENT_X, INCREMENT_Y = MARGIN / 2, MARGIN / 2
    rect(MARGIN, MARGIN, WIDTH - (MARGIN * 2), HEIGHT - (MARGIN * 2))
    for x in range(29):
def TTFontsXML(filenames):  # 转换成XMl 到临时目录
    filenametemp = "static/font/toolstemp.xml"
    font = TTFont(filenames)
    font.saveXML(filenametemp)
    return filenametemp
Пример #55
0
class WOFF2Reader(SFNTReader):

	flavor = "woff2"

	def __init__(self, file, checkChecksums=1, fontNumber=-1):
		if not haveBrotli:
			log.error(
				'The WOFF2 decoder requires the Brotli Python extension, available at: '
				'https://github.com/google/brotli')
			raise ImportError("No module named brotli")

		self.file = file

		signature = Tag(self.file.read(4))
		if signature != b"wOF2":
			raise TTLibError("Not a WOFF2 font (bad signature)")

		self.file.seek(0)
		self.DirectoryEntry = WOFF2DirectoryEntry
		data = self.file.read(woff2DirectorySize)
		if len(data) != woff2DirectorySize:
			raise TTLibError('Not a WOFF2 font (not enough data)')
		sstruct.unpack(woff2DirectoryFormat, data, self)

		self.tables = OrderedDict()
		offset = 0
		for i in range(self.numTables):
			entry = self.DirectoryEntry()
			entry.fromFile(self.file)
			tag = Tag(entry.tag)
			self.tables[tag] = entry
			entry.offset = offset
			offset += entry.length

		totalUncompressedSize = offset
		compressedData = self.file.read(self.totalCompressedSize)
		decompressedData = brotli.decompress(compressedData)
		if len(decompressedData) != totalUncompressedSize:
			raise TTLibError(
				'unexpected size for decompressed font data: expected %d, found %d'
				% (totalUncompressedSize, len(decompressedData)))
		self.transformBuffer = BytesIO(decompressedData)

		self.file.seek(0, 2)
		if self.length != self.file.tell():
			raise TTLibError("reported 'length' doesn't match the actual file size")

		self.flavorData = WOFF2FlavorData(self)

		# make empty TTFont to store data while reconstructing tables
		self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)

	def __getitem__(self, tag):
		"""Fetch the raw table data. Reconstruct transformed tables."""
		entry = self.tables[Tag(tag)]
		if not hasattr(entry, 'data'):
			if entry.transformed:
				entry.data = self.reconstructTable(tag)
			else:
				entry.data = entry.loadData(self.transformBuffer)
		return entry.data

	def reconstructTable(self, tag):
		"""Reconstruct table named 'tag' from transformed data."""
		entry = self.tables[Tag(tag)]
		rawData = entry.loadData(self.transformBuffer)
		if tag == 'glyf':
			# no need to pad glyph data when reconstructing
			padding = self.padding if hasattr(self, 'padding') else None
			data = self._reconstructGlyf(rawData, padding)
		elif tag == 'loca':
			data = self._reconstructLoca()
		elif tag == 'hmtx':
			data = self._reconstructHmtx(rawData)
		else:
			raise TTLibError("transform for table '%s' is unknown" % tag)
		return data

	def _reconstructGlyf(self, data, padding=None):
		""" Return recostructed glyf table data, and set the corresponding loca's
		locations. Optionally pad glyph offsets to the specified number of bytes.
		"""
		self.ttFont['loca'] = WOFF2LocaTable()
		glyfTable = self.ttFont['glyf'] = WOFF2GlyfTable()
		glyfTable.reconstruct(data, self.ttFont)
		if padding:
			glyfTable.padding = padding
		data = glyfTable.compile(self.ttFont)
		return data

	def _reconstructLoca(self):
		""" Return reconstructed loca table data. """
		if 'loca' not in self.ttFont:
			# make sure glyf is reconstructed first
			self.tables['glyf'].data = self.reconstructTable('glyf')
		locaTable = self.ttFont['loca']
		data = locaTable.compile(self.ttFont)
		if len(data) != self.tables['loca'].origLength:
			raise TTLibError(
				"reconstructed 'loca' table doesn't match original size: "
				"expected %d, found %d"
				% (self.tables['loca'].origLength, len(data)))
		return data

	def _reconstructHmtx(self, data):
		""" Return reconstructed hmtx table data. """
		# Before reconstructing 'hmtx' table we need to parse other tables:
		# 'glyf' is required for reconstructing the sidebearings from the glyphs'
		# bounding box; 'hhea' is needed for the numberOfHMetrics field.
		if "glyf" in self.flavorData.transformedTables:
			# transformed 'glyf' table is self-contained, thus 'loca' not needed
			tableDependencies = ("maxp", "hhea", "glyf")
		else:
			# decompiling untransformed 'glyf' requires 'loca', which requires 'head'
			tableDependencies = ("maxp", "head", "hhea", "loca", "glyf")
		for tag in tableDependencies:
			self._decompileTable(tag)
		hmtxTable = self.ttFont["hmtx"] = WOFF2HmtxTable()
		hmtxTable.reconstruct(data, self.ttFont)
		data = hmtxTable.compile(self.ttFont)
		return data

	def _decompileTable(self, tag):
		"""Decompile table data and store it inside self.ttFont."""
		data = self[tag]
		if self.ttFont.isLoaded(tag):
			return self.ttFont[tag]
		tableClass = getTableClass(tag)
		table = tableClass(tag)
		self.ttFont.tables[tag] = table
		table.decompile(data, self.ttFont)
Пример #56
0
 def _decode_antifont(self, font_url):
     response = requests.get(font_url)
     font = TTFont(BytesIO(response.content))
     cmap = font.getBestCmap()  # 返回unicode cmap字典可用的字体
     font.close()
     return cmap
Пример #57
0
from fontTools.ttLib import TTFont
font = TTFont('movie.woff')  # 打开当前目录的movie.woff文件
font.saveXML('movie.xml')  # 另存为movie.xml
import sys
from fontTools.ttLib import TTFont
import argparse

parser = argparse.ArgumentParser(description="Finds deeply nested components")
parser.add_argument("--depth",
                    metavar="depth",
                    type=int,
                    default=2,
                    help="minimum depth to report")
parser.add_argument("font", metavar="FONT", help="the font file to test")

args = parser.parse_args()
font = TTFont(args.font)

if "glyf" not in font:
    print("find-nested-components can only be used on TrueType fonts")

nesting = {}


def get_nesting(glyph):
    if glyph in nesting:
        return nesting[glyph]
    gg = font["glyf"].glyphs[glyph]
    components = gg.getComponentNames(font["glyf"])
    if not components:
        nesting[glyph] = {"depth": 0, "chain": []}
        return nesting[glyph]
    depth = 0
    chain = []
Пример #59
0
 def _CheckFont(self, font_path, glyph):
     font = TTFont(font_path)
     for table in font['cmap'].tables:
         if ord(glyph) in table.cmap.keys():
             return True
     return False
Пример #60
0
class WOFF2Writer(SFNTWriter):

	flavor = "woff2"

	def __init__(self, file, numTables, sfntVersion="\000\001\000\000",
		         flavor=None, flavorData=None):
		if not haveBrotli:
			log.error(
				'The WOFF2 encoder requires the Brotli Python extension, available at: '
				'https://github.com/google/brotli')
			raise ImportError("No module named brotli")

		self.file = file
		self.numTables = numTables
		self.sfntVersion = Tag(sfntVersion)
		self.flavorData = WOFF2FlavorData(data=flavorData)

		self.directoryFormat = woff2DirectoryFormat
		self.directorySize = woff2DirectorySize
		self.DirectoryEntry = WOFF2DirectoryEntry

		self.signature = Tag("wOF2")

		self.nextTableOffset = 0
		self.transformBuffer = BytesIO()

		self.tables = OrderedDict()

		# make empty TTFont to store data while normalising and transforming tables
		self.ttFont = TTFont(recalcBBoxes=False, recalcTimestamp=False)

	def __setitem__(self, tag, data):
		"""Associate new entry named 'tag' with raw table data."""
		if tag in self.tables:
			raise TTLibError("cannot rewrite '%s' table" % tag)
		if tag == 'DSIG':
			# always drop DSIG table, since the encoding process can invalidate it
			self.numTables -= 1
			return

		entry = self.DirectoryEntry()
		entry.tag = Tag(tag)
		entry.flags = getKnownTagIndex(entry.tag)
		# WOFF2 table data are written to disk only on close(), after all tags
		# have been specified
		entry.data = data

		self.tables[tag] = entry

	def close(self):
		""" All tags must have been specified. Now write the table data and directory.
		"""
		if len(self.tables) != self.numTables:
			raise TTLibError("wrong number of tables; expected %d, found %d" % (self.numTables, len(self.tables)))

		if self.sfntVersion in ("\x00\x01\x00\x00", "true"):
			isTrueType = True
		elif self.sfntVersion == "OTTO":
			isTrueType = False
		else:
			raise TTLibError("Not a TrueType or OpenType font (bad sfntVersion)")

		# The WOFF2 spec no longer requires the glyph offsets to be 4-byte aligned.
		# However, the reference WOFF2 implementation still fails to reconstruct
		# 'unpadded' glyf tables, therefore we need to 'normalise' them.
		# See:
		# https://github.com/khaledhosny/ots/issues/60
		# https://github.com/google/woff2/issues/15
		if isTrueType and "glyf" in self.flavorData.transformedTables:
			self._normaliseGlyfAndLoca(padding=4)
		self._setHeadTransformFlag()

		# To pass the legacy OpenType Sanitiser currently included in browsers,
		# we must sort the table directory and data alphabetically by tag.
		# See:
		# https://github.com/google/woff2/pull/3
		# https://lists.w3.org/Archives/Public/public-webfonts-wg/2015Mar/0000.html
		# TODO(user): remove to match spec once browsers are on newer OTS
		self.tables = OrderedDict(sorted(self.tables.items()))

		self.totalSfntSize = self._calcSFNTChecksumsLengthsAndOffsets()

		fontData = self._transformTables()
		compressedFont = brotli.compress(fontData, mode=brotli.MODE_FONT)

		self.totalCompressedSize = len(compressedFont)
		self.length = self._calcTotalSize()
		self.majorVersion, self.minorVersion = self._getVersion()
		self.reserved = 0

		directory = self._packTableDirectory()
		self.file.seek(0)
		self.file.write(pad(directory + compressedFont, size=4))
		self._writeFlavorData()

	def _normaliseGlyfAndLoca(self, padding=4):
		""" Recompile glyf and loca tables, aligning glyph offsets to multiples of
		'padding' size. Update the head table's 'indexToLocFormat' accordingly while
		compiling loca.
		"""
		if self.sfntVersion == "OTTO":
			return

		for tag in ('maxp', 'head', 'loca', 'glyf'):
			self._decompileTable(tag)
		self.ttFont['glyf'].padding = padding
		for tag in ('glyf', 'loca'):
			self._compileTable(tag)

	def _setHeadTransformFlag(self):
		""" Set bit 11 of 'head' table flags to indicate that the font has undergone
		a lossless modifying transform. Re-compile head table data."""
		self._decompileTable('head')
		self.ttFont['head'].flags |= (1 << 11)
		self._compileTable('head')

	def _decompileTable(self, tag):
		""" Fetch table data, decompile it, and store it inside self.ttFont. """
		tag = Tag(tag)
		if tag not in self.tables:
			raise TTLibError("missing required table: %s" % tag)
		if self.ttFont.isLoaded(tag):
			return
		data = self.tables[tag].data
		if tag == 'loca':
			tableClass = WOFF2LocaTable
		elif tag == 'glyf':
			tableClass = WOFF2GlyfTable
		elif tag == 'hmtx':
			tableClass = WOFF2HmtxTable
		else:
			tableClass = getTableClass(tag)
		table = tableClass(tag)
		self.ttFont.tables[tag] = table
		table.decompile(data, self.ttFont)

	def _compileTable(self, tag):
		""" Compile table and store it in its 'data' attribute. """
		self.tables[tag].data = self.ttFont[tag].compile(self.ttFont)

	def _calcSFNTChecksumsLengthsAndOffsets(self):
		""" Compute the 'original' SFNT checksums, lengths and offsets for checksum
		adjustment calculation. Return the total size of the uncompressed font.
		"""
		offset = sfntDirectorySize + sfntDirectoryEntrySize * len(self.tables)
		for tag, entry in self.tables.items():
			data = entry.data
			entry.origOffset = offset
			entry.origLength = len(data)
			if tag == 'head':
				entry.checkSum = calcChecksum(data[:8] + b'\0\0\0\0' + data[12:])
			else:
				entry.checkSum = calcChecksum(data)
			offset += (entry.origLength + 3) & ~3
		return offset

	def _transformTables(self):
		"""Return transformed font data."""
		transformedTables = self.flavorData.transformedTables
		for tag, entry in self.tables.items():
			data = None
			if tag in transformedTables:
				data = self.transformTable(tag)
				if data is not None:
					entry.transformed = True
			if data is None:
				# pass-through the table data without transformation
				data = entry.data
				entry.transformed = False
			entry.offset = self.nextTableOffset
			entry.saveData(self.transformBuffer, data)
			self.nextTableOffset += entry.length
		self.writeMasterChecksum()
		fontData = self.transformBuffer.getvalue()
		return fontData

	def transformTable(self, tag):
		"""Return transformed table data, or None if some pre-conditions aren't
		met -- in which case, the non-transformed table data will be used.
		"""
		if tag == "loca":
			data = b""
		elif tag == "glyf":
			for tag in ('maxp', 'head', 'loca', 'glyf'):
				self._decompileTable(tag)
			glyfTable = self.ttFont['glyf']
			data = glyfTable.transform(self.ttFont)
		elif tag == "hmtx":
			if "glyf" not in self.tables:
				return
			for tag in ("maxp", "head", "hhea", "loca", "glyf", "hmtx"):
				self._decompileTable(tag)
			hmtxTable = self.ttFont["hmtx"]
			data = hmtxTable.transform(self.ttFont)  # can be None
		else:
			raise TTLibError("Transform for table '%s' is unknown" % tag)
		return data

	def _calcMasterChecksum(self):
		"""Calculate checkSumAdjustment."""
		tags = list(self.tables.keys())
		checksums = []
		for i in range(len(tags)):
			checksums.append(self.tables[tags[i]].checkSum)

		# Create a SFNT directory for checksum calculation purposes
		self.searchRange, self.entrySelector, self.rangeShift = getSearchRange(self.numTables, 16)
		directory = sstruct.pack(sfntDirectoryFormat, self)
		tables = sorted(self.tables.items())
		for tag, entry in tables:
			sfntEntry = SFNTDirectoryEntry()
			sfntEntry.tag = entry.tag
			sfntEntry.checkSum = entry.checkSum
			sfntEntry.offset = entry.origOffset
			sfntEntry.length = entry.origLength
			directory = directory + sfntEntry.toString()

		directory_end = sfntDirectorySize + len(self.tables) * sfntDirectoryEntrySize
		assert directory_end == len(directory)

		checksums.append(calcChecksum(directory))
		checksum = sum(checksums) & 0xffffffff
		# BiboAfba!
		checksumadjustment = (0xB1B0AFBA - checksum) & 0xffffffff
		return checksumadjustment

	def writeMasterChecksum(self):
		"""Write checkSumAdjustment to the transformBuffer."""
		checksumadjustment = self._calcMasterChecksum()
		self.transformBuffer.seek(self.tables['head'].offset + 8)
		self.transformBuffer.write(struct.pack(">L", checksumadjustment))

	def _calcTotalSize(self):
		"""Calculate total size of WOFF2 font, including any meta- and/or private data."""
		offset = self.directorySize
		for entry in self.tables.values():
			offset += len(entry.toString())
		offset += self.totalCompressedSize
		offset = (offset + 3) & ~3
		offset = self._calcFlavorDataOffsetsAndSize(offset)
		return offset

	def _calcFlavorDataOffsetsAndSize(self, start):
		"""Calculate offsets and lengths for any meta- and/or private data."""
		offset = start
		data = self.flavorData
		if data.metaData:
			self.metaOrigLength = len(data.metaData)
			self.metaOffset = offset
			self.compressedMetaData = brotli.compress(
				data.metaData, mode=brotli.MODE_TEXT)
			self.metaLength = len(self.compressedMetaData)
			offset += self.metaLength
		else:
			self.metaOffset = self.metaLength = self.metaOrigLength = 0
			self.compressedMetaData = b""
		if data.privData:
			# make sure private data is padded to 4-byte boundary
			offset = (offset + 3) & ~3
			self.privOffset = offset
			self.privLength = len(data.privData)
			offset += self.privLength
		else:
			self.privOffset = self.privLength = 0
		return offset

	def _getVersion(self):
		"""Return the WOFF2 font's (majorVersion, minorVersion) tuple."""
		data = self.flavorData
		if data.majorVersion is not None and data.minorVersion is not None:
			return data.majorVersion, data.minorVersion
		else:
			# if None, return 'fontRevision' from 'head' table
			if 'head' in self.tables:
				return struct.unpack(">HH", self.tables['head'].data[4:8])
			else:
				return 0, 0

	def _packTableDirectory(self):
		"""Return WOFF2 table directory data."""
		directory = sstruct.pack(self.directoryFormat, self)
		for entry in self.tables.values():
			directory = directory + entry.toString()
		return directory

	def _writeFlavorData(self):
		"""Write metadata and/or private data using appropiate padding."""
		compressedMetaData = self.compressedMetaData
		privData = self.flavorData.privData
		if compressedMetaData and privData:
			compressedMetaData = pad(compressedMetaData, size=4)
		if compressedMetaData:
			self.file.seek(self.metaOffset)
			assert self.file.tell() == self.metaOffset
			self.file.write(compressedMetaData)
		if privData:
			self.file.seek(self.privOffset)
			assert self.file.tell() == self.privOffset
			self.file.write(privData)

	def reordersTables(self):
		return True