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()