xref: /aosp_15_r20/external/fonttools/Lib/fontTools/pens/transformPen.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1from fontTools.pens.filterPen import FilterPen, FilterPointPen
2
3
4__all__ = ["TransformPen", "TransformPointPen"]
5
6
7class TransformPen(FilterPen):
8    """Pen that transforms all coordinates using a Affine transformation,
9    and passes them to another pen.
10    """
11
12    def __init__(self, outPen, transformation):
13        """The 'outPen' argument is another pen object. It will receive the
14        transformed coordinates. The 'transformation' argument can either
15        be a six-tuple, or a fontTools.misc.transform.Transform object.
16        """
17        super(TransformPen, self).__init__(outPen)
18        if not hasattr(transformation, "transformPoint"):
19            from fontTools.misc.transform import Transform
20
21            transformation = Transform(*transformation)
22        self._transformation = transformation
23        self._transformPoint = transformation.transformPoint
24        self._stack = []
25
26    def moveTo(self, pt):
27        self._outPen.moveTo(self._transformPoint(pt))
28
29    def lineTo(self, pt):
30        self._outPen.lineTo(self._transformPoint(pt))
31
32    def curveTo(self, *points):
33        self._outPen.curveTo(*self._transformPoints(points))
34
35    def qCurveTo(self, *points):
36        if points[-1] is None:
37            points = self._transformPoints(points[:-1]) + [None]
38        else:
39            points = self._transformPoints(points)
40        self._outPen.qCurveTo(*points)
41
42    def _transformPoints(self, points):
43        transformPoint = self._transformPoint
44        return [transformPoint(pt) for pt in points]
45
46    def closePath(self):
47        self._outPen.closePath()
48
49    def endPath(self):
50        self._outPen.endPath()
51
52    def addComponent(self, glyphName, transformation):
53        transformation = self._transformation.transform(transformation)
54        self._outPen.addComponent(glyphName, transformation)
55
56
57class TransformPointPen(FilterPointPen):
58    """PointPen that transforms all coordinates using a Affine transformation,
59    and passes them to another PointPen.
60
61    >>> from fontTools.pens.recordingPen import RecordingPointPen
62    >>> rec = RecordingPointPen()
63    >>> pen = TransformPointPen(rec, (2, 0, 0, 2, -10, 5))
64    >>> v = iter(rec.value)
65    >>> pen.beginPath(identifier="contour-0")
66    >>> next(v)
67    ('beginPath', (), {'identifier': 'contour-0'})
68    >>> pen.addPoint((100, 100), "line")
69    >>> next(v)
70    ('addPoint', ((190, 205), 'line', False, None), {})
71    >>> pen.endPath()
72    >>> next(v)
73    ('endPath', (), {})
74    >>> pen.addComponent("a", (1, 0, 0, 1, -10, 5), identifier="component-0")
75    >>> next(v)
76    ('addComponent', ('a', <Transform [2 0 0 2 -30 15]>), {'identifier': 'component-0'})
77    """
78
79    def __init__(self, outPointPen, transformation):
80        """The 'outPointPen' argument is another point pen object.
81        It will receive the transformed coordinates.
82        The 'transformation' argument can either be a six-tuple, or a
83        fontTools.misc.transform.Transform object.
84        """
85        super().__init__(outPointPen)
86        if not hasattr(transformation, "transformPoint"):
87            from fontTools.misc.transform import Transform
88
89            transformation = Transform(*transformation)
90        self._transformation = transformation
91        self._transformPoint = transformation.transformPoint
92
93    def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
94        self._outPen.addPoint(
95            self._transformPoint(pt), segmentType, smooth, name, **kwargs
96        )
97
98    def addComponent(self, baseGlyphName, transformation, **kwargs):
99        transformation = self._transformation.transform(transformation)
100        self._outPen.addComponent(baseGlyphName, transformation, **kwargs)
101
102
103if __name__ == "__main__":
104    from fontTools.pens.basePen import _TestPen
105
106    pen = TransformPen(_TestPen(None), (2, 0, 0.5, 2, -10, 0))
107    pen.moveTo((0, 0))
108    pen.lineTo((0, 100))
109    pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
110    pen.closePath()
111