basePen.py revision 23cb20053223695a2fb1dd68920297ed8471d77f
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
2040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrThe AbstractPen class defines the Pen protocol. It implements almost
2140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrnothing (only no-op closePath() and endPath() methods), but is useful
2240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrfor documentation purposes. Subclassing it basically tells the reader:
2340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr"this class implements the Pen protocol.". An examples of an AbstractPen
2440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrsubclass is fontTools.pens.transformPen.TransformPen.
2540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
2640cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrThe BasePen class is a base implementation useful for pens that actually
2740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrdraw (for example a pen renders outlines using a native graphics engine).
2840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrBasePen contains a lot of base functionality, making it very easy to build
2940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvra pen that fully conforms to the pen protocol. Note that if you subclass
3040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrBasePen, you _don't_ override moveTo(), lineTo(), etc., but _moveTo(),
3140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr_lineTo(), etc. See the BasePen doc string for details. Examples of
3240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrBasePen subclasses are fontTools.pens.boundsPen.BoundsPen and
3340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrfontTools.pens.cocoaPen.CocoaPen.
3440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
3540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrCoordinates are usually expressed as (x, y) tuples, but generally any
3640cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvrsequence of length 2 will do.
37b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr"""
38b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
3940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
4023cb20053223695a2fb1dd68920297ed8471d77fjvr__all__ = ["AbstractPen", "BasePen",
4123cb20053223695a2fb1dd68920297ed8471d77fjvr           "decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
42b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
43b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
44b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass AbstractPen:
45b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
46b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
4740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""Begin a new sub path, set the current point to 'pt'. You must
4840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		end each sub path with a call to pen.closePath() or pen.endPath().
4940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""
50b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
51b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
52b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
5340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""Draw a straight line from the current point to 'pt'."""
54b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
55b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
56b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
57934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""Draw a cubic bezier with an arbitrary number of control points.
58934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr
59934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last point specified is on-curve, all others are off-curve
60934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		(control) points. If the number of control points is > 2, the
61934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		segment is split into multiple bezier segments. This works
62934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		like this:
63b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
64b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		Let n be the number of control points (which is the number of
65b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		arguments to this call minus 1). If n==2, a plain vanilla cubic
66b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		bezier is drawn. If n==1, we fall back to a quadratic segment and
67b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n==0 we draw a straight line. It gets interesting when n>2:
68b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n-1 PostScript-style cubic segments will be drawn as if it were
6923cb20053223695a2fb1dd68920297ed8471d77fjvr		one curve. See decomposeSuperBezierSegment().
70b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
71b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		The conversion algorithm used for n>2 is inspired by NURB
72b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		splines, and is conceptually equivalent to the TrueType "implied
7323cb20053223695a2fb1dd68920297ed8471d77fjvr		points" principle. See also decomposeQuadraticSegment().
74b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
75b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
76b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
77b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
78b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Draw a whole string of quadratic curve segments.
79b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
80934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last point specified is on-curve, all others are off-curve
81934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		points.
82934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr
83934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		This method implements TrueType-style curves, breaking up curves
84934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		using 'implied points': between each two consequtive off-curve points,
8523cb20053223695a2fb1dd68920297ed8471d77fjvr		there is one implied point exactly in the middle between them. See
8623cb20053223695a2fb1dd68920297ed8471d77fjvr		also decomposeQuadraticSegment().
87b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
88934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last argument (normally the on-curve point) may be None.
89934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		This is to support contours that have NO on-curve points (a rarely
90934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		seen feature of TrueType outlines).
91b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
92b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
93b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
94b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
9540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""Close the current sub path. You must call either pen.closePath()
9640cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		or pen.endPath() after each sub path.
9740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""
9840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		pass
9940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
10040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def endPath(self):
10140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""End the current sub path, but don't close it. You must call
10240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		either pen.closePath() or pen.endPath() after each sub path.
10340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""
104b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pass
105b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
106b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def addComponent(self, glyphName, transformation):
107934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""Add a sub glyph. The 'transformation' argument must be a 6-tuple
108934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		containing an affine transformation, or a Transform object from the
109934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		fontTools.misc.transform module. More precisely: it should be a
110934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		sequence containing 6 numbers.
111934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""
112b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
113b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
114b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
115b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass BasePen(AbstractPen):
116b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
11740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""Base class for drawing pens. You must override _moveTo, _lineTo and
11840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	_curveToOne. You may additionally override _closePath, _endPath,
11940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	addComponent and/or _qCurveToOne. You should not override any other
12040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	methods.
12140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""
122b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
123b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def __init__(self, glyphSet):
124b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.glyphSet = glyphSet
125b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
126b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
127b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# must override
128b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
129b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
130b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
131b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
132b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
133b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
134b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
135b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, pt1, pt2, pt3):
136b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
137b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
138b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# may override
139b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
140b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
141b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pass
142b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
14340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def _endPath(self):
14440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		pass
14540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
146b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _qCurveToOne(self, pt1, pt2):
147b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This method implements the basic quadratic curve type. The
148b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		default implementation delegates the work to the cubic curve
149b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		function. Optionally override with a native implementation.
150b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
151b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt0x, pt0y = self.__currentPoint
152b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt1x, pt1y = pt1
153b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt2x, pt2y = pt2
154b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1x = pt0x + 0.66666666666666667 * (pt1x - pt0x)
155b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1y = pt0y + 0.66666666666666667 * (pt1y - pt0y)
156b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2x = pt2x + 0.66666666666666667 * (pt1x - pt2x)
157b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y)
158b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._curveToOne((mid1x, mid1y), (mid2x, mid2y), pt2)
159b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
160b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def addComponent(self, glyphName, transformation):
161b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This default implementation simply transforms the points
162b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		of the base glyph and draws it onto self.
163b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
164b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		from fontTools.pens.transformPen import TransformPen
165b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		tPen = TransformPen(self, transformation)
166b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.glyphSet[glyphName].draw(tPen)
167b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
168b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# don't override
169b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
170b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _getCurrentPoint(self):
171b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Return the current point. This is not part of the public
172b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		interface, yet is useful for subclasses.
173b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
174b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		return self.__currentPoint
175b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
176b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
177b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._closePath()
178b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
179b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
18040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def endPath(self):
18140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self._endPath()
18240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self.__currentPoint = None
18340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
184b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
185b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._moveTo(pt)
186b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
187b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
188b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
189b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._lineTo(pt)
190b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
191b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
192b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
193b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
194b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
195b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n == 2:
196b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# The common case, we have exactly two BCP's, so this is a standard
19723cb20053223695a2fb1dd68920297ed8471d77fjvr			# cubic bezier. Even though decomposeSuperBezierSegment() handles
19823cb20053223695a2fb1dd68920297ed8471d77fjvr			# this case just fine, we special-case it anyway since it's so
19923cb20053223695a2fb1dd68920297ed8471d77fjvr			# common.
200b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self._curveToOne(*points)
201b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.__currentPoint = points[-1]
202b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n > 2:
203b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# n is the number of control points; split curve into n-1 cubic
204b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# bezier segments. The algorithm used here is inspired by NURB
205b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# splines and the TrueType "implied point" principle, and ensures
206b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# the smoothest possible connection between two curve segments,
207b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# with no disruption in the curvature. It is practical since it
208b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# allows one to construct multiple bezier segments with a much
209b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# smaller amount of points.
21023cb20053223695a2fb1dd68920297ed8471d77fjvr			_curveToOne = self._curveToOne
21123cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2, pt3 in decomposeSuperBezierSegment(points):
21223cb20053223695a2fb1dd68920297ed8471d77fjvr				_curveToOne(pt1, pt2, pt3)
21323cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt3
214b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 1:
21549b5521f2edcbf91651cae78ef3233bf16bb48d2jvr			self.qCurveTo(*points)
216b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 0:
217b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
218b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
219b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			raise AssertionError, "can't get there from here"
220b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
221b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
222b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
223b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
224934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		if points[-1] is None:
225934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# Special case for TrueType quadratics: it is possible to
226934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# define a contour with NO on-curve points. BasePen supports
227934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# this by allowing the final argument (the expected on-curve
228934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# point) to be None. We simulate the feature by making the implied
229934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# on-curve point between the last and the first off-curve points
230934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# explicit.
231934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			x, y = points[-2]  # last off-curve point
232934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			nx, ny = points[0] # first off-curve point
233934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
234934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self.__currentPoint = impliedStartPoint
235934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self._moveTo(impliedStartPoint)
236934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			points = points[:-1] + (impliedStartPoint,)
237b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n > 0:
23882ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# Split the string of points into discrete quadratic curve
23982ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# segments. Between any two consecutive off-curve points
24082ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# there's an implied on-curve point exactly in the middle.
24182ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# This is where the segment splits.
242b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			_qCurveToOne = self._qCurveToOne
24323cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2 in decomposeQuadraticSegment(points):
24423cb20053223695a2fb1dd68920297ed8471d77fjvr				_qCurveToOne(pt1, pt2)
24523cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt2
246b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
247b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
248b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
249b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
25023cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeSuperBezierSegment(points):
25123cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the SuperBezier described by 'points' into a list of regular
25223cb20053223695a2fb1dd68920297ed8471d77fjvr	bezier segments. The 'points' argument must be a sequence with length
25323cb20053223695a2fb1dd68920297ed8471d77fjvr	3 or greater, containing (x, y) coordinates. The last point is the
25423cb20053223695a2fb1dd68920297ed8471d77fjvr	destination on-curve point, the rest of the points are off-curve points.
25523cb20053223695a2fb1dd68920297ed8471d77fjvr	The start point should not be supplied.
25623cb20053223695a2fb1dd68920297ed8471d77fjvr
25723cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2, pt3) tuples, which each
25823cb20053223695a2fb1dd68920297ed8471d77fjvr	specify a regular curveto-style bezier segment.
25923cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
26023cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
26123cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 1
26223cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments = []
26323cb20053223695a2fb1dd68920297ed8471d77fjvr	pt1, pt2, pt3 = points[0], None, None
26423cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(2, n+1):
26523cb20053223695a2fb1dd68920297ed8471d77fjvr		# calculate points in between control points.
26623cb20053223695a2fb1dd68920297ed8471d77fjvr		nDivisions = min(i, 3, n-i+2)
26723cb20053223695a2fb1dd68920297ed8471d77fjvr		d = float(nDivisions)
26823cb20053223695a2fb1dd68920297ed8471d77fjvr		for j in range(1, nDivisions):
26923cb20053223695a2fb1dd68920297ed8471d77fjvr			factor = j / d
27023cb20053223695a2fb1dd68920297ed8471d77fjvr			temp1 = points[i-1]
27123cb20053223695a2fb1dd68920297ed8471d77fjvr			temp2 = points[i-2]
27223cb20053223695a2fb1dd68920297ed8471d77fjvr			temp = (temp2[0] + factor * (temp1[0] - temp2[0]),
27323cb20053223695a2fb1dd68920297ed8471d77fjvr					temp2[1] + factor * (temp1[1] - temp2[1]))
27423cb20053223695a2fb1dd68920297ed8471d77fjvr			if pt2 is None:
27523cb20053223695a2fb1dd68920297ed8471d77fjvr				pt2 = temp
27623cb20053223695a2fb1dd68920297ed8471d77fjvr			else:
27723cb20053223695a2fb1dd68920297ed8471d77fjvr				pt3 = (0.5 * (pt2[0] + temp[0]),
27823cb20053223695a2fb1dd68920297ed8471d77fjvr					   0.5 * (pt2[1] + temp[1]))
27923cb20053223695a2fb1dd68920297ed8471d77fjvr				bezierSegments.append((pt1, pt2, pt3))
28023cb20053223695a2fb1dd68920297ed8471d77fjvr				pt1, pt2, pt3 = temp, None, None
28123cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments.append((pt1, points[-2], points[-1]))
28223cb20053223695a2fb1dd68920297ed8471d77fjvr	return bezierSegments
28323cb20053223695a2fb1dd68920297ed8471d77fjvr
28423cb20053223695a2fb1dd68920297ed8471d77fjvr
28523cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeQuadraticSegment(points):
28623cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the quadratic curve segment described by 'points' into a list
28723cb20053223695a2fb1dd68920297ed8471d77fjvr	of "atomic" quadratic segments. The 'points' argument must be a sequence
28823cb20053223695a2fb1dd68920297ed8471d77fjvr	with length 2 or greater, containing (x, y) coordinates. The last point
28923cb20053223695a2fb1dd68920297ed8471d77fjvr	is the destination on-curve point, the rest of the points are off-curve
29023cb20053223695a2fb1dd68920297ed8471d77fjvr	points. The start point should not be supplied.
29123cb20053223695a2fb1dd68920297ed8471d77fjvr
29223cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2) tuples, which each specify a
29323cb20053223695a2fb1dd68920297ed8471d77fjvr	plain quadratic bezier segment.
29423cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
29523cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
29623cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 0
29723cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments = []
29823cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(n - 1):
29923cb20053223695a2fb1dd68920297ed8471d77fjvr		x, y = points[i]
30023cb20053223695a2fb1dd68920297ed8471d77fjvr		nx, ny = points[i+1]
30123cb20053223695a2fb1dd68920297ed8471d77fjvr		impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
30223cb20053223695a2fb1dd68920297ed8471d77fjvr		quadSegments.append((points[i], impliedPt))
30323cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments.append((points[-2], points[-1]))
30423cb20053223695a2fb1dd68920297ed8471d77fjvr	return quadSegments
30523cb20053223695a2fb1dd68920297ed8471d77fjvr
30623cb20053223695a2fb1dd68920297ed8471d77fjvr
307b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass _TestPen(BasePen):
30840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""Test class that prints PostScript to stdout."""
309b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
310b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "%s %s moveto" % (pt[0], pt[1])
311b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
312b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "%s %s lineto" % (pt[0], pt[1])
313b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, bcp1, bcp2, pt):
314934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		print "%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
31582ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr				bcp2[0], bcp2[1], pt[0], pt[1])
316b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
317b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "closepath"
318b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
319b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
320b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrif __name__ == "__main__":
321b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
322b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.moveTo((0, 0))
323b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.lineTo((0, 100))
32423cb20053223695a2fb1dd68920297ed8471d77fjvr	pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
325b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
326b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
327b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
328934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	# testing the "no on-curve point" scenario
329934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	pen.qCurveTo((0, 0), (0, 100), (100, 100), (100, 0), None)
330b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
331