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 For example::
62
63 >>> from fontTools.pens.recordingPen import RecordingPointPen
64 >>> rec = RecordingPointPen()
65 >>> pen = TransformPointPen(rec, (2, 0, 0, 2, -10, 5))
66 >>> v = iter(rec.value)
67 >>> pen.beginPath(identifier="contour-0")
68 >>> next(v)
69 ('beginPath', (), {'identifier': 'contour-0'})
70
71 >>> pen.addPoint((100, 100), "line")
72 >>> next(v)
73 ('addPoint', ((190, 205), 'line', False, None), {})
74
75 >>> pen.endPath()
76 >>> next(v)
77 ('endPath', (), {})
78
79 >>> pen.addComponent("a", (1, 0, 0, 1, -10, 5), identifier="component-0")
80 >>> next(v)
81 ('addComponent', ('a', <Transform [2 0 0 2 -30 15]>), {'identifier': 'component-0'})
82 """
83
84 def __init__(self, outPointPen, transformation):
85 """The 'outPointPen' argument is another point pen object.
86 It will receive the transformed coordinates.
87 The 'transformation' argument can either be a six-tuple, or a
88 fontTools.misc.transform.Transform object.
89 """
90 super().__init__(outPointPen)
91 if not hasattr(transformation, "transformPoint"):
92 from fontTools.misc.transform import Transform
93
94 transformation = Transform(*transformation)
95 self._transformation = transformation
96 self._transformPoint = transformation.transformPoint
97
98 def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
99 self._outPen.addPoint(
100 self._transformPoint(pt), segmentType, smooth, name, **kwargs
101 )
102
103 def addComponent(self, baseGlyphName, transformation, **kwargs):
104 transformation = self._transformation.transform(transformation)
105 self._outPen.addComponent(baseGlyphName, transformation, **kwargs)
106
107
108if __name__ == "__main__":
109 from fontTools.pens.basePen import _TestPen
110
111 pen = TransformPen(_TestPen(None), (2, 0, 0.5, 2, -10, 0))
112 pen.moveTo((0, 0))
113 pen.lineTo((0, 100))
114 pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
115 pen.closePath()