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