summaryrefslogtreecommitdiff
path: root/misc/glyphs-scripts/preflight.py
blob: 55f8e3b627b2feab74d6cd4ff1837165b340fa9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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()