1"""Pen recording operations that can be accessed or replayed.""" 2from __future__ import print_function, division, absolute_import 3from fontTools.misc.py23 import * 4from fontTools.pens.basePen import AbstractPen, DecomposingPen 5 6 7__all__ = ["replayRecording", "RecordingPen", "DecomposingRecordingPen"] 8 9 10def replayRecording(recording, pen): 11 """Replay a recording, as produced by RecordingPen or DecomposingRecordingPen, 12 to a pen. 13 14 Note that recording does not have to be produced by those pens. 15 It can be any iterable of tuples of method name and tuple-of-arguments. 16 Likewise, pen can be any objects receiving those method calls. 17 """ 18 for operator,operands in recording: 19 getattr(pen, operator)(*operands) 20 21 22class RecordingPen(AbstractPen): 23 """Pen recording operations that can be accessed or replayed. 24 25 The recording can be accessed as pen.value; or replayed using 26 pen.replay(otherPen). 27 28 Usage example: 29 ============== 30 from fontTools.ttLib import TTFont 31 from fontTools.pens.recordingPen import RecordingPen 32 33 glyph_name = 'dollar' 34 font_path = 'MyFont.otf' 35 36 font = TTFont(font_path) 37 glyphset = font.getGlyphSet() 38 glyph = glyphset[glyph_name] 39 40 pen = RecordingPen() 41 glyph.draw(pen) 42 print(pen.value) 43 """ 44 45 def __init__(self): 46 self.value = [] 47 def moveTo(self, p0): 48 self.value.append(('moveTo', (p0,))) 49 def lineTo(self, p1): 50 self.value.append(('lineTo', (p1,))) 51 def qCurveTo(self, *points): 52 self.value.append(('qCurveTo', points)) 53 def curveTo(self, *points): 54 self.value.append(('curveTo', points)) 55 def closePath(self): 56 self.value.append(('closePath', ())) 57 def endPath(self): 58 self.value.append(('endPath', ())) 59 def addComponent(self, glyphName, transformation): 60 self.value.append(('addComponent', (glyphName, transformation))) 61 def replay(self, pen): 62 replayRecording(self.value, pen) 63 64 65class DecomposingRecordingPen(DecomposingPen, RecordingPen): 66 """ Same as RecordingPen, except that it doesn't keep components 67 as references, but draws them decomposed as regular contours. 68 69 The constructor takes a single 'glyphSet' positional argument, 70 a dictionary of glyph objects (i.e. with a 'draw' method) keyed 71 by thir name. 72 73 >>> class SimpleGlyph(object): 74 ... def draw(self, pen): 75 ... pen.moveTo((0, 0)) 76 ... pen.curveTo((1, 1), (2, 2), (3, 3)) 77 ... pen.closePath() 78 >>> class CompositeGlyph(object): 79 ... def draw(self, pen): 80 ... pen.addComponent('a', (1, 0, 0, 1, -1, 1)) 81 >>> glyphSet = {'a': SimpleGlyph(), 'b': CompositeGlyph()} 82 >>> for name, glyph in sorted(glyphSet.items()): 83 ... pen = DecomposingRecordingPen(glyphSet) 84 ... glyph.draw(pen) 85 ... print("{}: {}".format(name, pen.value)) 86 a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())] 87 b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())] 88 """ 89 # raises KeyError if base glyph is not found in glyphSet 90 skipMissingComponents = False 91 92 93if __name__ == "__main__": 94 from fontTools.pens.basePen import _TestPen 95 pen = RecordingPen() 96 pen.moveTo((0, 0)) 97 pen.lineTo((0, 100)) 98 pen.curveTo((50, 75), (60, 50), (50, 25)) 99 pen.closePath() 100 from pprint import pprint 101 pprint(pen.value) 102