diff options
author | Rasmus Andersson <rasmus@figma.com> | 2019-03-31 03:00:54 +0300 |
---|---|---|
committer | Rasmus Andersson <rasmus@figma.com> | 2019-03-31 03:03:54 +0300 |
commit | 1dbc8fd0538fa646248cf7a077a8ef98ca726044 (patch) | |
tree | 5f9d63b6aef1a7719fc924c423a2eba0689d616d /misc | |
parent | e1d8712ecd1cc295a69b524a9267b2a9f983436e (diff) | |
download | inter-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.py | 110 | ||||
-rw-r--r-- | misc/glyphs-scripts/preflight.py | 183 |
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() |