1from fontTools.pens.basePen import BasePen 2 3 4def pointToString(pt): 5 return " ".join([str(i) for i in pt]) 6 7 8class SVGPathPen(BasePen): 9 10 def __init__(self, glyphSet): 11 BasePen.__init__(self, glyphSet) 12 self._commands = [] 13 self._lastCommand = None 14 self._lastX = None 15 self._lastY = None 16 17 def _handleAnchor(self): 18 """ 19 >>> pen = SVGPathPen(None) 20 >>> pen.moveTo((0, 0)) 21 >>> pen.moveTo((10, 10)) 22 >>> pen._commands 23 ['M10 10'] 24 """ 25 if self._lastCommand == "M": 26 self._commands.pop(-1) 27 28 def _moveTo(self, pt): 29 """ 30 >>> pen = SVGPathPen(None) 31 >>> pen.moveTo((0, 0)) 32 >>> pen._commands 33 ['M0 0'] 34 35 >>> pen = SVGPathPen(None) 36 >>> pen.moveTo((10, 0)) 37 >>> pen._commands 38 ['M10 0'] 39 40 >>> pen = SVGPathPen(None) 41 >>> pen.moveTo((0, 10)) 42 >>> pen._commands 43 ['M0 10'] 44 """ 45 self._handleAnchor() 46 t = "M%s" % (pointToString(pt)) 47 self._commands.append(t) 48 self._lastCommand = "M" 49 self._lastX, self._lastY = pt 50 51 def _lineTo(self, pt): 52 """ 53 # duplicate point 54 >>> pen = SVGPathPen(None) 55 >>> pen.moveTo((10, 10)) 56 >>> pen.lineTo((10, 10)) 57 >>> pen._commands 58 ['M10 10'] 59 60 # vertical line 61 >>> pen = SVGPathPen(None) 62 >>> pen.moveTo((10, 10)) 63 >>> pen.lineTo((10, 0)) 64 >>> pen._commands 65 ['M10 10', 'V0'] 66 67 # horizontal line 68 >>> pen = SVGPathPen(None) 69 >>> pen.moveTo((10, 10)) 70 >>> pen.lineTo((0, 10)) 71 >>> pen._commands 72 ['M10 10', 'H0'] 73 74 # basic 75 >>> pen = SVGPathPen(None) 76 >>> pen.lineTo((70, 80)) 77 >>> pen._commands 78 ['L70 80'] 79 80 # basic following a moveto 81 >>> pen = SVGPathPen(None) 82 >>> pen.moveTo((0, 0)) 83 >>> pen.lineTo((10, 10)) 84 >>> pen._commands 85 ['M0 0', ' 10 10'] 86 """ 87 x, y = pt 88 # duplicate point 89 if x == self._lastX and y == self._lastY: 90 return 91 # vertical line 92 elif x == self._lastX: 93 cmd = "V" 94 pts = str(y) 95 # horizontal line 96 elif y == self._lastY: 97 cmd = "H" 98 pts = str(x) 99 # previous was a moveto 100 elif self._lastCommand == "M": 101 cmd = None 102 pts = " " + pointToString(pt) 103 # basic 104 else: 105 cmd = "L" 106 pts = pointToString(pt) 107 # write the string 108 t = "" 109 if cmd: 110 t += cmd 111 self._lastCommand = cmd 112 t += pts 113 self._commands.append(t) 114 # store for future reference 115 self._lastX, self._lastY = pt 116 117 def _curveToOne(self, pt1, pt2, pt3): 118 """ 119 >>> pen = SVGPathPen(None) 120 >>> pen.curveTo((10, 20), (30, 40), (50, 60)) 121 >>> pen._commands 122 ['C10 20 30 40 50 60'] 123 """ 124 t = "C" 125 t += pointToString(pt1) + " " 126 t += pointToString(pt2) + " " 127 t += pointToString(pt3) 128 self._commands.append(t) 129 self._lastCommand = "C" 130 self._lastX, self._lastY = pt3 131 132 def _qCurveToOne(self, pt1, pt2): 133 """ 134 >>> pen = SVGPathPen(None) 135 >>> pen.qCurveTo((10, 20), (30, 40)) 136 >>> pen._commands 137 ['Q10 20 30 40'] 138 """ 139 assert pt2 is not None 140 t = "Q" 141 t += pointToString(pt1) + " " 142 t += pointToString(pt2) 143 self._commands.append(t) 144 self._lastCommand = "Q" 145 self._lastX, self._lastY = pt2 146 147 def _closePath(self): 148 """ 149 >>> pen = SVGPathPen(None) 150 >>> pen.closePath() 151 >>> pen._commands 152 ['Z'] 153 """ 154 self._commands.append("Z") 155 self._lastCommand = "Z" 156 self._lastX = self._lastY = None 157 158 def _endPath(self): 159 """ 160 >>> pen = SVGPathPen(None) 161 >>> pen.endPath() 162 >>> pen._commands 163 ['Z'] 164 """ 165 self._closePath() 166 self._lastCommand = None 167 self._lastX = self._lastY = None 168 169 def getCommands(self): 170 return "".join(self._commands) 171 172 173if __name__ == "__main__": 174 import sys 175 import doctest 176 sys.exit(doctest.testmod().failed) 177