aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/pens/recordingPen.py
blob: b25011d6dd115080a2f407e0759a9276f627bbbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
"""Pen recording operations that can be accessed or replayed."""
from fontTools.misc.py23 import *
from fontTools.pens.basePen import AbstractPen, DecomposingPen
from fontTools.pens.pointPen import AbstractPointPen


__all__ = [
	"replayRecording",
	"RecordingPen",
	"DecomposingRecordingPen",
	"RecordingPointPen",
]


def replayRecording(recording, pen):
	"""Replay a recording, as produced by RecordingPen or DecomposingRecordingPen,
	to a pen.

	Note that recording does not have to be produced by those pens.
	It can be any iterable of tuples of method name and tuple-of-arguments.
	Likewise, pen can be any objects receiving those method calls.
	"""
	for operator,operands in recording:
		getattr(pen, operator)(*operands)


class RecordingPen(AbstractPen):
	"""Pen recording operations that can be accessed or replayed.

	The recording can be accessed as pen.value; or replayed using
	pen.replay(otherPen).

	Usage example:
	==============
	from fontTools.ttLib import TTFont
	from fontTools.pens.recordingPen import RecordingPen

	glyph_name = 'dollar'
	font_path = 'MyFont.otf'

	font = TTFont(font_path)
	glyphset = font.getGlyphSet()
	glyph = glyphset[glyph_name]

	pen = RecordingPen()
	glyph.draw(pen)
	print(pen.value)
	"""

	def __init__(self):
		self.value = []
	def moveTo(self, p0):
		self.value.append(('moveTo', (p0,)))
	def lineTo(self, p1):
		self.value.append(('lineTo', (p1,)))
	def qCurveTo(self, *points):
		self.value.append(('qCurveTo', points))
	def curveTo(self, *points):
		self.value.append(('curveTo', points))
	def closePath(self):
		self.value.append(('closePath', ()))
	def endPath(self):
		self.value.append(('endPath', ()))
	def addComponent(self, glyphName, transformation):
		self.value.append(('addComponent', (glyphName, transformation)))
	def replay(self, pen):
		replayRecording(self.value, pen)


class DecomposingRecordingPen(DecomposingPen, RecordingPen):
	""" Same as RecordingPen, except that it doesn't keep components
	as references, but draws them decomposed as regular contours.

	The constructor takes a single 'glyphSet' positional argument,
	a dictionary of glyph objects (i.e. with a 'draw' method) keyed
	by thir name.

	>>> class SimpleGlyph(object):
	...     def draw(self, pen):
	...         pen.moveTo((0, 0))
	...         pen.curveTo((1, 1), (2, 2), (3, 3))
	...         pen.closePath()
	>>> class CompositeGlyph(object):
	...     def draw(self, pen):
	...         pen.addComponent('a', (1, 0, 0, 1, -1, 1))
	>>> glyphSet = {'a': SimpleGlyph(), 'b': CompositeGlyph()}
	>>> for name, glyph in sorted(glyphSet.items()):
	...     pen = DecomposingRecordingPen(glyphSet)
	...     glyph.draw(pen)
	...     print("{}: {}".format(name, pen.value))
	a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
	b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
	"""
	# raises KeyError if base glyph is not found in glyphSet
	skipMissingComponents = False


class RecordingPointPen(AbstractPointPen):
	"""PointPen recording operations that can be accessed or replayed.

	The recording can be accessed as pen.value; or replayed using
	pointPen.replay(otherPointPen).

	Usage example:
	==============
	from defcon import Font
	from fontTools.pens.recordingPen import RecordingPointPen

	glyph_name = 'a'
	font_path = 'MyFont.ufo'

	font = Font(font_path)
	glyph = font[glyph_name]

	pen = RecordingPointPen()
	glyph.drawPoints(pen)
	print(pen.value)

	new_glyph = font.newGlyph('b')
	pen.replay(new_glyph.getPointPen())
	"""

	def __init__(self):
		self.value = []

	def beginPath(self, **kwargs):
		self.value.append(("beginPath", (), kwargs))

	def endPath(self):
		self.value.append(("endPath", (), {}))

	def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
		self.value.append(("addPoint", (pt, segmentType, smooth, name), kwargs))

	def addComponent(self, baseGlyphName, transformation, **kwargs):
		self.value.append(("addComponent", (baseGlyphName, transformation), kwargs))

	def replay(self, pointPen):
		for operator, args, kwargs in self.value:
			getattr(pointPen, operator)(*args, **kwargs)


if __name__ == "__main__":
	from fontTools.pens.basePen import _TestPen
	pen = RecordingPen()
	pen.moveTo((0, 0))
	pen.lineTo((0, 100))
	pen.curveTo((50, 75), (60, 50), (50, 25))
	pen.closePath()
	from pprint import pprint
	pprint(pen.value)