Add SVGPen
Copied and slightly adapted from: https://github.com/typesupply/ufo2svg/blob/master/Lib/ufo2svg/svgPathPen.py
This commit is contained in:
parent
0df9b989be
commit
0355d967a0
215
Lib/fontTools/pens/svgPen.py
Normal file
215
Lib/fontTools/pens/svgPen.py
Normal file
@ -0,0 +1,215 @@
|
||||
from __future__ import print_function, division, absolute_import
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.pens.basePen import BasePen
|
||||
|
||||
# Number to String Conversion
|
||||
|
||||
def valueToString(v):
|
||||
"""
|
||||
>>> valueToString(0)
|
||||
'0'
|
||||
>>> valueToString(10)
|
||||
'10'
|
||||
>>> valueToString(-10)
|
||||
'-10'
|
||||
>>> valueToString(0.1)
|
||||
'0.1'
|
||||
>>> valueToString(0.0001)
|
||||
'0.0001'
|
||||
>>> valueToString(0.00001)
|
||||
'0'
|
||||
>>> valueToString(10.0001)
|
||||
'10.0001'
|
||||
>>> valueToString(10.00001)
|
||||
'10'
|
||||
"""
|
||||
if int(v) == v:
|
||||
v = "%d" % (int(v))
|
||||
else:
|
||||
v = "%.4f" % v
|
||||
# strip unnecessary zeros
|
||||
# there is probably an easier way to do this
|
||||
compiled = []
|
||||
for c in reversed(v):
|
||||
if not compiled and c == "0":
|
||||
continue
|
||||
compiled.append(c)
|
||||
v = "".join(reversed(compiled))
|
||||
if v.endswith("."):
|
||||
v = v[:-1]
|
||||
if not v:
|
||||
v = "0"
|
||||
return v
|
||||
|
||||
def pointToString(pt):
|
||||
return " ".join([valueToString(i) for i in pt])
|
||||
|
||||
class SVGPen(BasePen):
|
||||
|
||||
def __init__(self, glyphSet):
|
||||
BasePen.__init__(self, glyphSet)
|
||||
self._commands = []
|
||||
self._lastCommand = None
|
||||
self._lastX = None
|
||||
self._lastY = None
|
||||
|
||||
def _handleAnchor(self):
|
||||
"""
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((0, 0))
|
||||
>>> pen.moveTo((10, 10))
|
||||
>>> pen._commands
|
||||
['M10 10']
|
||||
"""
|
||||
if self._lastCommand == "M":
|
||||
self._commands.pop(-1)
|
||||
|
||||
def _moveTo(self, pt):
|
||||
"""
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((0, 0))
|
||||
>>> pen._commands
|
||||
['M0 0']
|
||||
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((10, 0))
|
||||
>>> pen._commands
|
||||
['M10 0']
|
||||
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((0, 10))
|
||||
>>> pen._commands
|
||||
['M0 10']
|
||||
"""
|
||||
self._handleAnchor()
|
||||
t = "M%s" % (pointToString(pt))
|
||||
self._commands.append(t)
|
||||
self._lastCommand = "M"
|
||||
self._lastX, self._lastY = pt
|
||||
|
||||
def _lineTo(self, pt):
|
||||
"""
|
||||
# duplicate point
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((10, 10))
|
||||
>>> pen.lineTo((10, 10))
|
||||
>>> pen._commands
|
||||
['M10 10']
|
||||
|
||||
# vertical line
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((10, 10))
|
||||
>>> pen.lineTo((10, 0))
|
||||
>>> pen._commands
|
||||
['M10 10', 'V0']
|
||||
|
||||
# horizontal line
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((10, 10))
|
||||
>>> pen.lineTo((0, 10))
|
||||
>>> pen._commands
|
||||
['M10 10', 'H0']
|
||||
|
||||
# basic
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.lineTo((70, 80))
|
||||
>>> pen._commands
|
||||
['L70 80']
|
||||
|
||||
# basic following a moveto
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.moveTo((0, 0))
|
||||
>>> pen.lineTo((10, 10))
|
||||
>>> pen._commands
|
||||
['M0 0', ' 10 10']
|
||||
"""
|
||||
x, y = pt
|
||||
# duplicate point
|
||||
if x == self._lastX and y == self._lastY:
|
||||
return
|
||||
# vertical line
|
||||
elif x == self._lastX:
|
||||
cmd = "V"
|
||||
pts = valueToString(y)
|
||||
# horizontal line
|
||||
elif y == self._lastY:
|
||||
cmd = "H"
|
||||
pts = valueToString(x)
|
||||
# previous was a moveto
|
||||
elif self._lastCommand == "M":
|
||||
cmd = None
|
||||
pts = " " + pointToString(pt)
|
||||
# basic
|
||||
else:
|
||||
cmd = "L"
|
||||
pts = pointToString(pt)
|
||||
# write the string
|
||||
t = ""
|
||||
if cmd:
|
||||
t += cmd
|
||||
self._lastCommand = cmd
|
||||
t += pts
|
||||
self._commands.append(t)
|
||||
# store for future reference
|
||||
self._lastX, self._lastY = pt
|
||||
|
||||
def _curveToOne(self, pt1, pt2, pt3):
|
||||
"""
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.curveTo((10, 20), (30, 40), (50, 60))
|
||||
>>> pen._commands
|
||||
['C10 20 30 40 50 60']
|
||||
"""
|
||||
t = "C"
|
||||
t += pointToString(pt1) + " "
|
||||
t += pointToString(pt2) + " "
|
||||
t += pointToString(pt3)
|
||||
self._commands.append(t)
|
||||
self._lastCommand = "C"
|
||||
self._lastX, self._lastY = pt3
|
||||
|
||||
def _qCurveToOne(self, pt1, pt2):
|
||||
"""
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.qCurveTo((10, 20), (30, 40))
|
||||
>>> pen._commands
|
||||
['Q10 20 30 40']
|
||||
"""
|
||||
assert pt2 is not None
|
||||
t = "Q"
|
||||
t += pointToString(pt1) + " "
|
||||
t += pointToString(pt2)
|
||||
self._commands.append(t)
|
||||
self._lastCommand = "Q"
|
||||
self._lastX, self._lastY = pt2
|
||||
|
||||
def _closePath(self):
|
||||
"""
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.closePath()
|
||||
>>> pen._commands
|
||||
['Z']
|
||||
"""
|
||||
self._commands.append("Z")
|
||||
self._lastCommand = "Z"
|
||||
self._lastX = self._lastY = None
|
||||
|
||||
def _endPath(self):
|
||||
"""
|
||||
>>> pen = SVGPen(None)
|
||||
>>> pen.endPath()
|
||||
>>> pen._commands
|
||||
['Z']
|
||||
"""
|
||||
self._closePath()
|
||||
self._lastCommand = None
|
||||
self._lastX = self._lastY = None
|
||||
|
||||
def getCommands(self):
|
||||
return "".join(self._commands)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import doctest
|
||||
sys.exit(doctest.testmod().failed)
|
Loading…
x
Reference in New Issue
Block a user