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