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
391ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import
407ed91eca1eaa96b79eae780778e89bb9ec44c1eeBehdad Esfahbodfrom fontTools.misc.py23 import *
4140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
42285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod__all__ = ["AbstractPen", "NullPen", "BasePen",
4323cb20053223695a2fb1dd68920297ed8471d77fjvr           "decomposeSuperBezierSegment", "decomposeQuadraticSegment"]
44b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
45b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
4691bca4244286fb519c93fe92329da96b0e6f32eejvrclass AbstractPen(object):
47b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
48b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
4940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""Begin a new sub path, set the current point to 'pt'. You must
5040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		end each sub path with a call to pen.closePath() or pen.endPath().
5140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""
52b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
53b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
54b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
5540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""Draw a straight line from the current point to 'pt'."""
56b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
57b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
58b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
59934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""Draw a cubic bezier with an arbitrary number of control points.
60934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr
61934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last point specified is on-curve, all others are off-curve
62934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		(control) points. If the number of control points is > 2, the
63934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		segment is split into multiple bezier segments. This works
64934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		like this:
65b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
66b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		Let n be the number of control points (which is the number of
67b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		arguments to this call minus 1). If n==2, a plain vanilla cubic
68b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		bezier is drawn. If n==1, we fall back to a quadratic segment and
69b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n==0 we draw a straight line. It gets interesting when n>2:
70b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n-1 PostScript-style cubic segments will be drawn as if it were
7123cb20053223695a2fb1dd68920297ed8471d77fjvr		one curve. See decomposeSuperBezierSegment().
72b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
73b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		The conversion algorithm used for n>2 is inspired by NURB
74b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		splines, and is conceptually equivalent to the TrueType "implied
7523cb20053223695a2fb1dd68920297ed8471d77fjvr		points" principle. See also decomposeQuadraticSegment().
76b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
77b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
78b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
79b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
80b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Draw a whole string of quadratic curve segments.
81b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
82934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last point specified is on-curve, all others are off-curve
83934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		points.
84934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr
85934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		This method implements TrueType-style curves, breaking up curves
86934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		using 'implied points': between each two consequtive off-curve points,
8723cb20053223695a2fb1dd68920297ed8471d77fjvr		there is one implied point exactly in the middle between them. See
8823cb20053223695a2fb1dd68920297ed8471d77fjvr		also decomposeQuadraticSegment().
89b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
90934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		The last argument (normally the on-curve point) may be None.
91934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		This is to support contours that have NO on-curve points (a rarely
92934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		seen feature of TrueType outlines).
93b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
94b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
95b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
96b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
9740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""Close the current sub path. You must call either pen.closePath()
9840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		or pen.endPath() after each sub path.
9940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""
10040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		pass
10140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
10240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def endPath(self):
10340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""End the current sub path, but don't close it. You must call
10440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		either pen.closePath() or pen.endPath() after each sub path.
10540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		"""
106b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pass
107b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
108b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def addComponent(self, glyphName, transformation):
109934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""Add a sub glyph. The 'transformation' argument must be a 6-tuple
110934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		containing an affine transformation, or a Transform object from the
111934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		fontTools.misc.transform module. More precisely: it should be a
112934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		sequence containing 6 numbers.
113934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		"""
114b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
115b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
116b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
117285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbodclass NullPen(object):
118285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
119285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	"""A pen that does nothing.
120285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	"""
121285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
122285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def moveTo(self, pt):
123285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
124285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
125285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def lineTo(self, pt):
126285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
127285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
128285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def curveTo(self, *points):
129285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
130285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
131285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def qCurveTo(self, *points):
132285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
133285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
134285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def closePath(self):
135285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
136285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
137285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def endPath(self):
138285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
139285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
140285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod	def addComponent(self, glyphName, transformation):
141285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod		pass
142285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
143285d7b81d3a1d9d060864438580f05c2b44366ffBehdad Esfahbod
144b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass BasePen(AbstractPen):
145b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
14640cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""Base class for drawing pens. You must override _moveTo, _lineTo and
14740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	_curveToOne. You may additionally override _closePath, _endPath,
14840cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	addComponent and/or _qCurveToOne. You should not override any other
14940cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	methods.
15040cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""
151b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
152b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def __init__(self, glyphSet):
153b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.glyphSet = glyphSet
154b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
155b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
156b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# must override
157b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
158b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
159b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
160b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
161b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
162b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
163b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
164b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, pt1, pt2, pt3):
165b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		raise NotImplementedError
166b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
167b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# may override
168b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
169b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
170b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pass
171b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
17240cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def _endPath(self):
17340cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		pass
17440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
175b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _qCurveToOne(self, pt1, pt2):
176b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This method implements the basic quadratic curve type. The
177b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		default implementation delegates the work to the cubic curve
178b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		function. Optionally override with a native implementation.
179b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
180b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt0x, pt0y = self.__currentPoint
181b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt1x, pt1y = pt1
182b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		pt2x, pt2y = pt2
183b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1x = pt0x + 0.66666666666666667 * (pt1x - pt0x)
184b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid1y = pt0y + 0.66666666666666667 * (pt1y - pt0y)
185b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2x = pt2x + 0.66666666666666667 * (pt1x - pt2x)
186b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y)
187b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._curveToOne((mid1x, mid1y), (mid2x, mid2y), pt2)
188b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
189b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def addComponent(self, glyphName, transformation):
190b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""This default implementation simply transforms the points
191b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		of the base glyph and draws it onto self.
192b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
193b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		from fontTools.pens.transformPen import TransformPen
194a5c92986dd7421474fd2eb7c86943d1921150978jvr		try:
195a5c92986dd7421474fd2eb7c86943d1921150978jvr			glyph = self.glyphSet[glyphName]
196a5c92986dd7421474fd2eb7c86943d1921150978jvr		except KeyError:
197a5c92986dd7421474fd2eb7c86943d1921150978jvr			pass
198a5c92986dd7421474fd2eb7c86943d1921150978jvr		else:
1992e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			tPen = TransformPen(self, transformation)
2002e4cc02ca31c43eafb6f752e44dbca9b004a3a2fjvr			glyph.draw(tPen)
201b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
202b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	# don't override
203b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
204b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _getCurrentPoint(self):
205b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""Return the current point. This is not part of the public
206b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		interface, yet is useful for subclasses.
207b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		"""
208b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		return self.__currentPoint
209b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
210b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def closePath(self):
211b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._closePath()
212b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = None
213b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
21440cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	def endPath(self):
21540cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self._endPath()
21640cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr		self.__currentPoint = None
21740cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr
218b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def moveTo(self, pt):
219b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._moveTo(pt)
220b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
221b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
222b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def lineTo(self, pt):
223b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self._lineTo(pt)
224b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		self.__currentPoint = pt
225b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
226b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def curveTo(self, *points):
227b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
228b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
229b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n == 2:
230b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# The common case, we have exactly two BCP's, so this is a standard
23123cb20053223695a2fb1dd68920297ed8471d77fjvr			# cubic bezier. Even though decomposeSuperBezierSegment() handles
23223cb20053223695a2fb1dd68920297ed8471d77fjvr			# this case just fine, we special-case it anyway since it's so
23323cb20053223695a2fb1dd68920297ed8471d77fjvr			# common.
234b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self._curveToOne(*points)
235b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.__currentPoint = points[-1]
236b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n > 2:
237b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# n is the number of control points; split curve into n-1 cubic
238b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# bezier segments. The algorithm used here is inspired by NURB
239b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# splines and the TrueType "implied point" principle, and ensures
240b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# the smoothest possible connection between two curve segments,
241b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# with no disruption in the curvature. It is practical since it
242b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# allows one to construct multiple bezier segments with a much
243b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			# smaller amount of points.
24423cb20053223695a2fb1dd68920297ed8471d77fjvr			_curveToOne = self._curveToOne
24523cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2, pt3 in decomposeSuperBezierSegment(points):
24623cb20053223695a2fb1dd68920297ed8471d77fjvr				_curveToOne(pt1, pt2, pt3)
24723cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt3
248b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 1:
24949b5521f2edcbf91651cae78ef3233bf16bb48d2jvr			self.qCurveTo(*points)
250b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		elif n == 0:
251b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
252b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
253cd5aad92f23737ff93a110d5c73d624658a28da8Behdad Esfahbod			raise AssertionError("can't get there from here")
254b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
255b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def qCurveTo(self, *points):
256b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		n = len(points) - 1  # 'n' is the number of control points
257b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		assert n >= 0
258934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr		if points[-1] is None:
259934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# Special case for TrueType quadratics: it is possible to
260934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# define a contour with NO on-curve points. BasePen supports
261934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# this by allowing the final argument (the expected on-curve
262934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# point) to be None. We simulate the feature by making the implied
263934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# on-curve point between the last and the first off-curve points
264934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			# explicit.
265934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			x, y = points[-2]  # last off-curve point
266934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			nx, ny = points[0] # first off-curve point
267934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			impliedStartPoint = (0.5 * (x + nx), 0.5 * (y + ny))
268934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self.__currentPoint = impliedStartPoint
269934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			self._moveTo(impliedStartPoint)
270934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr			points = points[:-1] + (impliedStartPoint,)
271b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		if n > 0:
27282ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# Split the string of points into discrete quadratic curve
27382ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# segments. Between any two consecutive off-curve points
27482ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# there's an implied on-curve point exactly in the middle.
27582ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr			# This is where the segment splits.
276b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			_qCurveToOne = self._qCurveToOne
27723cb20053223695a2fb1dd68920297ed8471d77fjvr			for pt1, pt2 in decomposeQuadraticSegment(points):
27823cb20053223695a2fb1dd68920297ed8471d77fjvr				_qCurveToOne(pt1, pt2)
27923cb20053223695a2fb1dd68920297ed8471d77fjvr				self.__currentPoint = pt2
280b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr		else:
281b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr			self.lineTo(points[0])
282b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
283b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
28423cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeSuperBezierSegment(points):
28523cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the SuperBezier described by 'points' into a list of regular
28623cb20053223695a2fb1dd68920297ed8471d77fjvr	bezier segments. The 'points' argument must be a sequence with length
28723cb20053223695a2fb1dd68920297ed8471d77fjvr	3 or greater, containing (x, y) coordinates. The last point is the
28823cb20053223695a2fb1dd68920297ed8471d77fjvr	destination on-curve point, the rest of the points are off-curve points.
28923cb20053223695a2fb1dd68920297ed8471d77fjvr	The start point should not be supplied.
29023cb20053223695a2fb1dd68920297ed8471d77fjvr
29123cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2, pt3) tuples, which each
29223cb20053223695a2fb1dd68920297ed8471d77fjvr	specify a regular curveto-style bezier segment.
29323cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
29423cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
29523cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 1
29623cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments = []
29723cb20053223695a2fb1dd68920297ed8471d77fjvr	pt1, pt2, pt3 = points[0], None, None
29823cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(2, n+1):
29923cb20053223695a2fb1dd68920297ed8471d77fjvr		# calculate points in between control points.
30023cb20053223695a2fb1dd68920297ed8471d77fjvr		nDivisions = min(i, 3, n-i+2)
30123cb20053223695a2fb1dd68920297ed8471d77fjvr		for j in range(1, nDivisions):
30232c10eecffb4923e0721c395e4b80fb732543f18Behdad Esfahbod			factor = j / nDivisions
30323cb20053223695a2fb1dd68920297ed8471d77fjvr			temp1 = points[i-1]
30423cb20053223695a2fb1dd68920297ed8471d77fjvr			temp2 = points[i-2]
30523cb20053223695a2fb1dd68920297ed8471d77fjvr			temp = (temp2[0] + factor * (temp1[0] - temp2[0]),
30623cb20053223695a2fb1dd68920297ed8471d77fjvr					temp2[1] + factor * (temp1[1] - temp2[1]))
30723cb20053223695a2fb1dd68920297ed8471d77fjvr			if pt2 is None:
30823cb20053223695a2fb1dd68920297ed8471d77fjvr				pt2 = temp
30923cb20053223695a2fb1dd68920297ed8471d77fjvr			else:
31023cb20053223695a2fb1dd68920297ed8471d77fjvr				pt3 = (0.5 * (pt2[0] + temp[0]),
31123cb20053223695a2fb1dd68920297ed8471d77fjvr					   0.5 * (pt2[1] + temp[1]))
31223cb20053223695a2fb1dd68920297ed8471d77fjvr				bezierSegments.append((pt1, pt2, pt3))
31323cb20053223695a2fb1dd68920297ed8471d77fjvr				pt1, pt2, pt3 = temp, None, None
31423cb20053223695a2fb1dd68920297ed8471d77fjvr	bezierSegments.append((pt1, points[-2], points[-1]))
31523cb20053223695a2fb1dd68920297ed8471d77fjvr	return bezierSegments
31623cb20053223695a2fb1dd68920297ed8471d77fjvr
31723cb20053223695a2fb1dd68920297ed8471d77fjvr
31823cb20053223695a2fb1dd68920297ed8471d77fjvrdef decomposeQuadraticSegment(points):
31923cb20053223695a2fb1dd68920297ed8471d77fjvr	"""Split the quadratic curve segment described by 'points' into a list
32023cb20053223695a2fb1dd68920297ed8471d77fjvr	of "atomic" quadratic segments. The 'points' argument must be a sequence
32123cb20053223695a2fb1dd68920297ed8471d77fjvr	with length 2 or greater, containing (x, y) coordinates. The last point
32223cb20053223695a2fb1dd68920297ed8471d77fjvr	is the destination on-curve point, the rest of the points are off-curve
32323cb20053223695a2fb1dd68920297ed8471d77fjvr	points. The start point should not be supplied.
32423cb20053223695a2fb1dd68920297ed8471d77fjvr
32523cb20053223695a2fb1dd68920297ed8471d77fjvr	This function returns a list of (pt1, pt2) tuples, which each specify a
32623cb20053223695a2fb1dd68920297ed8471d77fjvr	plain quadratic bezier segment.
32723cb20053223695a2fb1dd68920297ed8471d77fjvr	"""
32823cb20053223695a2fb1dd68920297ed8471d77fjvr	n = len(points) - 1
32923cb20053223695a2fb1dd68920297ed8471d77fjvr	assert n > 0
33023cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments = []
33123cb20053223695a2fb1dd68920297ed8471d77fjvr	for i in range(n - 1):
33223cb20053223695a2fb1dd68920297ed8471d77fjvr		x, y = points[i]
33323cb20053223695a2fb1dd68920297ed8471d77fjvr		nx, ny = points[i+1]
33423cb20053223695a2fb1dd68920297ed8471d77fjvr		impliedPt = (0.5 * (x + nx), 0.5 * (y + ny))
33523cb20053223695a2fb1dd68920297ed8471d77fjvr		quadSegments.append((points[i], impliedPt))
33623cb20053223695a2fb1dd68920297ed8471d77fjvr	quadSegments.append((points[-2], points[-1]))
33723cb20053223695a2fb1dd68920297ed8471d77fjvr	return quadSegments
33823cb20053223695a2fb1dd68920297ed8471d77fjvr
33923cb20053223695a2fb1dd68920297ed8471d77fjvr
340b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass _TestPen(BasePen):
34140cde70f1640b8a25655fba4ee3ce7a9d5ca962ejvr	"""Test class that prints PostScript to stdout."""
342b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _moveTo(self, pt):
3433ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("%s %s moveto" % (pt[0], pt[1]))
344b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _lineTo(self, pt):
3453ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("%s %s lineto" % (pt[0], pt[1]))
346b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _curveToOne(self, bcp1, bcp2, pt):
3473ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("%s %s %s %s %s %s curveto" % (bcp1[0], bcp1[1],
3483ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod				bcp2[0], bcp2[1], pt[0], pt[1]))
349b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	def _closePath(self):
3503ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod		print("closepath")
351b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
352b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
353b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrif __name__ == "__main__":
354b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
355b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.moveTo((0, 0))
356b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.lineTo((0, 100))
35723cb20053223695a2fb1dd68920297ed8471d77fjvr	pen.curveTo((50, 75), (60, 50), (50, 25), (0, 0))
358b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
359b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr
360b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen = _TestPen(None)
361934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	# testing the "no on-curve point" scenario
362934fe5fb8fd2ef80d9ec014513ca5e3a0fb9e325jvr	pen.qCurveTo((0, 0), (0, 100), (100, 100), (100, 0), None)
363b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr	pen.closePath()
364