File size: 3,687 Bytes
d7728be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Pen calculating area, center of mass, variance and standard-deviation,
covariance and correlation, and slant, of glyph shapes."""
import math
from fontTools.pens.momentsPen import MomentsPen

__all__ = ["StatisticsPen"]


class StatisticsPen(MomentsPen):

    """Pen calculating area, center of mass, variance and
    standard-deviation, covariance and correlation, and slant,
    of glyph shapes.

    Note that all the calculated values are 'signed'. Ie. if the
    glyph shape is self-intersecting, the values are not correct
    (but well-defined). As such, area will be negative if contour
    directions are clockwise.  Moreover, variance might be negative
    if the shapes are self-intersecting in certain ways."""

    def __init__(self, glyphset=None):
        MomentsPen.__init__(self, glyphset=glyphset)
        self.__zero()

    def _closePath(self):
        MomentsPen._closePath(self)
        self.__update()

    def __zero(self):
        self.meanX = 0
        self.meanY = 0
        self.varianceX = 0
        self.varianceY = 0
        self.stddevX = 0
        self.stddevY = 0
        self.covariance = 0
        self.correlation = 0
        self.slant = 0

    def __update(self):

        area = self.area
        if not area:
            self.__zero()
            return

        # Center of mass
        # https://en.wikipedia.org/wiki/Center_of_mass#A_continuous_volume
        self.meanX = meanX = self.momentX / area
        self.meanY = meanY = self.momentY / area

        #  Var(X) = E[X^2] - E[X]^2
        self.varianceX = varianceX = self.momentXX / area - meanX**2
        self.varianceY = varianceY = self.momentYY / area - meanY**2

        self.stddevX = stddevX = math.copysign(abs(varianceX) ** 0.5, varianceX)
        self.stddevY = stddevY = math.copysign(abs(varianceY) ** 0.5, varianceY)

        #  Covariance(X,Y) = ( E[X.Y] - E[X]E[Y] )
        self.covariance = covariance = self.momentXY / area - meanX * meanY

        #  Correlation(X,Y) = Covariance(X,Y) / ( stddev(X) * stddev(Y) )
        # https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient
        if stddevX * stddevY == 0:
            correlation = float("NaN")
        else:
            correlation = covariance / (stddevX * stddevY)
        self.correlation = correlation if abs(correlation) > 1e-3 else 0

        slant = covariance / varianceY if varianceY != 0 else float("NaN")
        self.slant = slant if abs(slant) > 1e-3 else 0


def _test(glyphset, upem, glyphs):
    from fontTools.pens.transformPen import TransformPen
    from fontTools.misc.transform import Scale

    print("upem", upem)

    for glyph_name in glyphs:
        print()
        print("glyph:", glyph_name)
        glyph = glyphset[glyph_name]
        pen = StatisticsPen(glyphset=glyphset)
        transformer = TransformPen(pen, Scale(1.0 / upem))
        glyph.draw(transformer)
        for item in [
            "area",
            "momentX",
            "momentY",
            "momentXX",
            "momentYY",
            "momentXY",
            "meanX",
            "meanY",
            "varianceX",
            "varianceY",
            "stddevX",
            "stddevY",
            "covariance",
            "correlation",
            "slant",
        ]:
            print("%s: %g" % (item, getattr(pen, item)))


def main(args):
    if not args:
        return
    filename, glyphs = args[0], args[1:]
    from fontTools.ttLib import TTFont

    font = TTFont(filename)
    if not glyphs:
        glyphs = font.getGlyphOrder()
    _test(font.getGlyphSet(), font["head"].unitsPerEm, glyphs)


if __name__ == "__main__":
    import sys

    main(sys.argv[1:])