basePen.py revision 3ec6a258238b6068e4eef3fe579f1f5c0a06bbba
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
40285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod__all__ = ["AbstractPen", "NullPen", "BasePen",
4123cb20053223695a2fb1dd68920297ed8471d77fjvr           "decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
42b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
43b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
4491bca4244286fb519c93fe92329da96b0e6f32eejvrclass AbstractPen(object):
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
115285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbodclass NullPen(object):
116285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
117285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	"""A pen that does nothing.
118285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	"""
119285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
120285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def moveTo(self, pt):
121285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
122285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
123285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def lineTo(self, pt):
124285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
125285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
126285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def curveTo(self, *points):
127285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
128285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
129285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def qCurveTo(self, *points):
130285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
131285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
132285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def closePath(self):
133285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
134285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
135285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def endPath(self):
136285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
137285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
138285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def addComponent(self, glyphName, transformation):
139285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
140285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
141285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
142b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass BasePen(AbstractPen):
143b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
14440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""Base class for drawing pens. You must override _moveTo, _lineTo and
14540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	_curveToOne. You may additionally override _closePath, _endPath,
14640cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	addComponent and/or _qCurveToOne. You should not override any other
14740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	methods.
14840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""
149b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
150b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def __init__(self, glyphSet):
151b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.glyphSet = glyphSet
152b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
153b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
154b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# must override
155b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
156b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
157b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
158b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
159b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
160b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
161b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
162b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, pt1, pt2, pt3):
163b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
164b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
165b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# may override
166b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
167b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
168b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pass
169b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
17040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def _endPath(self):
17140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		pass
17240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
173b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _qCurveToOne(self, pt1, pt2):
174b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This method implements the basic quadratic curve type. The
175b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		default implementation delegates the work to the cubic curve
176b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		function. Optionally override with a native implementation.
177b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
178b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt0x, pt0y = self.__currentPoint
179b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt1x, pt1y = pt1
180b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt2x, pt2y = pt2
181b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1x = pt0x + 0.66666666666666667 * (pt1x - pt0x)
182b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1y = pt0y + 0.66666666666666667 * (pt1y - pt0y)
183b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2x = pt2x + 0.66666666666666667 * (pt1x - pt2x)
184b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y)
185b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._curveToOne((mid1x, mid1y), (mid2x, mid2y), pt2)
186b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
187b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def addComponent(self, glyphName, transformation):
188b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This default implementation simply transforms the points
189b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		of the base glyph and draws it onto self.
190b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
191b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		from fontTools.pens.transformPen import TransformPen
192a5c92986dd7421474fd2eb7c86943d1921150978jvr		try:
193a5c92986dd7421474fd2eb7c86943d1921150978jvr			glyph = self.glyphSet[glyphName]
194a5c92986dd7421474fd2eb7c86943d1921150978jvr		except KeyError:
195a5c92986dd7421474fd2eb7c86943d1921150978jvr			pass
196a5c92986dd7421474fd2eb7c86943d1921150978jvr		else:
1972e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			tPen = TransformPen(self, transformation)
1982e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			glyph.draw(tPen)
199b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
200b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# don't override
201b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
202b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _getCurrentPoint(self):
203b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Return the current point. This is not part of the public
204b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		interface, yet is useful for subclasses.
205b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
206b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		return self.__currentPoint
207b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
208b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
209b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._closePath()
210b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
211b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
21240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def endPath(self):
21340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self._endPath()
21440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self.__currentPoint = None
21540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
216b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
217b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._moveTo(pt)
218b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
219b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
220b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
221b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._lineTo(pt)
222b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
223b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
224b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
225b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
226b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
227b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n == 2:
228b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# The common case, we have exactly two BCP's, so this is a standard
22923cb20053223695a2fb1dd68920297ed8471d77fjvr			# cubic bezier. Even though decomposeSuperBezierSegment() handles
23023cb20053223695a2fb1dd68920297ed8471d77fjvr			# this case just fine, we special-case it anyway since it's so
23123cb20053223695a2fb1dd68920297ed8471d77fjvr			# common.
232b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self._curveToOne(*points)
233b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.__currentPoint = points[-1]
234b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n > 2:
235b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# n is the number of control points; split curve into n-1 cubic
236b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# bezier segments. The algorithm used here is inspired by NURB
237b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# splines and the TrueType "implied point" principle, and ensures
238b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# the smoothest possible connection between two curve segments,
239b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# with no disruption in the curvature. It is practical since it
240b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# allows one to construct multiple bezier segments with a much
241b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# smaller amount of points.
24223cb20053223695a2fb1dd68920297ed8471d77fjvr			_curveToOne = self._curveToOne
24323cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2, pt3 in decomposeSuperBezierSegment(points):
24423cb20053223695a2fb1dd68920297ed8471d77fjvr				_curveToOne(pt1, pt2, pt3)
24523cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt3
246b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 1:
24749b5521f2edcbf91651cae78ef3233bf16bb48d2jvr			self.qCurveTo(*points)
248b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 0:
249b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
250b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
251cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AssertionError("can't get there from here")
252b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
253b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
254b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
255b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
256934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		if points[-1] is None:
257934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# Special case for TrueType quadratics: it is possible to
258934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# define a contour with NO on-curve points. BasePen supports
259934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# this by allowing the final argument (the expected on-curve
260934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# point) to be None. We simulate the feature by making the implied
261934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# on-curve point between the last and the first off-curve points
262934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# explicit.
263934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			x, y = points[-2]  # last off-curve point
264934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			nx, ny = points[0] # first off-curve point
265934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
266934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self.__currentPoint = impliedStartPoint
267934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self._moveTo(impliedStartPoint)
268934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			points = points[:-1] + (impliedStartPoint,)
269b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n > 0:
27082ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# Split the string of points into discrete quadratic curve
27182ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# segments. Between any two consecutive off-curve points
27282ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# there's an implied on-curve point exactly in the middle.
27382ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# This is where the segment splits.
274b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			_qCurveToOne = self._qCurveToOne
27523cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2 in decomposeQuadraticSegment(points):
27623cb20053223695a2fb1dd68920297ed8471d77fjvr				_qCurveToOne(pt1, pt2)
27723cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt2
278b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
279b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
280b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
281b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
28223cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeSuperBezierSegment(points):
28323cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the SuperBezier described by 'points' into a list of regular
28423cb20053223695a2fb1dd68920297ed8471d77fjvr	bezier segments. The 'points' argument must be a sequence with length
28523cb20053223695a2fb1dd68920297ed8471d77fjvr	3 or greater, containing (x, y) coordinates. The last point is the
28623cb20053223695a2fb1dd68920297ed8471d77fjvr	destination on-curve point, the rest of the points are off-curve points.
28723cb20053223695a2fb1dd68920297ed8471d77fjvr	The start point should not be supplied.
28823cb20053223695a2fb1dd68920297ed8471d77fjvr
28923cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2, pt3) tuples, which each
29023cb20053223695a2fb1dd68920297ed8471d77fjvr	specify a regular curveto-style bezier segment.
29123cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
29223cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
29323cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 1
29423cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments = []
29523cb20053223695a2fb1dd68920297ed8471d77fjvr	pt1, pt2, pt3 = points[0], None, None
29623cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(2, n+1):
29723cb20053223695a2fb1dd68920297ed8471d77fjvr		# calculate points in between control points.
29823cb20053223695a2fb1dd68920297ed8471d77fjvr		nDivisions = min(i, 3, n-i+2)
29923cb20053223695a2fb1dd68920297ed8471d77fjvr		d = float(nDivisions)
30023cb20053223695a2fb1dd68920297ed8471d77fjvr		for j in range(1, nDivisions):
30123cb20053223695a2fb1dd68920297ed8471d77fjvr			factor = j / d
30223cb20053223695a2fb1dd68920297ed8471d77fjvr			temp1 = points[i-1]
30323cb20053223695a2fb1dd68920297ed8471d77fjvr			temp2 = points[i-2]
30423cb20053223695a2fb1dd68920297ed8471d77fjvr			temp = (temp2[0] + factor * (temp1[0] - temp2[0]),
30523cb20053223695a2fb1dd68920297ed8471d77fjvr					temp2[1] + factor * (temp1[1] - temp2[1]))
30623cb20053223695a2fb1dd68920297ed8471d77fjvr			if pt2 is None:
30723cb20053223695a2fb1dd68920297ed8471d77fjvr				pt2 = temp
30823cb20053223695a2fb1dd68920297ed8471d77fjvr			else:
30923cb20053223695a2fb1dd68920297ed8471d77fjvr				pt3 = (0.5 * (pt2[0] + temp[0]),
31023cb20053223695a2fb1dd68920297ed8471d77fjvr					   0.5 * (pt2[1] + temp[1]))
31123cb20053223695a2fb1dd68920297ed8471d77fjvr				bezierSegments.append((pt1, pt2, pt3))
31223cb20053223695a2fb1dd68920297ed8471d77fjvr				pt1, pt2, pt3 = temp, None, None
31323cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments.append((pt1, points[-2], points[-1]))
31423cb20053223695a2fb1dd68920297ed8471d77fjvr	return bezierSegments
31523cb20053223695a2fb1dd68920297ed8471d77fjvr
31623cb20053223695a2fb1dd68920297ed8471d77fjvr
31723cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeQuadraticSegment(points):
31823cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the quadratic curve segment described by 'points' into a list
31923cb20053223695a2fb1dd68920297ed8471d77fjvr	of "atomic" quadratic segments. The 'points' argument must be a sequence
32023cb20053223695a2fb1dd68920297ed8471d77fjvr	with length 2 or greater, containing (x, y) coordinates. The last point
32123cb20053223695a2fb1dd68920297ed8471d77fjvr	is the destination on-curve point, the rest of the points are off-curve
32223cb20053223695a2fb1dd68920297ed8471d77fjvr	points. The start point should not be supplied.
32323cb20053223695a2fb1dd68920297ed8471d77fjvr
32423cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2) tuples, which each specify a
32523cb20053223695a2fb1dd68920297ed8471d77fjvr	plain quadratic bezier segment.
32623cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
32723cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
32823cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 0
32923cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments = []
33023cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(n - 1):
33123cb20053223695a2fb1dd68920297ed8471d77fjvr		x, y = points[i]
33223cb20053223695a2fb1dd68920297ed8471d77fjvr		nx, ny = points[i+1]
33323cb20053223695a2fb1dd68920297ed8471d77fjvr		impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
33423cb20053223695a2fb1dd68920297ed8471d77fjvr		quadSegments.append((points[i], impliedPt))
33523cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments.append((points[-2], points[-1]))
33623cb20053223695a2fb1dd68920297ed8471d77fjvr	return quadSegments
33723cb20053223695a2fb1dd68920297ed8471d77fjvr
33823cb20053223695a2fb1dd68920297ed8471d77fjvr
339b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass _TestPen(BasePen):
34040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""Test class that prints PostScript to stdout."""
341b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
3423ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("%s %s moveto" % (pt[0], pt[1]))
343b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
3443ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("%s %s lineto" % (pt[0], pt[1]))
345b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, bcp1, bcp2, pt):
3463ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
3473ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				bcp2[0], bcp2[1], pt[0], pt[1]))
348b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
3493ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("closepath")
350b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
351b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
352b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrif __name__ == "__main__":
353b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
354b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.moveTo((0, 0))
355b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.lineTo((0, 100))
35623cb20053223695a2fb1dd68920297ed8471d77fjvr	pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
357b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
358b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
359b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
360934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	# testing the "no on-curve point" scenario
361934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	pen.qCurveTo((0, 0), (0, 100), (100, 100), (100, 0), None)
362b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
363