• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from fontTools.pens.basePen import AbstractPen
2from fontTools.pens.pointPen import AbstractPointPen
3from fontTools.pens.recordingPen import RecordingPen
4
5
6class _PassThruComponentsMixin(object):
7
8    def addComponent(self, glyphName, transformation, **kwargs):
9        self._outPen.addComponent(glyphName, transformation, **kwargs)
10
11
12class FilterPen(_PassThruComponentsMixin, AbstractPen):
13
14    """ Base class for pens that apply some transformation to the coordinates
15    they receive and pass them to another pen.
16
17    You can override any of its methods. The default implementation does
18    nothing, but passes the commands unmodified to the other pen.
19
20    >>> from fontTools.pens.recordingPen import RecordingPen
21    >>> rec = RecordingPen()
22    >>> pen = FilterPen(rec)
23    >>> v = iter(rec.value)
24
25    >>> pen.moveTo((0, 0))
26    >>> next(v)
27    ('moveTo', ((0, 0),))
28
29    >>> pen.lineTo((1, 1))
30    >>> next(v)
31    ('lineTo', ((1, 1),))
32
33    >>> pen.curveTo((2, 2), (3, 3), (4, 4))
34    >>> next(v)
35    ('curveTo', ((2, 2), (3, 3), (4, 4)))
36
37    >>> pen.qCurveTo((5, 5), (6, 6), (7, 7), (8, 8))
38    >>> next(v)
39    ('qCurveTo', ((5, 5), (6, 6), (7, 7), (8, 8)))
40
41    >>> pen.closePath()
42    >>> next(v)
43    ('closePath', ())
44
45    >>> pen.moveTo((9, 9))
46    >>> next(v)
47    ('moveTo', ((9, 9),))
48
49    >>> pen.endPath()
50    >>> next(v)
51    ('endPath', ())
52
53    >>> pen.addComponent('foo', (1, 0, 0, 1, 0, 0))
54    >>> next(v)
55    ('addComponent', ('foo', (1, 0, 0, 1, 0, 0)))
56    """
57
58    def __init__(self, outPen):
59        self._outPen = outPen
60
61    def moveTo(self, pt):
62        self._outPen.moveTo(pt)
63
64    def lineTo(self, pt):
65        self._outPen.lineTo(pt)
66
67    def curveTo(self, *points):
68        self._outPen.curveTo(*points)
69
70    def qCurveTo(self, *points):
71        self._outPen.qCurveTo(*points)
72
73    def closePath(self):
74        self._outPen.closePath()
75
76    def endPath(self):
77        self._outPen.endPath()
78
79
80class ContourFilterPen(_PassThruComponentsMixin, RecordingPen):
81    """A "buffered" filter pen that accumulates contour data, passes
82    it through a ``filterContour`` method when the contour is closed or ended,
83    and finally draws the result with the output pen.
84
85    Components are passed through unchanged.
86    """
87
88    def __init__(self, outPen):
89        super(ContourFilterPen, self).__init__()
90        self._outPen = outPen
91
92    def closePath(self):
93        super(ContourFilterPen, self).closePath()
94        self._flushContour()
95
96    def endPath(self):
97        super(ContourFilterPen, self).endPath()
98        self._flushContour()
99
100    def _flushContour(self):
101        result = self.filterContour(self.value)
102        if result is not None:
103            self.value = result
104        self.replay(self._outPen)
105        self.value = []
106
107    def filterContour(self, contour):
108        """Subclasses must override this to perform the filtering.
109
110        The contour is a list of pen (operator, operands) tuples.
111        Operators are strings corresponding to the AbstractPen methods:
112        "moveTo", "lineTo", "curveTo", "qCurveTo", "closePath" and
113        "endPath". The operands are the positional arguments that are
114        passed to each method.
115
116        If the method doesn't return a value (i.e. returns None), it's
117        assumed that the argument was modified in-place.
118        Otherwise, the return value is drawn with the output pen.
119        """
120        return  # or return contour
121
122
123class FilterPointPen(_PassThruComponentsMixin, AbstractPointPen):
124    """ Baseclass for point pens that apply some transformation to the
125    coordinates they receive and pass them to another point pen.
126
127    You can override any of its methods. The default implementation does
128    nothing, but passes the commands unmodified to the other pen.
129
130    >>> from fontTools.pens.recordingPen import RecordingPointPen
131    >>> rec = RecordingPointPen()
132    >>> pen = FilterPointPen(rec)
133    >>> v = iter(rec.value)
134    >>> pen.beginPath(identifier="abc")
135    >>> next(v)
136    ('beginPath', (), {'identifier': 'abc'})
137    >>> pen.addPoint((1, 2), "line", False)
138    >>> next(v)
139    ('addPoint', ((1, 2), 'line', False, None), {})
140    >>> pen.addComponent("a", (2, 0, 0, 2, 10, -10), identifier="0001")
141    >>> next(v)
142    ('addComponent', ('a', (2, 0, 0, 2, 10, -10)), {'identifier': '0001'})
143    >>> pen.endPath()
144    >>> next(v)
145    ('endPath', (), {})
146    """
147
148    def __init__(self, outPointPen):
149        self._outPen = outPointPen
150
151    def beginPath(self, **kwargs):
152        self._outPen.beginPath(**kwargs)
153
154    def endPath(self):
155        self._outPen.endPath()
156
157    def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
158        self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
159