1"""Pen recording operations that can be accessed or replayed.""" 2from fontTools.pens.basePen import AbstractPen, DecomposingPen 3from fontTools.pens.pointPen import AbstractPointPen 4 5 6__all__ = [ 7 "replayRecording", 8 "RecordingPen", 9 "DecomposingRecordingPen", 10 "RecordingPointPen", 11] 12 13 14def replayRecording(recording, pen): 15 """Replay a recording, as produced by RecordingPen or DecomposingRecordingPen, 16 to a pen. 17 18 Note that recording does not have to be produced by those pens. 19 It can be any iterable of tuples of method name and tuple-of-arguments. 20 Likewise, pen can be any objects receiving those method calls. 21 """ 22 for operator,operands in recording: 23 getattr(pen, operator)(*operands) 24 25 26class RecordingPen(AbstractPen): 27 """Pen recording operations that can be accessed or replayed. 28 29 The recording can be accessed as pen.value; or replayed using 30 pen.replay(otherPen). 31 32 Usage example: 33 ============== 34 from fontTools.ttLib import TTFont 35 from fontTools.pens.recordingPen import RecordingPen 36 37 glyph_name = 'dollar' 38 font_path = 'MyFont.otf' 39 40 font = TTFont(font_path) 41 glyphset = font.getGlyphSet() 42 glyph = glyphset[glyph_name] 43 44 pen = RecordingPen() 45 glyph.draw(pen) 46 print(pen.value) 47 """ 48 49 def __init__(self): 50 self.value = [] 51 def moveTo(self, p0): 52 self.value.append(('moveTo', (p0,))) 53 def lineTo(self, p1): 54 self.value.append(('lineTo', (p1,))) 55 def qCurveTo(self, *points): 56 self.value.append(('qCurveTo', points)) 57 def curveTo(self, *points): 58 self.value.append(('curveTo', points)) 59 def closePath(self): 60 self.value.append(('closePath', ())) 61 def endPath(self): 62 self.value.append(('endPath', ())) 63 def addComponent(self, glyphName, transformation): 64 self.value.append(('addComponent', (glyphName, transformation))) 65 def replay(self, pen): 66 replayRecording(self.value, pen) 67 68 69class DecomposingRecordingPen(DecomposingPen, RecordingPen): 70 """ Same as RecordingPen, except that it doesn't keep components 71 as references, but draws them decomposed as regular contours. 72 73 The constructor takes a single 'glyphSet' positional argument, 74 a dictionary of glyph objects (i.e. with a 'draw' method) keyed 75 by thir name. 76 77 >>> class SimpleGlyph(object): 78 ... def draw(self, pen): 79 ... pen.moveTo((0, 0)) 80 ... pen.curveTo((1, 1), (2, 2), (3, 3)) 81 ... pen.closePath() 82 >>> class CompositeGlyph(object): 83 ... def draw(self, pen): 84 ... pen.addComponent('a', (1, 0, 0, 1, -1, 1)) 85 >>> glyphSet = {'a': SimpleGlyph(), 'b': CompositeGlyph()} 86 >>> for name, glyph in sorted(glyphSet.items()): 87 ... pen = DecomposingRecordingPen(glyphSet) 88 ... glyph.draw(pen) 89 ... print("{}: {}".format(name, pen.value)) 90 a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())] 91 b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())] 92 """ 93 # raises KeyError if base glyph is not found in glyphSet 94 skipMissingComponents = False 95 96 97class RecordingPointPen(AbstractPointPen): 98 """PointPen recording operations that can be accessed or replayed. 99 100 The recording can be accessed as pen.value; or replayed using 101 pointPen.replay(otherPointPen). 102 103 Usage example: 104 ============== 105 from defcon import Font 106 from fontTools.pens.recordingPen import RecordingPointPen 107 108 glyph_name = 'a' 109 font_path = 'MyFont.ufo' 110 111 font = Font(font_path) 112 glyph = font[glyph_name] 113 114 pen = RecordingPointPen() 115 glyph.drawPoints(pen) 116 print(pen.value) 117 118 new_glyph = font.newGlyph('b') 119 pen.replay(new_glyph.getPointPen()) 120 """ 121 122 def __init__(self): 123 self.value = [] 124 125 def beginPath(self, **kwargs): 126 self.value.append(("beginPath", (), kwargs)) 127 128 def endPath(self): 129 self.value.append(("endPath", (), {})) 130 131 def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): 132 self.value.append(("addPoint", (pt, segmentType, smooth, name), kwargs)) 133 134 def addComponent(self, baseGlyphName, transformation, **kwargs): 135 self.value.append(("addComponent", (baseGlyphName, transformation), kwargs)) 136 137 def replay(self, pointPen): 138 for operator, args, kwargs in self.value: 139 getattr(pointPen, operator)(*args, **kwargs) 140 141 142if __name__ == "__main__": 143 pen = RecordingPen() 144 pen.moveTo((0, 0)) 145 pen.lineTo((0, 100)) 146 pen.curveTo((50, 75), (60, 50), (50, 25)) 147 pen.closePath() 148 from pprint import pprint 149 pprint(pen.value) 150