diff options
Diffstat (limited to 'Snippets/statShape.py')
-rw-r--r-- | Snippets/statShape.py | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/Snippets/statShape.py b/Snippets/statShape.py new file mode 100644 index 00000000..e0b0d69e --- /dev/null +++ b/Snippets/statShape.py @@ -0,0 +1,85 @@ +"""Draw statistical shape of a glyph as an ellipse.""" + +from fontTools.ttLib import TTFont +from fontTools.pens.recordingPen import RecordingPen +from fontTools.pens.cairoPen import CairoPen +from fontTools.pens.statisticsPen import StatisticsPen +import cairo +import math +import sys + + +font = TTFont(sys.argv[1]) +unicode = sys.argv[2] + +cmap = font["cmap"].getBestCmap() +gid = cmap[ord(unicode)] + +hhea = font["hhea"] +glyphset = font.getGlyphSet() +with cairo.SVGSurface( + "example.svg", hhea.advanceWidthMax, hhea.ascent - hhea.descent +) as surface: + context = cairo.Context(surface) + context.translate(0, +font["hhea"].ascent) + context.scale(1, -1) + + glyph = glyphset[gid] + + recording = RecordingPen() + glyph.draw(recording) + + context.translate((hhea.advanceWidthMax - glyph.width) * 0.5, 0) + + pen = CairoPen(glyphset, context) + glyph.draw(pen) + context.fill() + + stats = StatisticsPen(glyphset) + glyph.draw(stats) + + # https://cookierobotics.com/007/ + a = stats.varianceX + b = stats.covariance + c = stats.varianceY + delta = (((a - c) * 0.5) ** 2 + b * b) ** 0.5 + lambda1 = (a + c) * 0.5 + delta # Major eigenvalue + lambda2 = (a + c) * 0.5 - delta # Minor eigenvalue + theta = math.atan2(lambda1 - a, b) if b != 0 else (math.pi * 0.5 if a < c else 0) + mult = 4 # Empirical by drawing '.' + transform = cairo.Matrix() + transform.translate(stats.meanX, stats.meanY) + transform.rotate(theta) + transform.scale(math.sqrt(lambda1), math.sqrt(lambda2)) + transform.scale(mult, mult) + + ellipse_area = math.sqrt(lambda1) * math.sqrt(lambda2) * math.pi / 4 * mult * mult + + if stats.area: + context.save() + context.set_line_cap(cairo.LINE_CAP_ROUND) + context.transform(transform) + context.move_to(0, 0) + context.line_to(0, 0) + context.set_line_width(1) + context.set_source_rgba(1, 0, 0, abs(stats.area / ellipse_area)) + context.stroke() + context.restore() + + context.save() + context.set_line_cap(cairo.LINE_CAP_ROUND) + context.set_source_rgb(0.8, 0, 0) + context.translate(stats.meanX, stats.meanY) + + context.move_to(0, 0) + context.line_to(0, 0) + context.set_line_width(15) + context.stroke() + + context.transform(cairo.Matrix(1, 0, stats.slant, 1, 0, 0)) + context.move_to(0, -stats.meanY + font["hhea"].ascent) + context.line_to(0, -stats.meanY + font["hhea"].descent) + context.set_line_width(5) + context.stroke() + + context.restore() |