basePen.py revision 934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325
1b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr"""fontTools.pens.basePen.py -- Tools and base classes to build pen objects.
2b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
3b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrThe Pen Protocol
4b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
5b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrA Pen is a kind of object that standardizes the way how to "draw" outlines:
6b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrit is a middle man between an outline and a drawing. In other words:
7b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrit is an abstraction for drawing outlines, making sure that outline objects
8b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrdon't need to know the details about how and where they're being drawn, and
9b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrthat drawings don't need to know the details of how outlines are stored.
10b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
11b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrThe most basic pattern is this:
12b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
13b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr    outline.draw(pen)  # 'outline' draws itself onto 'pen'
14b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
15b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrPens can be used to render outlines to the screen, but also to construct
16b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrnew outlines. Eg. an outline object can be both a drawable object (it has a
17b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrdraw() method) as well as a pen itself: you *build* an outline using pen
18b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrmethods.
19b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
20b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrThe AbstractPen class defines the Pen protocol.
21b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
22b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrThe BasePen class is a base implementation useful for drawing pens. See the
23b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrcomments in that class for which methods you need to override.
24b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr"""
25b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
26b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr__all__ = ["AbstractPen", "BasePen"]
27b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
28b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
29b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass AbstractPen:
30b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
31b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
32b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Begin a new sub path, set the current point to 'pt'."""
33b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
34b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
35b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
36b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Draw a straight line."""
37b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
38b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
39b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
40934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""Draw a cubic bezier with an arbitrary number of control points.
41934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr
42934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last point specified is on-curve, all others are off-curve
43934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		(control) points. If the number of control points is > 2, the
44934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		segment is split into multiple bezier segments. This works
45934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		like this:
46b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
47b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		Let n be the number of control points (which is the number of
48b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		arguments to this call minus 1). If n==2, a plain vanilla cubic
49b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		bezier is drawn. If n==1, we fall back to a quadratic segment and
50b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n==0 we draw a straight line. It gets interesting when n>2:
51b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n-1 PostScript-style cubic segments will be drawn as if it were
52b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		one curve.
53b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
54b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		The conversion algorithm used for n>2 is inspired by NURB
55b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		splines, and is conceptually equivalent to the TrueType "implied
56b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		points" principle. See also qCurve().
57b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
58b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
59b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
60b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
61b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Draw a whole string of quadratic curve segments.
62b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
63934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last point specified is on-curve, all others are off-curve
64934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		points.
65934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr
66934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		This method implements TrueType-style curves, breaking up curves
67934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		using 'implied points': between each two consequtive off-curve points,
68934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		there is one implied point exactly in the middle between them.
69b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
70934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last argument (normally the on-curve point) may be None.
71934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		This is to support contours that have NO on-curve points (a rarely
72934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		seen feature of TrueType outlines).
73b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
74b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
75b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
76b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
77b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Close the current sub path."""
78b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pass
79b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
80b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def addComponent(self, glyphName, transformation):
81934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""Add a sub glyph. The 'transformation' argument must be a 6-tuple
82934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		containing an affine transformation, or a Transform object from the
83934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		fontTools.misc.transform module. More precisely: it should be a
84934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		sequence containing 6 numbers.
85934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""
86b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
87b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
88b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
89b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass BasePen(AbstractPen):
90b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
91b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	"""Base class for drawing pens."""
92b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
93b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def __init__(self, glyphSet):
94b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.glyphSet = glyphSet
95b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
96b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
97b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# must override
98b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
99b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
100b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
101b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
102b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
103b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
104b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
105b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, pt1, pt2, pt3):
106b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
107b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
108b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# may override
109b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
110b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
111b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pass
112b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
113b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _qCurveToOne(self, pt1, pt2):
114b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This method implements the basic quadratic curve type. The
115b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		default implementation delegates the work to the cubic curve
116b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		function. Optionally override with a native implementation.
117b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
118b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt0x, pt0y = self.__currentPoint
119b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt1x, pt1y = pt1
120b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt2x, pt2y = pt2
121b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1x = pt0x + 0.66666666666666667 * (pt1x - pt0x)
122b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1y = pt0y + 0.66666666666666667 * (pt1y - pt0y)
123b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2x = pt2x + 0.66666666666666667 * (pt1x - pt2x)
124b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y)
125b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._curveToOne((mid1x, mid1y), (mid2x, mid2y), pt2)
126b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
127b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def addComponent(self, glyphName, transformation):
128b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This default implementation simply transforms the points
129b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		of the base glyph and draws it onto self.
130b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
131b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		from fontTools.pens.transformPen import TransformPen
132b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		tPen = TransformPen(self, transformation)
133b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.glyphSet[glyphName].draw(tPen)
134b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
135b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# don't override
136b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
137b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _getCurrentPoint(self):
138b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Return the current point. This is not part of the public
139b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		interface, yet is useful for subclasses.
140b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
141b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		return self.__currentPoint
142b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
143b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
144b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._closePath()
145b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
146b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
147b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
148b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._moveTo(pt)
149b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
150b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
151b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
152b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._lineTo(pt)
153b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
154b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
155b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
156b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
157b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
158b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n == 2:
159b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# The common case, we have exactly two BCP's, so this is a standard
160b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# cubic bezier.
161b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self._curveToOne(*points)
162b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.__currentPoint = points[-1]
163b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n > 2:
164b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# n is the number of control points; split curve into n-1 cubic
165b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# bezier segments. The algorithm used here is inspired by NURB
166b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# splines and the TrueType "implied point" principle, and ensures
167b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# the smoothest possible connection between two curve segments,
168b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# with no disruption in the curvature. It is practical since it
169b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# allows one to construct multiple bezier segments with a much
170b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# smaller amount of points.
171b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			pt1, pt2, pt3 = points[0], None, None
172b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			for i in range(2, n+1):
173b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				# calculate points in between control points.
174b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				nDivisions = min(i, 3, n-i+2)
175b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				d = float(nDivisions)
176b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				for j in range(1, nDivisions):
177b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr					factor = j / d
178b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr					temp1 = points[i-1]
179b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr					temp2 = points[i-2]
180b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr					temp = (temp2[0] + factor * (temp1[0] - temp2[0]),
181b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr					        temp2[1] + factor * (temp1[1] - temp2[1]))
182b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr					if pt2 is None:
183b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr						pt2 = temp
184b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr					else:
18582ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr						pt3 = (0.5 * (pt2[0] + temp[0]),
18682ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr						       0.5 * (pt2[1] + temp[1]))
187b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr						self._curveToOne(pt1, pt2, pt3)
188b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr						pt1, pt2, pt3 = temp, None, None
189b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self._curveToOne(pt1, points[-2], points[-1])
190b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.__currentPoint = points[-1]
191b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 1:
192b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self._qCurveOne(*points)
193b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 0:
194b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
195b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
196b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			raise AssertionError, "can't get there from here"
197b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
198b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
199b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
200b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
201934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		if points[-1] is None:
202934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# Special case for TrueType quadratics: it is possible to
203934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# define a contour with NO on-curve points. BasePen supports
204934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# this by allowing the final argument (the expected on-curve
205934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# point) to be None. We simulate the feature by making the implied
206934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# on-curve point between the last and the first off-curve points
207934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# explicit.
208934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			x, y = points[-2]  # last off-curve point
209934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			nx, ny = points[0] # first off-curve point
210934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
211934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self.__currentPoint = impliedStartPoint
212934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self._moveTo(impliedStartPoint)
213934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			points = points[:-1] + (impliedStartPoint,)
214b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n > 0:
21582ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# Split the string of points into discrete quadratic curve
21682ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# segments. Between any two consecutive off-curve points
21782ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# there's an implied on-curve point exactly in the middle.
21882ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# This is where the segment splits.
219b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			_qCurveToOne = self._qCurveToOne
220382102645ae23afa81867bde88974ecce438d952jvr			for i in range(n - 1):
221b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				x, y = points[i]
222b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				nx, ny = points[i+1]
223b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
224b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				_qCurveToOne(points[i], impliedPt)
225b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr				self.__currentPoint = impliedPt
226b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			_qCurveToOne(points[-2], points[-1])
227b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.__currentPoint = points[-1]
228b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
229b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
230b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
231b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
232b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass _TestPen(BasePen):
233b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
234b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "%s %s moveto" % (pt[0], pt[1])
235b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
236b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "%s %s lineto" % (pt[0], pt[1])
237b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, bcp1, bcp2, pt):
238934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		print "%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
23982ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr				bcp2[0], bcp2[1], pt[0], pt[1])
240b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
241b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "closepath"
242b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
243b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
244b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrif __name__ == "__main__":
245b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
246b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.moveTo((0, 0))
247b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.lineTo((0, 100))
248b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0))
249b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
250b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
251b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
252934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	# testing the "no on-curve point" scenario
253934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	pen.qCurveTo((0, 0), (0, 100), (100, 100), (100, 0), None)
254b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
255