import re from fontTools.pens.transformPen import TransformPen from fontTools.misc.transform import Transform from fontTools.pens.reverseContourPen import ReverseContourPen # Directives are glyph-specific post-processing directives for the compiler. # A directive is added to the "note" section of a glyph and takes the # following form: # # !post:DIRECTIVE # # Where DIRECTIVE is the name of a known directive. # This string can appear anywhere in the glyph note. # Directives are _not_ case sensitive but normalized by str.lower(), meaning # that e.g. "removeoverlap" == "RemoveOverlap" == "REMOVEOVERLAP". # knownDirectives = set([ 'removeoverlap', # applies overlap removal (boolean union) 'decompose', # decomposes components ]) _findDirectiveRegEx = re.compile(r'\!post:([^ ]+)', re.I | re.U) def findGlyphDirectives(string): # -> set | None directives = set() if string and len(string) > 0: for directive in _findDirectiveRegEx.findall(string): directive = directive.lower() if directive in knownDirectives: directives.add(directive) else: print( 'unknown glyph directive !post:%s in glyph %s' % (directive, g.name), file=sys.stderr ) return directives def composedGlyphIsTrivial(g): # A trivial glyph is one that does not use components or where component transformation # does not include mirroring (i.e. "flipped"). if g.components and len(g.components) > 0: for c in g.components: # has non-trivial transformation? (i.e. scaled) # Example of optimally trivial transformation: # (1, 0, 0, 1, 0, 0) no scale or offset # Example of scaled transformation matrix: # (-1.0, 0, 0.3311, 1, 1464.0, 0) flipped x axis, sheered and offset # xScale = c.transformation[0] yScale = c.transformation[3] # If glyph is reflected along x or y axes, it won't slant well. if xScale < 0 or yScale < 0: return False return True def decomposeGlyphs(ufos, glyphNamesToDecompose): for ufo in ufos: for glyphname in glyphNamesToDecompose: glyph = ufo[glyphname] _deepCopyContours(ufo, glyph, glyph, Transform()) glyph.clearComponents() def _deepCopyContours(ufo, parent, component, transformation): """Copy contours from component to parent, including nested components.""" for nested in component.components: _deepCopyContours( ufo, parent, ufo[nested.baseGlyph], transformation.transform(nested.transformation) ) if component != parent: pen = TransformPen(parent.getPen(), transformation) # if the transformation has a negative determinant, it will reverse # the contour direction of the component xx, xy, yx, yy = transformation[:4] if xx*yy - xy*yx < 0: pen = ReverseContourPen(pen) component.draw(pen)