Change vertical metrics to make Apple ATS work properly. New ascender value: 2728, new descender value: -680

Includes alternate fix to @thundernixon's PR #146
This commit is contained in:
Rasmus Andersson 2019-03-30 17:00:54 -07:00
parent e1d8712ecd
commit 1dbc8fd053
3 changed files with 1073 additions and 1239 deletions

View file

@ -0,0 +1,110 @@
#MenuTitle: Fixup Vertical Metrics
# -*- coding: utf-8 -*-
font = Glyphs.font
caps = set(font.classes['Uppercase'].code.strip().split(' '))
lowercase = set(font.classes['Lowercase'].code.strip().split(' '))
print(font)
mainMaxDescent = 0
mainMaxDescentGlyph = ""
maxDescent = 0
mainMaxAscent = 0
mainMaxAscentGlyph = ""
maxAscent = 0
typoAscender = 0
typoDescender = 0
for master in font.masters:
ta = max(master.ascender, master.capHeight)
if typoAscender == 0:
typoAscender = ta
elif typoAscender != ta:
raise Error('ascender or capHeight varies with masters; vertical metrics must be same in all masters')
td = master.descender
if typoDescender == 0:
typoDescender = td
elif typoDescender != td:
raise Error('descender or capHeight varies with masters; vertical metrics must be same in all masters')
for glyph in font.glyphs:
if not glyph.export:
continue
layer = glyph.layers[master.id]
# get descender of current layer
descent = layer.bounds.origin.y
# get ascender of current layer
ascent = layer.bounds.size.height + descent
# if descent/ascent of current layer is greater than previous max descents/ascents, update the max descent/ascent
if descent <= maxDescent:
maxDescent = descent
maxDescentGlyph = glyph.name
if ascent >= maxAscent:
maxAscent = ascent
maxAscentGlyph = glyph.name
# get descender of current layer
descent = layer.bounds.origin.y
# get ascender of current layer (total height of layer, subtracting value of descender)
ascent = layer.bounds.size.height + descent
# get maximums of only letters in list vars, for typo and hhea values
if glyph.name in caps and ascent >= mainMaxAscent:
mainMaxAscent = ascent
mainMaxAscentGlyph = glyph.name
if glyph.name in lowercase and descent <= mainMaxDescent:
# if descent/ascent of current layer is greater than previous max descents/ascents, update the max descent/ascent
mainMaxDescent = descent
mainMaxDescentGlyph = glyph.name
# check values for sanity
# print(maxDescentGlyph, maxDescent, maxAscentGlyph, maxAscent)
# make lineGap so that the total of `ascent + descent + lineGap` equals 120% of UPM size
# UPM = font.upm
# totalSize = maxAscent + abs(maxDescent)
# lineGap = int((UPM * 1.2)) - totalSize
# print(UPM, UPM * 1.2, totalSize, lineGap)
## use highest/lowest points to set custom parameters for winAscent and winDescent
## following vertical metric schema from https://github.com/googlefonts/gf-docs/tree/master/VerticalMetrics
font.customParameters["Use Typo Metrics"] = True
# ascenderDelta = max(abs(typoAscender), abs(mainMaxAscent)) - min(abs(typoAscender), abs(mainMaxAscent))
descenderDelta = max(typoDescender, mainMaxDescent) - min(typoDescender, mainMaxDescent)
if descenderDelta == 0:
print('descenderDelta is zero -- no change')
else:
print('descenderDelta:', descenderDelta)
for master in font.masters:
# Win Ascent/Descent = Font bbox yMax/yMin
master.customParameters["winAscent"] = maxAscent
master.customParameters["winDescent"] = abs(maxDescent)
# no/zero line gap
# if "typoLineGap" in master.customParameters:
# del master.customParameters["typoLineGap"]
# if "hheaLineGap" in master.customParameters:
# del master.customParameters["hheaLineGap"]
master.customParameters["typoLineGap"] = 0
master.customParameters["hheaLineGap"] = 0
master.customParameters["typoDescender"] = typoDescender - descenderDelta
master.customParameters["hheaDescender"] = typoDescender - descenderDelta
master.customParameters["typoAscender"] = typoAscender + descenderDelta
master.customParameters["hheaAscender"] = typoAscender + descenderDelta

View file

