summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rwxr-xr-xtools/layoutlib/rename_font/build_font_single.py224
1 files changed, 224 insertions, 0 deletions
diff --git a/tools/layoutlib/rename_font/build_font_single.py b/tools/layoutlib/rename_font/build_font_single.py
new file mode 100755
index 0000000..c747d92
--- /dev/null
+++ b/tools/layoutlib/rename_font/build_font_single.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Rename the PS name of all fonts in the input directories and copy them to the
+output directory.
+
+Usage: build_font.py /path/to/input_fonts1/ /path/to/input_fonts2/ /path/to/output_fonts/
+
+"""
+
+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
+
+# 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/')
+ for directory in argv:
+ if not os.path.isdir(directory):
+ sys.exit(directory + ' is not a valid directory')
+ global dest_dir
+ dest_dir = argv[-1]
+ src_dirs = argv[:-1]
+ cwd = os.getcwd()
+ os.chdir(dest_dir)
+ files = glob.glob('*')
+ for filename in files:
+ os.remove(filename)
+ os.chdir(cwd)
+ input_fonts = list()
+ 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)
+ if '.git' in dirnames:
+ # 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)
+
+
+def convert_font(input_path):
+ filename = os.path.basename(input_path)
+ print 'Converting font: ' + filename
+ # the path to the output file. The file name is the fontfilename.ttx
+ ttx_path = os.path.join(dest_dir, filename)
+ ttx_path = ttx_path[:-1] + 'x'
+ try:
+ # run ttx to generate an xml file in the output folder which represents all
+ # its info
+ ttx_args = ['-q', '-d', dest_dir, input_path]
+ ttx.main(ttx_args)
+ # now parse the xml file to change its PS name.
+ tree = etree.parse(ttx_path)
+ root = tree.getroot()
+ for name in root.iter('name'):
+ 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)
+ except InvalidFontException:
+ # In case of invalid fonts, we exit.
+ print filename + ' is not a valid font'
+ raise
+ except Exception as e:
+ print 'Error converting font: ' + filename
+ print e
+ # Some fonts are too big to be handled by the ttx library.
+ # Just copy paste them.
+ shutil.copy(input_path, dest_dir)
+ try:
+ # delete the temp ttx file is it exists.
+ os.remove(ttx_path)
+ except OSError:
+ pass
+
+
+def get_font_info(tag):
+ """ 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:
+ 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:
+ 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 get_version(string):
+ # The string must begin with 'Version n.nn '
+ # to extract n.nn, we return the second entry in the split strings.
+ string = string.strip()
+ if not string.startswith('Version '):
+ 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:])