Adds misc/kernsample.py
This commit is contained in:
parent
e867bb7bd7
commit
9209081fe9
2 changed files with 180 additions and 0 deletions
|
|
@ -228,6 +228,45 @@ You can now visit `http://localhost:2015/lab/`.
|
||||||
After you rebuild some font files, reload the web page to refresh fonts.
|
After you rebuild some font files, reload the web page to refresh fonts.
|
||||||
|
|
||||||
|
|
||||||
|
### Kerning
|
||||||
|
|
||||||
|
Kerning is the concept of harmony in the pace of characters, defined as a set of distances
|
||||||
|
between specific character pairs, like "A" & "c".
|
||||||
|
Good kerning makes words more readable. Oftentimes this means that when adjusting kerning,
|
||||||
|
you have to look at whole words and sentences when adjusting the kerning of a pair, since
|
||||||
|
the spacing between two characters should work in harmony with the spacing of all other characters.
|
||||||
|
|
||||||
|
All major font editors provide user interfaces for previewing and adjusting kerning.
|
||||||
|
|
||||||
|
When adding or adjusting kerning:
|
||||||
|
|
||||||
|
- Make sure to use kerning groups (`src/Inter-UI-*.ufo/groups.plist`)
|
||||||
|
|
||||||
|
- If a glyphname is missing in kerning groups, define a new group for it.
|
||||||
|
Group naming scheme is: `@KERN_<DIRECTION>_<NAME>` where `<DIRECTION>`
|
||||||
|
is either `LEFT` or `RIGHT`, depending on if the group represents the left-hand side
|
||||||
|
of a pair or the right-hand side. `<NAME>` is the name of a glyph that most commonly
|
||||||
|
represents the group. For instance, for all glyphs that has a left-side shape similar
|
||||||
|
to "c", like "d", the group name is "c". This makes it easy to test kerning of groups
|
||||||
|
by just using the `<NAME>` part in previews.
|
||||||
|
|
||||||
|
- Try to submit image samples of kerning adjustments with your pull requests whenever
|
||||||
|
feasible.
|
||||||
|
|
||||||
|
The script `misc/kernsample.py` is helpful in generating samples for all existing
|
||||||
|
right-hand side characters given a left-hand side glyphname.
|
||||||
|
|
||||||
|
```txt
|
||||||
|
$ misc/kernsample.py src/Inter-UI-Black.ufo P -suffix MOR
|
||||||
|
PAMOR P/AE MOR PJMOR PXMOR PYMOR PZMOR P/ae mor P/ampersand mor P/backslash mor P/dzcaron mor P/eightsub mor P/ellipsis mor Pfmor P/four mor P/guilsinglleft mor P/idieresisacute mor P/periodcentered mor P/quotedblbase mor Psmor P/seven mor P/slash mor Ptmor P/two mor P/underscore mor Pymor
|
||||||
|
```
|
||||||
|
|
||||||
|
Type `misc/kernsample.py -h` for help on how to use the program.
|
||||||
|
|
||||||
|
This only includes existing kerning and is thus only useful for adjustments.
|
||||||
|
Additions must still be done manually.
|
||||||
|
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
> Do I need RoboFont?
|
> Do I need RoboFont?
|
||||||
|
|
|
||||||
141
misc/kernsample.py
Executable file
141
misc/kernsample.py
Executable file
|
|
@ -0,0 +1,141 @@
|
||||||
|
#!/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
|
||||||
|
|
||||||
|
|
||||||
|
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 _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 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 samplesForGlyphname(font, groups, groupmap, kerning, glyphname, args):
|
||||||
|
leftGlyph = font[glyphname] # verify it's present
|
||||||
|
rightnames = set()
|
||||||
|
|
||||||
|
_addRightnames(groups, kerning, glyphname, rightnames, includeAll=args.includeAllInGroup)
|
||||||
|
|
||||||
|
if glyphname in groupmap:
|
||||||
|
for groupname in groupmap[glyphname]:
|
||||||
|
_addRightnames(groups, kerning, groupname, rightnames, includeAll=args.includeAllInGroup)
|
||||||
|
|
||||||
|
rightnames = sorted(rightnames)
|
||||||
|
|
||||||
|
out = []
|
||||||
|
if args.formatAsUnicode:
|
||||||
|
left = '\\u%04X' % leftGlyph.unicode
|
||||||
|
for rightname in rightnames:
|
||||||
|
if rightname in font:
|
||||||
|
rightGlyph = font[rightname]
|
||||||
|
if rightGlyph.unicode is not None:
|
||||||
|
out.append('%s\\u%04X' % (left, rightGlyph.unicode))
|
||||||
|
else:
|
||||||
|
left = fmtGlyphname(glyphname, leftGlyph)
|
||||||
|
suffix_uc = ''
|
||||||
|
suffix_lc = ''
|
||||||
|
if 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 rightname in rightnames:
|
||||||
|
if rightname in font:
|
||||||
|
rightGlyph = None
|
||||||
|
if len(rightname) == 1:
|
||||||
|
rightGlyph = font[rightname]
|
||||||
|
suffix = suffix_lc
|
||||||
|
if unicode(rightname[0]).isupper():
|
||||||
|
suffix = suffix_uc
|
||||||
|
out.append('%s%s%s' % (left, fmtGlyphname(rightname, rightGlyph), suffix))
|
||||||
|
|
||||||
|
print(' '.join(out))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
||||||
|
'-suffix', dest='suffix', metavar='<text>', type=str,
|
||||||
|
help='Text to append after each pair')
|
||||||
|
|
||||||
|
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(
|
||||||
|
'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()
|
||||||
|
dryRun = args.dryRun
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
samplesForGlyphname(font, groups, groupmap, kerning, glyphname, args)
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
Reference in a new issue