@ -0,0 +1,183 @@
#MenuTitle: Preflight
# -*- coding: utf-8 -*-
__doc__="""
Checks for bad paths and anchors
"""
import AppKit
Glyphs.clearLog()
Glyphs.showMacroWindow()
mainRunLoop = AppKit.NSRunLoop.mainRunLoop()
_lowerCaseGlyphNames = None
def getLowerCaseGlyphNames():
global _lowerCaseGlyphNames
if _lowerCaseGlyphNames is None:
# TODO: split with regexp to allow more than one space as a separator
_lowerCaseGlyphNames = set(font.classes['Lowercase'].code.strip().split(' '))
return _lowerCaseGlyphNames
def yieldAppMain():
mainRunLoop.runMode_beforeDate_(AppKit.NSRunLoopCommonModes, AppKit.NSDate.new())
def headline(titleString):
print("\n------ %s ------" % titleString.upper())
def log(glyphName, layerName, msg):
if layerName != "":
print("[glyph] %s \t Layer %s: %s." % ( glyphName, layerName, msg ))
elif glyphName != "":
print("[glyph] %s \t - \t %s." % ( glyphName, msg ))
else:
print("[info] %s." % ( msg ))
def masterLayersIterator(font):
for g in font.glyphs:
for master in font.masters:
yield g.layers[master.id], g
yieldAppMain()
def checkForOpenPaths(font):
headline("Checking for open paths")
ok = True
for layer, g in masterLayersIterator(font):
openPathsFound = 0
for path in layer.paths:
if not path.closed:
openPathsFound += 1
if openPathsFound > 0:
ok = False
log(g.name, layer.name, "%d open path(s) found" % openPathsFound)
if ok:
print("OK")
def checkForPathDirections(font):
headline("Checking for path directions")
ok = True
for layer, g in masterLayersIterator(font):
firstPath = layer.paths[0]
if firstPath and firstPath.direction != -1:
ok = False
if len(layer.paths) > 1:
msg = "Bad path order or direction."
else:
msg = "Bad path direction."
log(g.name, layer.name, msg)
if ok:
print("OK")
def checkForPointsOutOfBounds(font):
headline("Checking for nodes out of bounds")
ok = True
for layer, g in masterLayersIterator(font):
nodesOutOfBounds = 0
anchorsOutOfBounds = []
for path in layer.paths:
for n in path.nodes:
if abs(n.x) > 32766 or abs(n.y) > 32766:
nodesOutOfBounds += 1
for a in layer.anchors:
if abs(a.x) > 32766 or abs(a.y) > 32766:
anchorsOutOfBounds.append(a.name)
if nodesOutOfBounds:
ok = False
log(g.name, layer.name, "%d node(s) out of bounds" % nodesOutOfBounds)
if anchorsOutOfBounds:
ok = False
log(g.name, layer.name, "%d anchor(s) out of bounds (%r)" % (
len(anchorsOutOfBounds),
anchorsOutOfBounds
))
if ok:
print("OK")
def checkUnicode(font):
headline("Checking Unicodes")
ok = True
listOfUnicodes = [ (g.name, g.unicode) for g in font.glyphs if g.unicode != None ]
numberOfGlyphs = len(listOfUnicodes)
# glyphsWithoutUnicodes = [ g.name for g in allGlyphs if g.unicode == None ]
# for gName in glyphsWithoutUnicodes:
# log( gName, "", "Warning: No Unicode value set" )
for i in range(numberOfGlyphs - 1):
firstGlyph = listOfUnicodes[i]
for j in range(i+1, numberOfGlyphs):
secondGlyph = listOfUnicodes[j]
if firstGlyph[1] == secondGlyph[1]:
ok = False
log(
"%s & %s" % (firstGlyph[0], secondGlyph[0]),
"-",
"Both glyphs carry same Unicode value %s" % (firstGlyph[1])
)
if ok:
print("OK")
def checkVerticalMetrics(font):
headline("Checking vertical metrics")
ascender = 0
descender = 0
capHeight = 0
lowerCase = getLowerCaseGlyphNames()
ok = True
for master in font.masters:
if ascender == 0:
ascender = master.ascender
elif ascender != master.ascender:
print('ascender varies with masters; vertical metrics must be same in all masters')
ok = False
if capHeight == 0:
capHeight = master.capHeight
elif capHeight != master.capHeight:
print('capHeight varies with masters; vertical metrics must be same in all masters')
ok = False
if descender == 0:
descender = master.descender
elif descender != master.descender:
print('descender varies with masters; vertical metrics must be same in all masters')
ok = False
for master in font.masters:
for glyph in font.glyphs:
if not glyph.export or glyph.name not in lowerCase:
continue
layer = glyph.layers[master.id]
# get ymin of current layer
ymin = layer.bounds.origin.y
if ymin < descender:
ok = False
log(glyph.name, layer.name,
'Warning: lower than descender (ymin=%r, descender=%r)' % (
ymin, descender))
if ok:
print("OK")
font = Glyphs.font
font.disableUpdateInterface()
try:
checkForOpenPaths(font)
checkForPathDirections(font)
checkForPointsOutOfBounds(font)
checkUnicode(font)
checkVerticalMetrics(font)
finally:
font.enableUpdateInterface()

File diff suppressed because one or more lines are too long