I have a module in Python for dealing with SVG paths. One of the problems with this is that the SVG spec is obsessed with saving characters to a pointless extent. As such, this path is valid:
"M3.4E-34-2.2e56L23-34z"
And should be parsed as follows:
"M", "3.4E-34", "-2.2e56", "L", "23", "-34", "z"
As you see, anything non-ambiguous is allowed, including separating two numbers only by a minus sign, as long as that minus-sign is not preceded by an "E" or an "e", in which case it should be interpreted as an exponent of the first number. Letters are commands (except "E" and "e" of course), and both comma and any sort of whitespace is allowed as separators.
My module currently uses a rather ugly way of tokenizing the SVG path by multiple string replacements and then a split:
COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
def _tokenize_path_replace(pathdef):
# First handle negative exponents:
pathdef = pathdef.replace('e-', 'NEGEXP').replace('E-', 'NEGEXP')
# Commas and minus-signs are separators, just like spaces.
pathdef = pathdef.replace(',', ' ').replace('-', ' -')
pathdef = pathdef.replace('NEGEXP', 'e-')
# Commands are allowed without spaces around. Let's insert spaces so it's
# easier to split later.
for c in COMMANDS:
pathdef = pathdef.replace(c, ' %s ' % c)
# Split the path into elements
return pathdef.split()
This in fact is doing a total of 23 string replacements on the path, and this is easy, but seems like it should be slow. I tried doing this other ways, but to my surprise they were all slower. I did a character by character tokenizer, which took around 30-40% more time. A user of the module also suggested a regex:
import re
TOKEN_RE = re.compile("[MmZzLlHhVvCcSsQqTtAa]|[-+]?[0-9]*\.?[0-9]+(?:[eE]
[-+]?[0-9]+)?")
def _tokenize_path_replace(pathdef):
return TOKEN_RE.findall(pathdef)
To my surprise this was also slower than doing 23 string replacements, although just 20-30%.
One thing that could speed up this is if the two expressions in the regex could be merged to one, but I can't find a way of doing that. If some regex guru can, that would improve things.
Any other way of making a speedy parsing of SVG paths that I haven't though about would also be appreciated.
svgwrite
has the same problem. \$\endgroup\$re.split('([MmZzLlHhVvCcSsQqTtAa])', "M3.4E-34-2.2e56L23-34z")
and then parsing the coordinates separately (with a smaller regex) would speed things up? \$\endgroup\$