Created
February 1, 2019 15:42
-
-
Save simoncozens/4f506dacb99d8e4b1f96f5fbfc102fa5 to your computer and use it in GitHub Desktop.
HT LetterKerner, first draft
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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) | |
margin.append(endPoint) | |
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(font.selectedFontMaster.id, left.parent.name,right.parent.name, kern) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 :)