aboutsummaryrefslogtreecommitdiff
path: root/Lib/fontTools/svgLib/path/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/fontTools/svgLib/path/parser.py')
-rw-r--r--Lib/fontTools/svgLib/path/parser.py72
1 files changed, 66 insertions, 6 deletions
diff --git a/Lib/fontTools/svgLib/path/parser.py b/Lib/fontTools/svgLib/path/parser.py
index bdf3de0c..1fcf8998 100644
--- a/Lib/fontTools/svgLib/path/parser.py
+++ b/Lib/fontTools/svgLib/path/parser.py
@@ -7,26 +7,86 @@
# Copyright (c) 2013-2014 Lennart Regebro
# License: MIT
-from __future__ import (
- print_function, division, absolute_import, unicode_literals)
-from fontTools.misc.py23 import *
from .arc import EllipticalArc
import re
COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
+ARC_COMMANDS = set("Aa")
UPPERCASE = set('MZLHVCSQTA')
COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
-FLOAT_RE = re.compile(r"[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")
+FLOAT_RE = re.compile(
+ r"[-+]?" # optional sign
+ r"(?:"
+ r"(?:0|[1-9][0-9]*)(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)?" # int/float
+ r"|"
+ r"(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)" # float with leading dot (e.g. '.42')
+ r")"
+)
+BOOL_RE = re.compile("^[01]")
+SEPARATOR_RE = re.compile(f"[, \t]")
def _tokenize_path(pathdef):
+ arc_cmd = None
for x in COMMAND_RE.split(pathdef):
if x in COMMANDS:
+ arc_cmd = x if x in ARC_COMMANDS else None
yield x
- for token in FLOAT_RE.findall(x):
- yield token
+ continue
+
+ if arc_cmd:
+ try:
+ yield from _tokenize_arc_arguments(x)
+ except ValueError as e:
+ raise ValueError(f"Invalid arc command: '{arc_cmd}{x}'") from e
+ else:
+ for token in FLOAT_RE.findall(x):
+ yield token
+
+
+ARC_ARGUMENT_TYPES = (
+ ("rx", FLOAT_RE),
+ ("ry", FLOAT_RE),
+ ("x-axis-rotation", FLOAT_RE),
+ ("large-arc-flag", BOOL_RE),
+ ("sweep-flag", BOOL_RE),
+ ("x", FLOAT_RE),
+ ("y", FLOAT_RE),
+)
+
+
+def _tokenize_arc_arguments(arcdef):
+ raw_args = [s for s in SEPARATOR_RE.split(arcdef) if s]
+ if not raw_args:
+ raise ValueError(f"Not enough arguments: '{arcdef}'")
+ raw_args.reverse()
+
+ i = 0
+ while raw_args:
+ arg = raw_args.pop()
+
+ name, pattern = ARC_ARGUMENT_TYPES[i]
+ match = pattern.search(arg)
+ if not match:
+ raise ValueError(f"Invalid argument for '{name}' parameter: {arg!r}")
+
+ j, k = match.span()
+ yield arg[j:k]
+ arg = arg[k:]
+
+ if arg:
+ raw_args.append(arg)
+
+ # wrap around every 7 consecutive arguments
+ if i == 6:
+ i = 0
+ else:
+ i += 1
+
+ if i != 0:
+ raise ValueError(f"Not enough arguments: '{arcdef}'")
def parse_path(pathdef, pen, current_pos=(0, 0), arc_class=EllipticalArc):