1from fontTools.misc.roundTools import noRound, otRound 2from fontTools.misc.transform import Transform 3from fontTools.pens.filterPen import FilterPen, FilterPointPen 4 5 6__all__ = ["RoundingPen", "RoundingPointPen"] 7 8 9class RoundingPen(FilterPen): 10 """ 11 Filter pen that rounds point coordinates and component XY offsets to integer. For 12 rounding the component transform values, a separate round function can be passed to 13 the pen. 14 15 >>> from fontTools.pens.recordingPen import RecordingPen 16 >>> recpen = RecordingPen() 17 >>> roundpen = RoundingPen(recpen) 18 >>> roundpen.moveTo((0.4, 0.6)) 19 >>> roundpen.lineTo((1.6, 2.5)) 20 >>> roundpen.qCurveTo((2.4, 4.6), (3.3, 5.7), (4.9, 6.1)) 21 >>> roundpen.curveTo((6.4, 8.6), (7.3, 9.7), (8.9, 10.1)) 22 >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5)) 23 >>> recpen.value == [ 24 ... ('moveTo', ((0, 1),)), 25 ... ('lineTo', ((2, 3),)), 26 ... ('qCurveTo', ((2, 5), (3, 6), (5, 6))), 27 ... ('curveTo', ((6, 9), (7, 10), (9, 10))), 28 ... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10))), 29 ... ] 30 True 31 """ 32 33 def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound): 34 super().__init__(outPen) 35 self.roundFunc = roundFunc 36 self.transformRoundFunc = transformRoundFunc 37 38 def moveTo(self, pt): 39 self._outPen.moveTo((self.roundFunc(pt[0]), self.roundFunc(pt[1]))) 40 41 def lineTo(self, pt): 42 self._outPen.lineTo((self.roundFunc(pt[0]), self.roundFunc(pt[1]))) 43 44 def curveTo(self, *points): 45 self._outPen.curveTo( 46 *((self.roundFunc(x), self.roundFunc(y)) for x, y in points) 47 ) 48 49 def qCurveTo(self, *points): 50 self._outPen.qCurveTo( 51 *((self.roundFunc(x), self.roundFunc(y)) for x, y in points) 52 ) 53 54 def addComponent(self, glyphName, transformation): 55 xx, xy, yx, yy, dx, dy = transformation 56 self._outPen.addComponent( 57 glyphName, 58 Transform( 59 self.transformRoundFunc(xx), 60 self.transformRoundFunc(xy), 61 self.transformRoundFunc(yx), 62 self.transformRoundFunc(yy), 63 self.roundFunc(dx), 64 self.roundFunc(dy), 65 ), 66 ) 67 68 69class RoundingPointPen(FilterPointPen): 70 """ 71 Filter point pen that rounds point coordinates and component XY offsets to integer. 72 For rounding the component scale values, a separate round function can be passed to 73 the pen. 74 75 >>> from fontTools.pens.recordingPen import RecordingPointPen 76 >>> recpen = RecordingPointPen() 77 >>> roundpen = RoundingPointPen(recpen) 78 >>> roundpen.beginPath() 79 >>> roundpen.addPoint((0.4, 0.6), 'line') 80 >>> roundpen.addPoint((1.6, 2.5), 'line') 81 >>> roundpen.addPoint((2.4, 4.6)) 82 >>> roundpen.addPoint((3.3, 5.7)) 83 >>> roundpen.addPoint((4.9, 6.1), 'qcurve') 84 >>> roundpen.endPath() 85 >>> roundpen.addComponent("a", (1.5, 0, 0, 1.5, 10.5, -10.5)) 86 >>> recpen.value == [ 87 ... ('beginPath', (), {}), 88 ... ('addPoint', ((0, 1), 'line', False, None), {}), 89 ... ('addPoint', ((2, 3), 'line', False, None), {}), 90 ... ('addPoint', ((2, 5), None, False, None), {}), 91 ... ('addPoint', ((3, 6), None, False, None), {}), 92 ... ('addPoint', ((5, 6), 'qcurve', False, None), {}), 93 ... ('endPath', (), {}), 94 ... ('addComponent', ('a', (1.5, 0, 0, 1.5, 11, -10)), {}), 95 ... ] 96 True 97 """ 98 99 def __init__(self, outPen, roundFunc=otRound, transformRoundFunc=noRound): 100 super().__init__(outPen) 101 self.roundFunc = roundFunc 102 self.transformRoundFunc = transformRoundFunc 103 104 def addPoint( 105 self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs 106 ): 107 self._outPen.addPoint( 108 (self.roundFunc(pt[0]), self.roundFunc(pt[1])), 109 segmentType=segmentType, 110 smooth=smooth, 111 name=name, 112 identifier=identifier, 113 **kwargs, 114 ) 115 116 def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs): 117 xx, xy, yx, yy, dx, dy = transformation 118 self._outPen.addComponent( 119 baseGlyphName=baseGlyphName, 120 transformation=Transform( 121 self.transformRoundFunc(xx), 122 self.transformRoundFunc(xy), 123 self.transformRoundFunc(yx), 124 self.transformRoundFunc(yy), 125 self.roundFunc(dx), 126 self.roundFunc(dy), 127 ), 128 identifier=identifier, 129 **kwargs, 130 ) 131