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