Major overhaul, moving from UFO2 to Glyphs and UFO3, plus a brand new and much simpler fontbuild
This commit is contained in:
parent
8c1a4c181e
commit
c833e252c9
5246 changed files with 346953 additions and 163499 deletions
235
misc/tools/kernsample.py
Executable file
235
misc/tools/kernsample.py
Executable file
|
|
@ -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()
|
||||
Reference in a new issue