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