Loading tools/layoutlib/rename_font/build_font.py +117 −47 Original line number Original line Diff line number Diff line Loading @@ -22,19 +22,45 @@ Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/outp """ """ import glob from multiprocessing import Pool import os import re import shutil import sys import sys import xml.etree.ElementTree as etree # Prevent .pyc files from being created. sys.dont_write_bytecode = True # fontTools is available at platform/external/fonttools # fontTools is available at platform/external/fonttools from fontTools import ttx from fontTools import ttx import re import os import xml.etree.ElementTree as etree import shutil import glob from multiprocessing import Pool # global variable # global variable dest_dir = '/tmp' dest_dir = '/tmp' class FontInfo(object): family = None style = None version = None ends_in_regular = False fullname = None class InvalidFontException(Exception): pass # These constants represent the value of nameID parameter in the namerecord for # different information. # see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b NAMEID_FAMILY = 1 NAMEID_STYLE = 2 NAMEID_FULLNAME = 4 NAMEID_VERSION = 5 def main(argv): def main(argv): if len(argv) < 2: if len(argv) < 2: sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/') sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/') Loading @@ -56,9 +82,9 @@ def main(argv): for filename in filenames: for filename in filenames: input_path = os.path.join(dirname, filename) input_path = os.path.join(dirname, filename) extension = os.path.splitext(filename)[1].lower() extension = os.path.splitext(filename)[1].lower() if (extension == '.ttf'): if extension == '.ttf': input_fonts.append(input_path) input_fonts.append(input_path) elif (extension == '.xml'): elif extension == '.xml': shutil.copy(input_path, dest_dir) shutil.copy(input_path, dest_dir) if '.git' in dirnames: if '.git' in dirnames: # don't go into any .git directories. # don't go into any .git directories. Loading @@ -68,9 +94,6 @@ def main(argv): pool.map(convert_font, input_fonts) pool.map(convert_font, input_fonts) class InvalidFontException(Exception): pass def convert_font(input_path): def convert_font(input_path): filename = os.path.basename(input_path) filename = os.path.basename(input_path) print 'Converting font: ' + filename print 'Converting font: ' + filename Loading @@ -86,10 +109,7 @@ def convert_font(input_path): tree = etree.parse(ttx_path) tree = etree.parse(ttx_path) root = tree.getroot() root = tree.getroot() for name in root.iter('name'): for name in root.iter('name'): [old_ps_name, version] = get_font_info(name) update_tag(name, get_font_info(name)) if old_ps_name is not None and version is not None: new_ps_name = old_ps_name + version update_name(name, new_ps_name) tree.write(ttx_path, xml_declaration=True, encoding='utf-8') tree.write(ttx_path, xml_declaration=True, encoding='utf-8') # generate the udpated font now. # generate the udpated font now. ttx_args = ['-q', '-d', dest_dir, ttx_path] ttx_args = ['-q', '-d', dest_dir, ttx_path] Loading @@ -110,37 +130,83 @@ def convert_font(input_path): except OSError: except OSError: pass pass def get_font_info(tag): def get_font_info(tag): ps_name = None """ Returns a list of FontInfo representing the various sets of namerecords ps_version = None found in the name table of the font. """ fonts = [] font = None last_name_id = sys.maxint for namerecord in tag.iter('namerecord'): if 'nameID' in namerecord.attrib: name_id = int(namerecord.attrib['nameID']) # A new font should be created for each platform, encoding and language # id. But, since the nameIDs are sorted, we use the easy approach of # creating a new one when the nameIDs reset. if name_id <= last_name_id and font is not None: fonts.append(font) font = None last_name_id = name_id if font is None: font = FontInfo() if name_id == NAMEID_FAMILY: font.family = namerecord.text.strip() if name_id == NAMEID_STYLE: font.style = namerecord.text.strip() if name_id == NAMEID_FULLNAME: font.ends_in_regular = ends_in_regular(namerecord.text) font.fullname = namerecord.text.strip() if name_id == NAMEID_VERSION: font.version = get_version(namerecord.text) if font is not None: fonts.append(font) return fonts def update_tag(tag, fonts): last_name_id = sys.maxint fonts_iterator = fonts.__iter__() font = None for namerecord in tag.iter('namerecord'): for namerecord in tag.iter('namerecord'): if 'nameID' in namerecord.attrib: if 'nameID' in namerecord.attrib: # if the tag has nameID=6, it is the postscript name of the font. name_id = int(namerecord.attrib['nameID']) # see: http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b if name_id <= last_name_id: if namerecord.attrib['nameID'] == '6': font = fonts_iterator.next() if ps_name is not None: font = update_font_name(font) if not sanitize(namerecord.text) == ps_name: last_name_id = name_id raise InvalidFontException('found multiple possibilities of the font name') if name_id == NAMEID_FAMILY: namerecord.text = font.family if name_id == NAMEID_FULLNAME: namerecord.text = font.fullname def update_font_name(font): """ Compute the new font family name and font fullname. If the font has a valid version, it's sanitized and appended to the font family name. The font fullname is then created by joining the new family name and the style. If the style is 'Regular', it is appended only if the original font had it. """ if font.family is None or font.style is None: raise InvalidFontException('Font doesn\'t have proper family name or style') if font.version is not None: new_family = font.family + font.version else: else: ps_name = sanitize(namerecord.text) new_family = font.family # nameID=5 means the font version if font.style is 'Regular' and not font.ends_in_regular: if namerecord.attrib['nameID'] == '5': font.fullname = new_family if ps_version is not None: if not ps_version == get_version(namerecord.text): raise InvalidFontException('found multiple possibilities of the font version') else: else: ps_version = get_version(namerecord.text) font.fullname = new_family + ' ' + font.style return [ps_name, ps_version] font.family = new_family return font def update_name(tag, name): def ends_in_regular(string): for namerecord in tag.iter('namerecord'): """ According to the specification, the font fullname should not end in if 'nameID' in namerecord.attrib: 'Regular' for plain fonts. However, some fonts don't obey this rule. We if namerecord.attrib['nameID'] == '6': keep the style info, to minimize the diff. """ namerecord.text = name string = string.strip().split()[-1] return string is 'Regular' def sanitize(string): return re.sub(r'[^\w-]+', '', string) def get_version(string): def get_version(string): # The string must begin with 'Version n.nn ' # The string must begin with 'Version n.nn ' Loading @@ -150,5 +216,9 @@ def get_version(string): raise InvalidFontException('mal-formed font version') raise InvalidFontException('mal-formed font version') return sanitize(string.split()[1]) return sanitize(string.split()[1]) def sanitize(string): return re.sub(r'[^\w-]+', '', string) if __name__ == '__main__': if __name__ == '__main__': main(sys.argv[1:]) main(sys.argv[1:]) tools/layoutlib/rename_font/test.py +3 −2 Original line number Original line Diff line number Diff line Loading @@ -33,10 +33,11 @@ class MyTest(unittest.TestCase): tree = etree.parse(ttx_path) tree = etree.parse(ttx_path) root = tree.getroot() root = tree.getroot() name_tag = root.find('name') name_tag = root.find('name') [f_name, f_version] = build_font.get_font_info(name_tag) fonts = build_font.get_font_info(name_tag) shutil.rmtree(srcdir) shutil.rmtree(srcdir) shutil.rmtree(destdir) shutil.rmtree(destdir) self.assertEqual(f_name, "Roboto-Regular1200310") self.assertEqual(fonts[0].family, "Roboto1200310") self.assertEqual(fonts[0].fullname, "Roboto1200310 Regular") Loading Loading
tools/layoutlib/rename_font/build_font.py +117 −47 Original line number Original line Diff line number Diff line Loading @@ -22,19 +22,45 @@ Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/outp """ """ import glob from multiprocessing import Pool import os import re import shutil import sys import sys import xml.etree.ElementTree as etree # Prevent .pyc files from being created. sys.dont_write_bytecode = True # fontTools is available at platform/external/fonttools # fontTools is available at platform/external/fonttools from fontTools import ttx from fontTools import ttx import re import os import xml.etree.ElementTree as etree import shutil import glob from multiprocessing import Pool # global variable # global variable dest_dir = '/tmp' dest_dir = '/tmp' class FontInfo(object): family = None style = None version = None ends_in_regular = False fullname = None class InvalidFontException(Exception): pass # These constants represent the value of nameID parameter in the namerecord for # different information. # see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b NAMEID_FAMILY = 1 NAMEID_STYLE = 2 NAMEID_FULLNAME = 4 NAMEID_VERSION = 5 def main(argv): def main(argv): if len(argv) < 2: if len(argv) < 2: sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/') sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/') Loading @@ -56,9 +82,9 @@ def main(argv): for filename in filenames: for filename in filenames: input_path = os.path.join(dirname, filename) input_path = os.path.join(dirname, filename) extension = os.path.splitext(filename)[1].lower() extension = os.path.splitext(filename)[1].lower() if (extension == '.ttf'): if extension == '.ttf': input_fonts.append(input_path) input_fonts.append(input_path) elif (extension == '.xml'): elif extension == '.xml': shutil.copy(input_path, dest_dir) shutil.copy(input_path, dest_dir) if '.git' in dirnames: if '.git' in dirnames: # don't go into any .git directories. # don't go into any .git directories. Loading @@ -68,9 +94,6 @@ def main(argv): pool.map(convert_font, input_fonts) pool.map(convert_font, input_fonts) class InvalidFontException(Exception): pass def convert_font(input_path): def convert_font(input_path): filename = os.path.basename(input_path) filename = os.path.basename(input_path) print 'Converting font: ' + filename print 'Converting font: ' + filename Loading @@ -86,10 +109,7 @@ def convert_font(input_path): tree = etree.parse(ttx_path) tree = etree.parse(ttx_path) root = tree.getroot() root = tree.getroot() for name in root.iter('name'): for name in root.iter('name'): [old_ps_name, version] = get_font_info(name) update_tag(name, get_font_info(name)) if old_ps_name is not None and version is not None: new_ps_name = old_ps_name + version update_name(name, new_ps_name) tree.write(ttx_path, xml_declaration=True, encoding='utf-8') tree.write(ttx_path, xml_declaration=True, encoding='utf-8') # generate the udpated font now. # generate the udpated font now. ttx_args = ['-q', '-d', dest_dir, ttx_path] ttx_args = ['-q', '-d', dest_dir, ttx_path] Loading @@ -110,37 +130,83 @@ def convert_font(input_path): except OSError: except OSError: pass pass def get_font_info(tag): def get_font_info(tag): ps_name = None """ Returns a list of FontInfo representing the various sets of namerecords ps_version = None found in the name table of the font. """ fonts = [] font = None last_name_id = sys.maxint for namerecord in tag.iter('namerecord'): if 'nameID' in namerecord.attrib: name_id = int(namerecord.attrib['nameID']) # A new font should be created for each platform, encoding and language # id. But, since the nameIDs are sorted, we use the easy approach of # creating a new one when the nameIDs reset. if name_id <= last_name_id and font is not None: fonts.append(font) font = None last_name_id = name_id if font is None: font = FontInfo() if name_id == NAMEID_FAMILY: font.family = namerecord.text.strip() if name_id == NAMEID_STYLE: font.style = namerecord.text.strip() if name_id == NAMEID_FULLNAME: font.ends_in_regular = ends_in_regular(namerecord.text) font.fullname = namerecord.text.strip() if name_id == NAMEID_VERSION: font.version = get_version(namerecord.text) if font is not None: fonts.append(font) return fonts def update_tag(tag, fonts): last_name_id = sys.maxint fonts_iterator = fonts.__iter__() font = None for namerecord in tag.iter('namerecord'): for namerecord in tag.iter('namerecord'): if 'nameID' in namerecord.attrib: if 'nameID' in namerecord.attrib: # if the tag has nameID=6, it is the postscript name of the font. name_id = int(namerecord.attrib['nameID']) # see: http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b if name_id <= last_name_id: if namerecord.attrib['nameID'] == '6': font = fonts_iterator.next() if ps_name is not None: font = update_font_name(font) if not sanitize(namerecord.text) == ps_name: last_name_id = name_id raise InvalidFontException('found multiple possibilities of the font name') if name_id == NAMEID_FAMILY: namerecord.text = font.family if name_id == NAMEID_FULLNAME: namerecord.text = font.fullname def update_font_name(font): """ Compute the new font family name and font fullname. If the font has a valid version, it's sanitized and appended to the font family name. The font fullname is then created by joining the new family name and the style. If the style is 'Regular', it is appended only if the original font had it. """ if font.family is None or font.style is None: raise InvalidFontException('Font doesn\'t have proper family name or style') if font.version is not None: new_family = font.family + font.version else: else: ps_name = sanitize(namerecord.text) new_family = font.family # nameID=5 means the font version if font.style is 'Regular' and not font.ends_in_regular: if namerecord.attrib['nameID'] == '5': font.fullname = new_family if ps_version is not None: if not ps_version == get_version(namerecord.text): raise InvalidFontException('found multiple possibilities of the font version') else: else: ps_version = get_version(namerecord.text) font.fullname = new_family + ' ' + font.style return [ps_name, ps_version] font.family = new_family return font def update_name(tag, name): def ends_in_regular(string): for namerecord in tag.iter('namerecord'): """ According to the specification, the font fullname should not end in if 'nameID' in namerecord.attrib: 'Regular' for plain fonts. However, some fonts don't obey this rule. We if namerecord.attrib['nameID'] == '6': keep the style info, to minimize the diff. """ namerecord.text = name string = string.strip().split()[-1] return string is 'Regular' def sanitize(string): return re.sub(r'[^\w-]+', '', string) def get_version(string): def get_version(string): # The string must begin with 'Version n.nn ' # The string must begin with 'Version n.nn ' Loading @@ -150,5 +216,9 @@ def get_version(string): raise InvalidFontException('mal-formed font version') raise InvalidFontException('mal-formed font version') return sanitize(string.split()[1]) return sanitize(string.split()[1]) def sanitize(string): return re.sub(r'[^\w-]+', '', string) if __name__ == '__main__': if __name__ == '__main__': main(sys.argv[1:]) main(sys.argv[1:])
tools/layoutlib/rename_font/test.py +3 −2 Original line number Original line Diff line number Diff line Loading @@ -33,10 +33,11 @@ class MyTest(unittest.TestCase): tree = etree.parse(ttx_path) tree = etree.parse(ttx_path) root = tree.getroot() root = tree.getroot() name_tag = root.find('name') name_tag = root.find('name') [f_name, f_version] = build_font.get_font_info(name_tag) fonts = build_font.get_font_info(name_tag) shutil.rmtree(srcdir) shutil.rmtree(srcdir) shutil.rmtree(destdir) shutil.rmtree(destdir) self.assertEqual(f_name, "Roboto-Regular1200310") self.assertEqual(fonts[0].family, "Roboto1200310") self.assertEqual(fonts[0].fullname, "Roboto1200310 Regular") Loading