summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorRasmus Andersson <rasmus@figma.com>2019-03-31 03:00:54 +0300
committerRasmus Andersson <rasmus@figma.com>2019-03-31 03:03:54 +0300
commit1dbc8fd0538fa646248cf7a077a8ef98ca726044 (patch)
tree5f9d63b6aef1a7719fc924c423a2eba0689d616d /misc
parente1d8712ecd1cc295a69b524a9267b2a9f983436e (diff)
downloadinter-1dbc8fd0538fa646248cf7a077a8ef98ca726044.tar.xz
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
Diffstat (limited to 'misc')
-rw-r--r--misc/glyphs-scripts/fixup-vertical-metrics.py110
-rw-r--r--misc/glyphs-scripts/preflight.py183
2 files changed, 293 insertions, 0 deletions
diff --git a/misc/glyphs-scripts/fixup-vertical-metrics.py b/misc/glyphs-scripts/fixup-vertical-metrics.py
new file mode 100644
index 000000000..a7ad651e9
--- /dev/null
+++ b/misc/glyphs-scripts/fixup-vertical-metrics.py
@@ -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
diff --git a/misc/glyphs-scripts/preflight.py b/misc/glyphs-scripts/preflight.py
new file mode 100644
index 000000000..55f8e3b62
--- /dev/null
+++ b/misc/glyphs-scripts/preflight.py
@@ -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()