basePen.py revision 2e4cc02ca31c43eafb6f752e44dbca9b004a3a2f
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
1652e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr		glyph = self.glyphSet.get(glyphName)
1662e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr		if glyph is not None:
1672e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			tPen = TransformPen(self, transformation)
1682e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			glyph.draw(tPen)
169b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
170b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# don't override
171b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
172b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _getCurrentPoint(self):
173b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Return the current point. This is not part of the public
174b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		interface, yet is useful for subclasses.
175b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
176b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		return self.__currentPoint
177b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
178b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
179b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._closePath()
180b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
181b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
18240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def endPath(self):
18340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self._endPath()
18440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self.__currentPoint = None
18540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
186b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
187b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._moveTo(pt)
188b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
189b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
190b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
191b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._lineTo(pt)
192b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
193b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
194b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
195b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
196b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
197b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n == 2:
198b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# The common case, we have exactly two BCP's, so this is a standard
19923cb20053223695a2fb1dd68920297ed8471d77fjvr			# cubic bezier. Even though decomposeSuperBezierSegment() handles
20023cb20053223695a2fb1dd68920297ed8471d77fjvr			# this case just fine, we special-case it anyway since it's so
20123cb20053223695a2fb1dd68920297ed8471d77fjvr			# common.
202b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self._curveToOne(*points)
203b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.__currentPoint = points[-1]
204b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n > 2:
205b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# n is the number of control points; split curve into n-1 cubic
206b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# bezier segments. The algorithm used here is inspired by NURB
207b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# splines and the TrueType "implied point" principle, and ensures
208b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# the smoothest possible connection between two curve segments,
209b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# with no disruption in the curvature. It is practical since it
210b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# allows one to construct multiple bezier segments with a much
211b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# smaller amount of points.
21223cb20053223695a2fb1dd68920297ed8471d77fjvr			_curveToOne = self._curveToOne
21323cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2, pt3 in decomposeSuperBezierSegment(points):
21423cb20053223695a2fb1dd68920297ed8471d77fjvr				_curveToOne(pt1, pt2, pt3)
21523cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt3
216b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 1:
21749b5521f2edcbf91651cae78ef3233bf16bb48d2jvr			self.qCurveTo(*points)
218b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 0:
219b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
220b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
221b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			raise AssertionError, "can't get there from here"
222b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
223b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
224b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
225b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
226934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		if points[-1] is None:
227934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# Special case for TrueType quadratics: it is possible to
228934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# define a contour with NO on-curve points. BasePen supports
229934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# this by allowing the final argument (the expected on-curve
230934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# point) to be None. We simulate the feature by making the implied
231934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# on-curve point between the last and the first off-curve points
232934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# explicit.
233934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			x, y = points[-2]  # last off-curve point
234934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			nx, ny = points[0] # first off-curve point
235934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
236934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self.__currentPoint = impliedStartPoint
237934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self._moveTo(impliedStartPoint)
238934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			points = points[:-1] + (impliedStartPoint,)
239b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n > 0:
24082ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# Split the string of points into discrete quadratic curve
24182ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# segments. Between any two consecutive off-curve points
24282ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# there's an implied on-curve point exactly in the middle.
24382ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# This is where the segment splits.
244b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			_qCurveToOne = self._qCurveToOne
24523cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2 in decomposeQuadraticSegment(points):
24623cb20053223695a2fb1dd68920297ed8471d77fjvr				_qCurveToOne(pt1, pt2)
24723cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt2
248b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
249b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
250b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
251b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
25223cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeSuperBezierSegment(points):
25323cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the SuperBezier described by 'points' into a list of regular
25423cb20053223695a2fb1dd68920297ed8471d77fjvr	bezier segments. The 'points' argument must be a sequence with length
25523cb20053223695a2fb1dd68920297ed8471d77fjvr	3 or greater, containing (x, y) coordinates. The last point is the
25623cb20053223695a2fb1dd68920297ed8471d77fjvr	destination on-curve point, the rest of the points are off-curve points.
25723cb20053223695a2fb1dd68920297ed8471d77fjvr	The start point should not be supplied.
25823cb20053223695a2fb1dd68920297ed8471d77fjvr
25923cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2, pt3) tuples, which each
26023cb20053223695a2fb1dd68920297ed8471d77fjvr	specify a regular curveto-style bezier segment.
26123cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
26223cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
26323cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 1
26423cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments = []
26523cb20053223695a2fb1dd68920297ed8471d77fjvr	pt1, pt2, pt3 = points[0], None, None
26623cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(2, n+1):
26723cb20053223695a2fb1dd68920297ed8471d77fjvr		# calculate points in between control points.
26823cb20053223695a2fb1dd68920297ed8471d77fjvr		nDivisions = min(i, 3, n-i+2)
26923cb20053223695a2fb1dd68920297ed8471d77fjvr		d = float(nDivisions)
27023cb20053223695a2fb1dd68920297ed8471d77fjvr		for j in range(1, nDivisions):
27123cb20053223695a2fb1dd68920297ed8471d77fjvr			factor = j / d
27223cb20053223695a2fb1dd68920297ed8471d77fjvr			temp1 = points[i-1]
27323cb20053223695a2fb1dd68920297ed8471d77fjvr			temp2 = points[i-2]
27423cb20053223695a2fb1dd68920297ed8471d77fjvr			temp = (temp2[0] + factor * (temp1[0] - temp2[0]),
27523cb20053223695a2fb1dd68920297ed8471d77fjvr					temp2[1] + factor * (temp1[1] - temp2[1]))
27623cb20053223695a2fb1dd68920297ed8471d77fjvr			if pt2 is None:
27723cb20053223695a2fb1dd68920297ed8471d77fjvr				pt2 = temp
27823cb20053223695a2fb1dd68920297ed8471d77fjvr			else:
27923cb20053223695a2fb1dd68920297ed8471d77fjvr				pt3 = (0.5 * (pt2[0] + temp[0]),
28023cb20053223695a2fb1dd68920297ed8471d77fjvr					   0.5 * (pt2[1] + temp[1]))
28123cb20053223695a2fb1dd68920297ed8471d77fjvr				bezierSegments.append((pt1, pt2, pt3))
28223cb20053223695a2fb1dd68920297ed8471d77fjvr				pt1, pt2, pt3 = temp, None, None
28323cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments.append((pt1, points[-2], points[-1]))
28423cb20053223695a2fb1dd68920297ed8471d77fjvr	return bezierSegments
28523cb20053223695a2fb1dd68920297ed8471d77fjvr
28623cb20053223695a2fb1dd68920297ed8471d77fjvr
28723cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeQuadraticSegment(points):
28823cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the quadratic curve segment described by 'points' into a list
28923cb20053223695a2fb1dd68920297ed8471d77fjvr	of "atomic" quadratic segments. The 'points' argument must be a sequence
29023cb20053223695a2fb1dd68920297ed8471d77fjvr	with length 2 or greater, containing (x, y) coordinates. The last point
29123cb20053223695a2fb1dd68920297ed8471d77fjvr	is the destination on-curve point, the rest of the points are off-curve
29223cb20053223695a2fb1dd68920297ed8471d77fjvr	points. The start point should not be supplied.
29323cb20053223695a2fb1dd68920297ed8471d77fjvr
29423cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2) tuples, which each specify a
29523cb20053223695a2fb1dd68920297ed8471d77fjvr	plain quadratic bezier segment.
29623cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
29723cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
29823cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 0
29923cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments = []
30023cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(n - 1):
30123cb20053223695a2fb1dd68920297ed8471d77fjvr		x, y = points[i]
30223cb20053223695a2fb1dd68920297ed8471d77fjvr		nx, ny = points[i+1]
30323cb20053223695a2fb1dd68920297ed8471d77fjvr		impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
30423cb20053223695a2fb1dd68920297ed8471d77fjvr		quadSegments.append((points[i], impliedPt))
30523cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments.append((points[-2], points[-1]))
30623cb20053223695a2fb1dd68920297ed8471d77fjvr	return quadSegments
30723cb20053223695a2fb1dd68920297ed8471d77fjvr
30823cb20053223695a2fb1dd68920297ed8471d77fjvr
309b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass _TestPen(BasePen):
31040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""Test class that prints PostScript to stdout."""
311b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
312b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "%s %s moveto" % (pt[0], pt[1])
313b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
314b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "%s %s lineto" % (pt[0], pt[1])
315b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, bcp1, bcp2, pt):
316934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		print "%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
31782ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr				bcp2[0], bcp2[1], pt[0], pt[1])
318b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
319b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		print "closepath"
320b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
321b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
322b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrif __name__ == "__main__":
323b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
324b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.moveTo((0, 0))
325b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.lineTo((0, 100))
32623cb20053223695a2fb1dd68920297ed8471d77fjvr	pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
327b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
328b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
329b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
330934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	# testing the "no on-curve point" scenario
331934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	pen.qCurveTo((0, 0), (0, 100), (100, 100), (100, 0), None)
332b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
333