diff options
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/layoutlib/rename_font/build_font.py | 164 | ||||
-rwxr-xr-x | tools/layoutlib/rename_font/test.py | 5 |
2 files changed, 120 insertions, 49 deletions
diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py index bd9b14c..c747d92 100755 --- a/tools/layoutlib/rename_font/build_font.py +++ b/tools/layoutlib/rename_font/build_font.py @@ -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 xml.etree.ElementTree as etree + +# Prevent .pyc files from being created. +sys.dont_write_bytecode = True + # fontTools is available at platform/external/fonttools from fontTools import ttx -import re -import os -import xml.etree.ElementTree as etree -import shutil -import glob -from multiprocessing import Pool # global variable 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): if len(argv) < 2: sys.exit('Usage: build_font.py /path/to/input_fonts/ /path/to/out/dir/') @@ -54,23 +80,20 @@ def main(argv): for src_dir in src_dirs: for dirname, dirnames, filenames in os.walk(src_dir): for filename in filenames: - input_path = os.path.join(dirname, filename) - extension = os.path.splitext(filename)[1].lower() - if (extension == '.ttf'): - input_fonts.append(input_path) - elif (extension == '.xml'): - shutil.copy(input_path, dest_dir) + input_path = os.path.join(dirname, filename) + extension = os.path.splitext(filename)[1].lower() + if extension == '.ttf': + input_fonts.append(input_path) + elif extension == '.xml': + shutil.copy(input_path, dest_dir) if '.git' in dirnames: - # don't go into any .git directories. - dirnames.remove('.git') + # don't go into any .git directories. + dirnames.remove('.git') # Create as many threads as the number of CPUs pool = Pool(processes=None) pool.map(convert_font, input_fonts) -class InvalidFontException(Exception): - pass - def convert_font(input_path): filename = os.path.basename(input_path) print 'Converting font: ' + filename @@ -86,11 +109,8 @@ def convert_font(input_path): tree = etree.parse(ttx_path) root = tree.getroot() for name in root.iter('name'): - [old_ps_name, version] = 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' ) + update_tag(name, get_font_info(name)) + tree.write(ttx_path, xml_declaration=True, encoding='utf-8') # generate the udpated font now. ttx_args = ['-q', '-d', dest_dir, ttx_path] ttx.main(ttx_args) @@ -110,37 +130,83 @@ def convert_font(input_path): except OSError: pass + def get_font_info(tag): - ps_name = None - ps_version = None + """ Returns a list of FontInfo representing the various sets of namerecords + 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: - # if the tag has nameID=6, it is the postscript name of the font. - # see: http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b - if namerecord.attrib['nameID'] == '6': - if ps_name is not None: - if not sanitize(namerecord.text) == ps_name: - raise InvalidFontException('found multiple possibilities of the font name') - else: - ps_name = sanitize(namerecord.text) - # nameID=5 means the font version - if namerecord.attrib['nameID'] == '5': - if ps_version is not None: - if not ps_version == get_version(namerecord.text): - raise InvalidFontException('found multiple possibilities of the font version') - else: - ps_version = get_version(namerecord.text) - return [ps_name, ps_version] - - -def update_name(tag, name): + 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'): if 'nameID' in namerecord.attrib: - if namerecord.attrib['nameID'] == '6': - namerecord.text = name + name_id = int(namerecord.attrib['nameID']) + if name_id <= last_name_id: + font = fonts_iterator.next() + font = update_font_name(font) + last_name_id = name_id + 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: + new_family = font.family + if font.style is 'Regular' and not font.ends_in_regular: + font.fullname = new_family + else: + font.fullname = new_family + ' ' + font.style + font.family = new_family + return font + + +def ends_in_regular(string): + """ According to the specification, the font fullname should not end in + 'Regular' for plain fonts. However, some fonts don't obey this rule. We + keep the style info, to minimize the diff. """ + string = string.strip().split()[-1] + return string is 'Regular' -def sanitize(string): - return re.sub(r'[^\w-]+', '', string) def get_version(string): # The string must begin with 'Version n.nn ' @@ -150,5 +216,9 @@ def get_version(string): raise InvalidFontException('mal-formed font version') return sanitize(string.split()[1]) + +def sanitize(string): + return re.sub(r'[^\w-]+', '', string) + if __name__ == '__main__': main(sys.argv[1:]) diff --git a/tools/layoutlib/rename_font/test.py b/tools/layoutlib/rename_font/test.py index b0b69d8..2ffddf4 100755 --- a/tools/layoutlib/rename_font/test.py +++ b/tools/layoutlib/rename_font/test.py @@ -33,10 +33,11 @@ class MyTest(unittest.TestCase): tree = etree.parse(ttx_path) root = tree.getroot() 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(destdir) - self.assertEqual(f_name, "Roboto-Regular1200310") + self.assertEqual(fonts[0].family, "Roboto1200310") + self.assertEqual(fonts[0].fullname, "Roboto1200310 Regular") |