Skip to content

Instantly share code, notes, and snippets.

Created February 1, 2019 15:42
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simoncozens/4f506dacb99d8e4b1f96f5fbfc102fa5 to your computer and use it in GitHub Desktop.
Save simoncozens/4f506dacb99d8e4b1f96f5fbfc102fa5 to your computer and use it in GitHub Desktop.
HT LetterKerner, first draft
#MenuTitle: HT LetterKerner
# Put this in your HTLetterSpacer script directory so it can find
# the library.
# Todo:
# Does not handle whole-font yet.
# Does not know or care about kerning groups.
# The reference glyph should be adjusted based on the letter pair
# - "n" works well for lowercase-lowercase
# - "H" works well for uppercase-uppercase
# - what to do for everything else?
from HT_LetterSpacer_script import HTLetterspacerLib, marginList, area
engine = HTLetterspacerLib()
# Overall counter depth in percent of an x-width. Adjust for tighter/looser.
engine.paramDepth = 20
referenceGlyph = "H"
font = Glyphs.font
selectedLayers = Glyphs.font.selectedLayers
layerID = selectedLayers[0].associatedMasterId
master = font.masters[layerID]
engine.upm = font.upm
engine.angle = master.italicAngle
engine.xHeight = master.xHeight
referenceLayer = font.glyphs[referenceGlyph].layers[layerID]
def closeOpenCounters(margin):
maxY = max([p.y for p in margin])
initPoint = NSMakePoint(0, 0)
endPoint = NSMakePoint(0, maxY)
margin.insert(0, initPoint)
return margin
from itertools import izip, tee
def pairwise(iterable):
a, b = tee(iterable)
next(b, None)
return izip(a, b)
for left,right in pairwise(selectedLayers):
_, rFullMargin = marginList(left)
lFullMargin, _ = marginList(right)
minY = max(NSMinY(left.bounds),NSMinY(right.bounds))
maxY = min(NSMaxY(left.bounds),NSMaxY(right.bounds))
def processMargins(layer, minY, maxY):
l,r = marginList(layer)
for p in r:
p.x = layer.width - p.x # X coord now represents sidebearing size at Y point
l = filter(lambda p: p.y >= minY and p.y <= maxY, l)
r = filter(lambda p: p.y >= minY and p.y <= maxY, r)
l,r = engine.deSlant(l), engine.deSlant(r)
# Trim to depth
maxdepth = engine.xHeight * engine.paramDepth / 100
l = [ NSMakePoint(min(p.x, maxdepth), p.y) for p in l]
r = [ NSMakePoint(min(p.x, maxdepth), p.y) for p in r]
# We should probably also do a diagonise thing here. Later.
l = closeOpenCounters(l)
r = closeOpenCounters(r)
l,r = engine.slant(l), engine.slant(r)
return l,r
lRefMargin, rRefMargin = processMargins(referenceLayer, minY, maxY)
_, rMargin = processMargins(left, minY, maxY)
lMargin, _ = processMargins(right, minY, maxY)
referenceArea = area(lRefMargin) + area(rRefMargin)
currentArea = area(lMargin) + area(rMargin)
kern = ((referenceArea-currentArea)/(maxY-minY))
joint = []
for r,l in zip(rFullMargin, lFullMargin):
r.x = left.width - r.x
joint.append(r.x + l.x)
clash_avoidance = left.RSB + right.LSB - min(joint)
kern = int(max(kern,clash_avoidance))
if kern < -5 or kern > 5:
font.setKerningForPair(,,, kern)
Copy link

Hi Simon! This is great!

I know that you made amazing work for Arabic kerning and mark positioning.
I am exploring some alternatives for helping us to make a consistent kerning of a large family (Latin, 16 masters).
Do you consider this GIST a good starting point, or do you have some other repo we can take a look at?

Thanks a lot for the magic :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment