From 4ab36d3e3bec37edc7e0eb7f9e20b0d14d37071c Mon Sep 17 00:00:00 2001 From: Rasmus Andersson Date: Mon, 3 Sep 2018 16:06:04 -0700 Subject: update glyphinfo --- misc/tools/common.py | 23 +++++ misc/tools/gen-glyphinfo.py | 223 ++++++++++---------------------------------- 2 files changed, 71 insertions(+), 175 deletions(-) (limited to 'misc') diff --git a/misc/tools/common.py b/misc/tools/common.py index c38ef817b..d53c8e755 100644 --- a/misc/tools/common.py +++ b/misc/tools/common.py @@ -3,6 +3,7 @@ from __future__ import print_function import sys, os from os.path import dirname, abspath, join as pjoin import subprocess +import time # patch PYTHONPATH to include $BASEDIR/build/venv/python/site-packages BASEDIR = abspath(pjoin(dirname(__file__), os.pardir, os.pardir)) @@ -34,5 +35,27 @@ def getVersion(): return _version +_local_tz_offs = None +def getLocalTimeZoneOffset(): # in seconds from UTC + # seriously ugly hack to get timezone offset in Python + global _local_tz_offs + if _local_tz_offs is None: + tzname = time.strftime("%Z", time.localtime()) + s = time.strftime('%z', time.strptime(tzname, '%Z')) + i = 0 + neg = False + if s[0] == '-': + neg = True + i = 1 + elif s[0] == '+': + i = 1 + h = int(s[i:i+2]) + m = int(s[i+2:]) + _local_tz_offs = ((h * 60) + m) * 60 + if neg: + _local_tz_offs = -_local_tz_offs + return _local_tz_offs + + # update environment to include $VENVDIR/bin os.environ['PATH'] = os.path.join(VENVDIR, 'bin') + ':' + os.environ['PATH'] diff --git a/misc/tools/gen-glyphinfo.py b/misc/tools/gen-glyphinfo.py index 4fdf73fae..9be36952e 100755 --- a/misc/tools/gen-glyphinfo.py +++ b/misc/tools/gen-glyphinfo.py @@ -4,129 +4,20 @@ # Grab http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt # from __future__ import print_function -import os, sys, json, re + +import os, sys +from os.path import dirname, basename, abspath, relpath, join as pjoin +sys.path.append(abspath(pjoin(dirname(__file__), 'tools'))) +from common import BASEDIR, getVersion, getLocalTimeZoneOffset + +import json, re +import time from argparse import ArgumentParser -from robofab.objects.objectsRF import OpenFont from collections import OrderedDict -from unicode_util import parseUnicodeDataFile from ConfigParser import RawConfigParser - - -BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) - - -# Regex matching "default" glyph names, like "uni2043" and "u01C5" -uniNameRe = re.compile(r'^u(?:ni)([0-9A-F]{4,8})$') - - -def unicodeForDefaultGlyphName(glyphName): - m = uniNameRe.match(glyphName) - if m is not None: - try: - return int(m.group(1), 16) - except: - pass - return None - - -def loadAGL(filename): # -> { 2126: 'Omega', ... } - m = {} - with open(filename, 'r') as f: - for line in f: - # Omega;2126 - # dalethatafpatah;05D3 05B2 # higher-level combinations; ignored - line = line.strip() - if len(line) > 0 and line[0] != '#': - name, uc = tuple([c.strip() for c in line.split(';')]) - if uc.find(' ') == -1: - # it's a 1:1 mapping - m[int(uc, 16)] = name - return m - - -def loadLocalNamesDB(fonts, agl, diacriticComps): - uc2names = None # { 2126: ['Omega', ...], ...} - allNames = OrderedDict() # {'Omega':True, ...} - - for font in fonts: - _uc2names = font.getCharacterMapping() # { 2126: ['Omega', ...], ...} - if uc2names is None: - uc2names = _uc2names - else: - for uc, _names in _uc2names.iteritems(): - names = uc2names.setdefault(uc, []) - for name in _names: - if name not in names: - names.append(name) - for g in font: - allNames.setdefault(g.name, True) - - # agl { 2126: 'Omega', ...} -> { 'Omega': [2126, ...], ...} - aglName2Ucs = {} - for uc, name in agl.iteritems(): - aglName2Ucs.setdefault(name, []).append(uc) - - for glyphName, comp in diacriticComps.iteritems(): - aglUCs = aglName2Ucs.get(glyphName) - if aglUCs is None: - uc = unicodeForDefaultGlyphName(glyphName) - if uc is not None: - glyphName2 = agl.get(uc) - if glyphName2 is not None: - glyphName = glyphName2 - names = uc2names.setdefault(uc, []) - if glyphName not in names: - names.append(glyphName) - allNames.setdefault(glyphName, True) - else: - allNames.setdefault(glyphName, True) - for uc in aglUCs: - names = uc2names.get(uc, []) - if glyphName not in names: - names.append(glyphName) - uc2names[uc] = names - - name2ucs = {} # { 'Omega': [2126, ...], ...} - for uc, names in uc2names.iteritems(): - for name in names: - name2ucs.setdefault(name, set()).add(uc) - - return uc2names, name2ucs, allNames - - -def canonicalGlyphName(glyphName, uc2names): - uc = unicodeForDefaultGlyphName(glyphName) - if uc is not None: - names = uc2names.get(uc) - if names is not None and len(names) > 0: - return names[0] - return glyphName - - -def parseGlyphComposition(composite): - c = composite.split("=") - d = c[1].split("/") - glyphName = d[0] - if len(d) == 1: - offset = [0, 0] - else: - offset = [int(i) for i in d[1].split(",")] - accentString = c[0] - accents = accentString.split("+") - baseName = accents.pop(0) - accentNames = [i.split(":") for i in accents] - return (glyphName, baseName, accentNames, offset) - - -def loadGlyphCompositions(filename): # { glyphName => (baseName, accentNames, offset) } - compositions = OrderedDict() - with open(filename, 'r') as f: - for line in f: - line = line.strip() - if len(line) > 0 and line[0] != '#': - glyphName, baseName, accentNames, offset = parseGlyphComposition(line) - compositions[glyphName] = (baseName, accentNames, offset) - return compositions +# from robofab.objects.objectsRF import OpenFont +from unicode_util import parseUnicodeDataFile +from defcon import Font def rgbaToCSSColor(r=0, g=0, b=0, a=1): @@ -134,7 +25,7 @@ def rgbaToCSSColor(r=0, g=0, b=0, a=1): if a == 1: return '#%02x%02x%02x' % (R,G,B) else: - return 'rgba(%d,%d,%d,%f)' % (R,G,B,a) + return 'rgba(%d,%d,%d,%g)' % (R,G,B,a) def unicodeName(cp): @@ -146,6 +37,14 @@ def unicodeName(cp): return None +# YYYY/MM/DD HH:mm:ss => YYYY-MM-DDTHH:mm:ssZ (local time -> UTC) +def localDateTimeToUTCStr(localstr, pattern='%Y/%m/%d %H:%M:%S'): + t = time.strptime(localstr, pattern) + ts = time.mktime(t) - getLocalTimeZoneOffset() + return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime(ts)) + + + def main(): argparser = ArgumentParser( description='Generate info on name, unicodes and color mark for all glyphs') @@ -158,18 +57,6 @@ def main(): 'fontPaths', metavar='', type=str, nargs='+', help='UFO fonts to update') args = argparser.parse_args() - markLibKey = 'com.typemytype.robofont.mark' - - srcDir = os.path.join(BASEDIR, 'src') - - # load fontbuild config - config = RawConfigParser(dict_type=OrderedDict) - configFilename = os.path.join(srcDir, 'fontbuild.cfg') - config.read(configFilename) - deleteNames = set() - for sectionName, value in config.items('glyphs'): - if sectionName == 'delete': - deleteNames = set(value.split()) fontPaths = [] for fontPath in args.fontPaths: @@ -179,75 +66,61 @@ def main(): else: fontPaths.append(fontPath) - fonts = [OpenFont(fontPath) for fontPath in args.fontPaths] - - agl = loadAGL(os.path.join(srcDir, 'glyphlist.txt')) # { 2126: 'Omega', ... } - diacriticComps = loadGlyphCompositions(os.path.join(srcDir, 'diacritics.txt')) - uc2names, name2ucs, allNames = loadLocalNamesDB(fonts, agl, diacriticComps) + fonts = [Font(fontPath) for fontPath in args.fontPaths] ucd = {} if args.ucdFile: ucd = parseUnicodeDataFile(args.ucdFile) - glyphorder = OrderedDict() - with open(os.path.join(os.path.dirname(args.fontPaths[0]), 'glyphorder.txt'), 'r') as f: - for name in f.read().splitlines(): - if len(name) and name[0] != '#': - glyphorder[name] = True - - for name in diacriticComps.iterkeys(): - glyphorder[name] = True - - glyphNames = glyphorder.keys() + glyphs = [] # contains final glyph data printed as JSON visitedGlyphNames = set() - glyphs = [] for font in fonts: - for name, v in glyphorder.iteritems(): - if name in deleteNames: - continue + glyphorder = font.lib['public.glyphOrder'] + for name in glyphorder: if name in visitedGlyphNames: continue - g = None - ucs = [] - try: - g = font[name] - ucs = g.unicodes - except: - ucs = name2ucs.get(name) - if ucs is None: - continue + g = font[name] + # color color = None - if g is not None and markLibKey in g.lib: - # TODO: translate from (r,g,b,a) to #RRGGBB (skip A) - rgba = g.lib[markLibKey] - if isinstance(rgba, list) or isinstance(rgba, tuple): - color = rgbaToCSSColor(*rgba) - elif name in diacriticComps: - color = '' + if 'public.markColor' in g.lib: + rgba = [float(c.strip()) for c in g.lib['public.markColor'].strip().split(',')] + color = rgbaToCSSColor(*rgba) + + # mtime + mtime = None + if 'com.schriftgestaltung.Glyphs.lastChange' in g.lib: + datetimestr = g.lib['com.schriftgestaltung.Glyphs.lastChange'] + mtime = localDateTimeToUTCStr(datetimestr) # name[, unicode[, unicodeName[, color]]] + glyph = None + ucs = g.unicodes if len(ucs): for uc in ucs: ucName = unicodeName(ucd.get(uc)) - if not ucName and uc >= 0xE000 and uc <= 0xF8FF: ucName = '[private use %04X]' % uc if color: - glyph = [name, uc, ucName, color] + glyph = [name, uc, ucName, mtime, color] + elif mtime: + glyph = [name, uc, ucName, mtime] elif ucName: glyph = [name, uc, ucName] else: glyph = [name, uc] - - glyphs.append(glyph) else: - glyph = [name, None, None, color] if color else [name] - glyphs.append(glyph) - + if color: + glyph = [name, None, None, mtime, color] + elif mtime: + glyph = [name, None, None, mtime] + else: + glyph = [name] + + glyphs.append(glyph) visitedGlyphNames.add(name) print('{"glyphs":[') -- cgit v1.2.3