1*e1fe3e4aSElliott Hughes"""Draw statistical shape of a glyph as an ellipse.""" 2*e1fe3e4aSElliott Hughes 3*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont 4*e1fe3e4aSElliott Hughesfrom fontTools.pens.recordingPen import RecordingPen 5*e1fe3e4aSElliott Hughesfrom fontTools.pens.cairoPen import CairoPen 6*e1fe3e4aSElliott Hughesfrom fontTools.pens.statisticsPen import StatisticsPen 7*e1fe3e4aSElliott Hughesimport cairo 8*e1fe3e4aSElliott Hughesimport math 9*e1fe3e4aSElliott Hughesimport sys 10*e1fe3e4aSElliott Hughes 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott Hughesfont = TTFont(sys.argv[1]) 13*e1fe3e4aSElliott Hughesunicode = sys.argv[2] 14*e1fe3e4aSElliott Hughes 15*e1fe3e4aSElliott Hughescmap = font["cmap"].getBestCmap() 16*e1fe3e4aSElliott Hughesgid = cmap[ord(unicode)] 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hugheshhea = font["hhea"] 19*e1fe3e4aSElliott Hughesglyphset = font.getGlyphSet() 20*e1fe3e4aSElliott Hugheswith cairo.SVGSurface( 21*e1fe3e4aSElliott Hughes "example.svg", hhea.advanceWidthMax, hhea.ascent - hhea.descent 22*e1fe3e4aSElliott Hughes) as surface: 23*e1fe3e4aSElliott Hughes context = cairo.Context(surface) 24*e1fe3e4aSElliott Hughes context.translate(0, +font["hhea"].ascent) 25*e1fe3e4aSElliott Hughes context.scale(1, -1) 26*e1fe3e4aSElliott Hughes 27*e1fe3e4aSElliott Hughes glyph = glyphset[gid] 28*e1fe3e4aSElliott Hughes 29*e1fe3e4aSElliott Hughes recording = RecordingPen() 30*e1fe3e4aSElliott Hughes glyph.draw(recording) 31*e1fe3e4aSElliott Hughes 32*e1fe3e4aSElliott Hughes context.translate((hhea.advanceWidthMax - glyph.width) * 0.5, 0) 33*e1fe3e4aSElliott Hughes 34*e1fe3e4aSElliott Hughes pen = CairoPen(glyphset, context) 35*e1fe3e4aSElliott Hughes glyph.draw(pen) 36*e1fe3e4aSElliott Hughes context.fill() 37*e1fe3e4aSElliott Hughes 38*e1fe3e4aSElliott Hughes stats = StatisticsPen(glyphset) 39*e1fe3e4aSElliott Hughes glyph.draw(stats) 40*e1fe3e4aSElliott Hughes 41*e1fe3e4aSElliott Hughes # https://cookierobotics.com/007/ 42*e1fe3e4aSElliott Hughes a = stats.varianceX 43*e1fe3e4aSElliott Hughes b = stats.covariance 44*e1fe3e4aSElliott Hughes c = stats.varianceY 45*e1fe3e4aSElliott Hughes delta = (((a - c) * 0.5) ** 2 + b * b) ** 0.5 46*e1fe3e4aSElliott Hughes lambda1 = (a + c) * 0.5 + delta # Major eigenvalue 47*e1fe3e4aSElliott Hughes lambda2 = (a + c) * 0.5 - delta # Minor eigenvalue 48*e1fe3e4aSElliott Hughes theta = math.atan2(lambda1 - a, b) if b != 0 else (math.pi * 0.5 if a < c else 0) 49*e1fe3e4aSElliott Hughes mult = 4 # Empirical by drawing '.' 50*e1fe3e4aSElliott Hughes transform = cairo.Matrix() 51*e1fe3e4aSElliott Hughes transform.translate(stats.meanX, stats.meanY) 52*e1fe3e4aSElliott Hughes transform.rotate(theta) 53*e1fe3e4aSElliott Hughes transform.scale(math.sqrt(lambda1), math.sqrt(lambda2)) 54*e1fe3e4aSElliott Hughes transform.scale(mult, mult) 55*e1fe3e4aSElliott Hughes 56*e1fe3e4aSElliott Hughes ellipse_area = math.sqrt(lambda1) * math.sqrt(lambda2) * math.pi / 4 * mult * mult 57*e1fe3e4aSElliott Hughes 58*e1fe3e4aSElliott Hughes if stats.area: 59*e1fe3e4aSElliott Hughes context.save() 60*e1fe3e4aSElliott Hughes context.set_line_cap(cairo.LINE_CAP_ROUND) 61*e1fe3e4aSElliott Hughes context.transform(transform) 62*e1fe3e4aSElliott Hughes context.move_to(0, 0) 63*e1fe3e4aSElliott Hughes context.line_to(0, 0) 64*e1fe3e4aSElliott Hughes context.set_line_width(1) 65*e1fe3e4aSElliott Hughes context.set_source_rgba(1, 0, 0, abs(stats.area / ellipse_area)) 66*e1fe3e4aSElliott Hughes context.stroke() 67*e1fe3e4aSElliott Hughes context.restore() 68*e1fe3e4aSElliott Hughes 69*e1fe3e4aSElliott Hughes context.save() 70*e1fe3e4aSElliott Hughes context.set_line_cap(cairo.LINE_CAP_ROUND) 71*e1fe3e4aSElliott Hughes context.set_source_rgb(0.8, 0, 0) 72*e1fe3e4aSElliott Hughes context.translate(stats.meanX, stats.meanY) 73*e1fe3e4aSElliott Hughes 74*e1fe3e4aSElliott Hughes context.move_to(0, 0) 75*e1fe3e4aSElliott Hughes context.line_to(0, 0) 76*e1fe3e4aSElliott Hughes context.set_line_width(15) 77*e1fe3e4aSElliott Hughes context.stroke() 78*e1fe3e4aSElliott Hughes 79*e1fe3e4aSElliott Hughes context.transform(cairo.Matrix(1, 0, stats.slant, 1, 0, 0)) 80*e1fe3e4aSElliott Hughes context.move_to(0, -stats.meanY + font["hhea"].ascent) 81*e1fe3e4aSElliott Hughes context.line_to(0, -stats.meanY + font["hhea"].descent) 82*e1fe3e4aSElliott Hughes context.set_line_width(5) 83*e1fe3e4aSElliott Hughes context.stroke() 84*e1fe3e4aSElliott Hughes 85*e1fe3e4aSElliott Hughes context.restore() 86