summaryrefslogtreecommitdiff
path: root/misc/fontbuildlib/glyph.py
diff options
context:
space:
mode:
authorRasmus Andersson <rasmus@notion.se>2019-10-23 03:00:58 +0300
committerRasmus Andersson <rasmus@notion.se>2019-10-23 03:00:58 +0300
commitaa7ad2d7a0e255d4052c4999ec62d88c699586ac (patch)
tree54e8c771f6b0c5c09cedc3da4d9173c48968e580 /misc/fontbuildlib/glyph.py
parent9c444dededcb0787d562072a08c909ec463ea4b7 (diff)
downloadinter-aa7ad2d7a0e255d4052c4999ec62d88c699586ac.tar.xz
fontbuild: remove use of fontmake, simplifying things.
Diffstat (limited to 'misc/fontbuildlib/glyph.py')
-rw-r--r--misc/fontbuildlib/glyph.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/misc/fontbuildlib/glyph.py b/misc/fontbuildlib/glyph.py
new file mode 100644
index 000000000..734b881e0
--- /dev/null
+++ b/misc/fontbuildlib/glyph.py
@@ -0,0 +1,86 @@
+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)
+])
+
+
+_findDirectiveRegEx = re.compile(r'\!post:([^ ]+)', re.I | re.U)
+
+
+def findGlyphDirectives(string): # -> set<string> | 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) \ No newline at end of file