diff options
author | Rasmus Andersson <rasmus@notion.se> | 2018-09-03 22:55:49 +0300 |
---|---|---|
committer | Rasmus Andersson <rasmus@notion.se> | 2018-09-03 22:55:49 +0300 |
commit | c833e252c925e8dd68108660710ca835d95daa6f (patch) | |
tree | 6b2e28264ed45efd7f054e453b622098d0d875b8 /misc/tools/kernsample.py | |
parent | 8c1a4c181ef12000179dfec541f1af87e9b03122 (diff) | |
download | inter-c833e252c925e8dd68108660710ca835d95daa6f.tar.xz |
Major overhaul, moving from UFO2 to Glyphs and UFO3, plus a brand new and much simpler fontbuild
Diffstat (limited to 'misc/tools/kernsample.py')
-rwxr-xr-x | misc/tools/kernsample.py | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/misc/tools/kernsample.py b/misc/tools/kernsample.py new file mode 100755 index 000000000..7e1fbe0f8 --- /dev/null +++ b/misc/tools/kernsample.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +# encoding: utf8 +from __future__ import print_function +import os, sys, plistlib +from collections import OrderedDict +from argparse import ArgumentParser +from robofab.objects.objectsRF import OpenFont + +RIGHT = 1 +LEFT = 2 + + +def mapGroups(groups): # => { glyphname => set(groupname, ...), ... } + m = OrderedDict() + for groupname, glyphnames in groups.iteritems(): + for glyphname in glyphnames: + m.setdefault(glyphname, set()).add(groupname) + return m + + +def fmtGlyphname(glyphname, glyph=None): + if glyph is not None and len(glyphname) == 1 and ord(glyphname[0]) == glyph.unicode: + # literal, e.g. "A" + return glyphname + else: + # named, e.g. "\Omega" + return '/' + glyphname + ' ' + + +def printPairs(font, baseSide, baseSideGlyph, otherSideNames, args): + out = [] + if args.formatAsUnicode: + base = '\\u%04X' % baseSideGlyph.unicode + for otherName in otherSideNames: + if otherName in font: + otherGlyph = font[otherName] + if otherGlyph.unicode is not None: + if baseSide == LEFT: + out.append('%s\\u%04X' % (base, otherGlyph.unicode)) + else: + out.append('\\u%04X%s' % (otherGlyph.unicode, base)) + else: + base = fmtGlyphname(baseSideGlyph.name, baseSideGlyph) + prefix_uc = '' + prefix_lc = '' + suffix_uc = '' + suffix_lc = '' + + if args.noPrefixAutocase: + prefix_uc = args.prefix + prefix_lc = args.prefix + suffix_uc = args.suffix + suffix_lc = args.suffix + else: + if args.prefix and len(args.prefix) > 0: + s = unicode(args.prefix) + if s[0].isupper(): + prefix_uc = args.prefix + prefix_lc = args.prefix.lower() + else: + prefix_uc = args.prefix.upper() + prefix_lc = args.prefix + + if args.suffix and len(args.suffix) > 0: + s = unicode(args.suffix) + if s[0].isupper(): + suffix_uc = args.suffix + suffix_lc = args.suffix.lower() + else: + suffix_uc = args.suffix.upper() + suffix_lc = args.suffix + + for otherName in otherSideNames: + if otherName in font: + otherGlyph = None + if len(otherName) == 1: + otherGlyph = font[otherName] + prefix = prefix_lc + suffix = suffix_lc + if unicode(otherName[0]).isupper(): + prefix = prefix_uc + suffix = suffix_uc + if baseSide == LEFT: + out.append('%s%s%s%s' % ( + prefix, base, fmtGlyphname(otherName, otherGlyph), suffix + )) + else: + out.append('%s%s%s%s' % ( + prefix, fmtGlyphname(otherName, otherGlyph), base, suffix + )) + + print(' '.join(out)) + + +def samplesForGlyphnameR(font, groups, groupmap, kerning, glyphname, args): + rightGlyph = font[glyphname] + includeAll = args.includeAllInGroup + leftnames = set() + + _addLeftnames(groups, kerning, glyphname, leftnames, includeAll) + + if glyphname in groupmap: + for groupname in groupmap[glyphname]: + if groupname.find('_RIGHT_') != -1: + _addLeftnames(groups, kerning, groupname, leftnames, includeAll) + + leftnames = sorted(leftnames) + printPairs(font, RIGHT, rightGlyph, leftnames, args) + + +def _addLeftnames(groups, kerning, glyphname, leftnames, includeAll=True): + # kerning : { leftName => {rightName => kernVal} } + for leftname, kern in kerning.iteritems(): + if glyphname in kern: + if leftname[0] == '@': + for leftname2 in groups[leftname]: + leftnames.add(leftname2) + if not includeAll: + # TODO: in this case, pick the one leftname that has the highest + # ranking in glyphorder + break + else: + leftnames.add(leftname) + + +def samplesForGlyphnameL(font, groups, groupmap, kerning, glyphname, args): + leftGlyph = font[glyphname] + includeAll = args.includeAllInGroup + rightnames = set() + + _addRightnames(groups, kerning, glyphname, rightnames, includeAll) + + if glyphname in groupmap: + for groupname in groupmap[glyphname]: + if groupname.find('_LEFT_') != -1 or groupname.find('_RIGHT_') == -1: + _addRightnames(groups, kerning, groupname, rightnames, includeAll) + + rightnames = sorted(rightnames) + printPairs(font, LEFT, leftGlyph, rightnames, args) + + +def _addRightnames(groups, kerning, leftname, rightnames, includeAll=True): + if leftname in kerning: + for rightname in kerning[leftname]: + if rightname[0] == '@': + for rightname2 in groups[rightname]: + rightnames.add(rightname2) + if not includeAll: + # TODO: in this case, pick the one rightname that has the highest + # ranking in glyphorder + break + else: + rightnames.add(rightname) + + +def main(): + argparser = ArgumentParser( + description='Generate kerning samples by providing the left-hand side glyph') + + argparser.add_argument( + '-u', dest='formatAsUnicode', action='store_const', const=True, default=False, + help='Format output as unicode escape sequences instead of glyphnames. ' + + 'E.g. "\\u2126" instead of "\\Omega"') + + argparser.add_argument( + '-prefix', dest='prefix', metavar='<text>', type=str, + help='Text to append before each pair') + + argparser.add_argument( + '-suffix', dest='suffix', metavar='<text>', type=str, + help='Text to append after each pair') + + argparser.add_argument( + '-no-prefix-autocase', dest='noPrefixAutocase', + action='store_const', const=True, default=False, + help='Do not convert -prefix and -suffix to match case') + + argparser.add_argument( + '-all-in-groups', dest='includeAllInGroup', + action='store_const', const=True, default=False, + help='Include all glyphs for groups rather than just the first glyph listed.') + + argparser.add_argument( + '-left', dest='asLeft', + action='store_const', const=True, default=False, + help='Only include pairs where the glyphnames are on the left side.') + + argparser.add_argument( + '-right', dest='asRight', + action='store_const', const=True, default=False, + help='Only include pairs where the glyphnames are on the right side.'+ + ' When neither -left or -right is provided, include all pairs.') + + argparser.add_argument( + 'fontPath', metavar='<ufofile>', type=str, help='UFO font source') + + argparser.add_argument( + 'glyphnames', metavar='<glyphname>', type=str, nargs='+', + help='Name of glyphs to generate samples for. '+ + 'You can also provide a Unicode code point using the syntax "U+XXXX"') + + args = argparser.parse_args() + + font = OpenFont(args.fontPath) + + groupsFilename = os.path.join(args.fontPath, 'groups.plist') + kerningFilename = os.path.join(args.fontPath, 'kerning.plist') + + groups = plistlib.readPlist(groupsFilename) # { groupName => [glyphName] } + kerning = plistlib.readPlist(kerningFilename) # { leftName => {rightName => kernVal} } + groupmap = mapGroups(groups) # { glyphname => set(groupname, ...), ... } + + if not args.asLeft and not args.asRight: + args.asLeft = True + args.asRight = True + + # expand any unicode codepoints + glyphnames = [] + for glyphname in args.glyphnames: + if len(glyphname) > 2 and glyphname[:2] == 'U+': + cp = int(glyphname[2:], 16) + ucmap = font.getCharacterMapping() # { 2126: ['Omega', ...], ...} + for glyphname2 in ucmap[cp]: + glyphnames.append(glyphname2) + else: + glyphnames.append(glyphname) + + for glyphname in glyphnames: + if args.asLeft: + samplesForGlyphnameL(font, groups, groupmap, kerning, glyphname, args) + if args.asRight: + samplesForGlyphnameR(font, groups, groupmap, kerning, glyphname, args) + + +main() |