svgPathPen: add option to customize number formatting (e.g. rounding)
Using a RoundingPen as a filter doesn't work for TrueType quadratic splines (qCurveTo), because the rounding would occur too early, before these get split into atomic quadratic Bezier segments (by the superclass BasePen.qCurveTo method), thus leaving unwanted floating-point coordinates in the SVG output. So here we add a 'ntos' parameter (by default simpli calls str()) that takes a Callable[[float], str] and can be used to customize the formatting of the numbers in SVG path coordinates.
This commit is contained in:
parent
d190a7c206
commit
fce1fa2da8
@ -1,18 +1,36 @@
|
||||
from typing import Callable
|
||||
from fontTools.pens.basePen import BasePen
|
||||
|
||||
|
||||
def pointToString(pt):
|
||||
return " ".join([str(i) for i in pt])
|
||||
def pointToString(pt, ntos=str):
|
||||
return " ".join(ntos(i) for i in pt)
|
||||
|
||||
|
||||
class SVGPathPen(BasePen):
|
||||
""" Pen to draw SVG path d commands.
|
||||
|
||||
def __init__(self, glyphSet):
|
||||
Example::
|
||||
>>> pen = SVGPathPen(None)
|
||||
>>> pen.moveTo((0, 0))
|
||||
>>> pen.lineTo((1, 1))
|
||||
>>> pen.curveTo((2, 2), (3, 3), (4, 4))
|
||||
>>> pen.closePath()
|
||||
>>> pen.getCommands()
|
||||
'M0 0 1 1C2 2 3 3 4 4Z'
|
||||
|
||||
Args:
|
||||
glyphSet: a dictionary of drawable glyph objects keyed by name
|
||||
used to resolve component references in composite glyphs.
|
||||
ntos: a callable that takes a number and returns a string, to
|
||||
customize how numbers are formatted (default: str).
|
||||
"""
|
||||
def __init__(self, glyphSet, ntos: Callable[[float], str] = str):
|
||||
BasePen.__init__(self, glyphSet)
|
||||
self._commands = []
|
||||
self._lastCommand = None
|
||||
self._lastX = None
|
||||
self._lastY = None
|
||||
self._ntos = ntos
|
||||
|
||||
def _handleAnchor(self):
|
||||
"""
|
||||
@ -43,7 +61,7 @@ class SVGPathPen(BasePen):
|
||||
['M0 10']
|
||||
"""
|
||||
self._handleAnchor()
|
||||
t = "M%s" % (pointToString(pt))
|
||||
t = "M%s" % (pointToString(pt, self._ntos))
|
||||
self._commands.append(t)
|
||||
self._lastCommand = "M"
|
||||
self._lastX, self._lastY = pt
|
||||
@ -99,11 +117,11 @@ class SVGPathPen(BasePen):
|
||||
# previous was a moveto
|
||||
elif self._lastCommand == "M":
|
||||
cmd = None
|
||||
pts = " " + pointToString(pt)
|
||||
pts = " " + pointToString(pt, self._ntos)
|
||||
# basic
|
||||
else:
|
||||
cmd = "L"
|
||||
pts = pointToString(pt)
|
||||
pts = pointToString(pt, self._ntos)
|
||||
# write the string
|
||||
t = ""
|
||||
if cmd:
|
||||
@ -122,9 +140,9 @@ class SVGPathPen(BasePen):
|
||||
['C10 20 30 40 50 60']
|
||||
"""
|
||||
t = "C"
|
||||
t += pointToString(pt1) + " "
|
||||
t += pointToString(pt2) + " "
|
||||
t += pointToString(pt3)
|
||||
t += pointToString(pt1, self._ntos) + " "
|
||||
t += pointToString(pt2, self._ntos) + " "
|
||||
t += pointToString(pt3, self._ntos)
|
||||
self._commands.append(t)
|
||||
self._lastCommand = "C"
|
||||
self._lastX, self._lastY = pt3
|
||||
@ -135,11 +153,16 @@ class SVGPathPen(BasePen):
|
||||
>>> pen.qCurveTo((10, 20), (30, 40))
|
||||
>>> pen._commands
|
||||
['Q10 20 30 40']
|
||||
>>> from fontTools.misc.roundTools import otRound
|
||||
>>> pen = SVGPathPen(None, ntos=lambda v: str(otRound(v)))
|
||||
>>> pen.qCurveTo((3, 3), (7, 5), (11, 4))
|
||||
>>> pen._commands
|
||||
['Q3 3 5 4', 'Q7 5 11 4']
|
||||
"""
|
||||
assert pt2 is not None
|
||||
t = "Q"
|
||||
t += pointToString(pt1) + " "
|
||||
t += pointToString(pt2)
|
||||
t += pointToString(pt1, self._ntos) + " "
|
||||
t += pointToString(pt2, self._ntos)
|
||||
self._commands.append(t)
|
||||
self._lastCommand = "Q"
|
||||
self._lastX, self._lastY = pt2
|
||||
|
Loading…
x
Reference in New Issue
Block a user