| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						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"[-+]?"   | 
					
					
						
						| 
							 | 
						    r"(?:" | 
					
					
						
						| 
							 | 
						    r"(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?"   | 
					
					
						
						| 
							 | 
						    r"|" | 
					
					
						
						| 
							 | 
						    r"(?:\.[0-9]+(?:[eE][-+]?[0-9]+)?)"   | 
					
					
						
						| 
							 | 
						    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 | 
					
					
						
						| 
							 | 
						            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) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						         | 
					
					
						
						| 
							 | 
						        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): | 
					
					
						
						| 
							 | 
						    """Parse SVG path definition (i.e. "d" attribute of <path> elements) | 
					
					
						
						| 
							 | 
						    and call a 'pen' object's moveTo, lineTo, curveTo, qCurveTo and closePath | 
					
					
						
						| 
							 | 
						    methods. | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						    If 'current_pos' (2-float tuple) is provided, the initial moveTo will | 
					
					
						
						| 
							 | 
						    be relative to that instead being absolute. | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						    If the pen has an "arcTo" method, it is called with the original values | 
					
					
						
						| 
							 | 
						    of the elliptical arc curve commands: | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						        pen.arcTo(rx, ry, rotation, arc_large, arc_sweep, (x, y)) | 
					
					
						
						| 
							 | 
						 | 
					
					
						
						| 
							 | 
						    Otherwise, the arcs are approximated by series of cubic Bezier segments | 
					
					
						
						| 
							 | 
						    ("curveTo"), one every 90 degrees. | 
					
					
						
						| 
							 | 
						    """ | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    current_pos = complex(*current_pos) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    elements = list(_tokenize_path(pathdef)) | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    elements.reverse() | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    start_pos = None | 
					
					
						
						| 
							 | 
						    command = None | 
					
					
						
						| 
							 | 
						    last_control = None | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    have_arcTo = hasattr(pen, "arcTo") | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						    while elements: | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        if elements[-1] in COMMANDS: | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            last_command = command   | 
					
					
						
						| 
							 | 
						            command = elements.pop() | 
					
					
						
						| 
							 | 
						            absolute = command in UPPERCASE | 
					
					
						
						| 
							 | 
						            command = command.upper() | 
					
					
						
						| 
							 | 
						        else: | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if command is None: | 
					
					
						
						| 
							 | 
						                raise ValueError( | 
					
					
						
						| 
							 | 
						                    "Unallowed implicit command in %s, position %s" | 
					
					
						
						| 
							 | 
						                    % (pathdef, len(pathdef.split()) - len(elements)) | 
					
					
						
						| 
							 | 
						                ) | 
					
					
						
						| 
							 | 
						            last_command = command   | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        if command == "M": | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            x = elements.pop() | 
					
					
						
						| 
							 | 
						            y = elements.pop() | 
					
					
						
						| 
							 | 
						            pos = float(x) + float(y) * 1j | 
					
					
						
						| 
							 | 
						            if absolute: | 
					
					
						
						| 
							 | 
						                current_pos = pos | 
					
					
						
						| 
							 | 
						            else: | 
					
					
						
						| 
							 | 
						                current_pos += pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if start_pos is not None: | 
					
					
						
						| 
							 | 
						                pen.endPath() | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            pen.moveTo((current_pos.real, current_pos.imag)) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            start_pos = current_pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            command = "L" | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "Z": | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if current_pos != start_pos: | 
					
					
						
						| 
							 | 
						                pen.lineTo((start_pos.real, start_pos.imag)) | 
					
					
						
						| 
							 | 
						            pen.closePath() | 
					
					
						
						| 
							 | 
						            current_pos = start_pos | 
					
					
						
						| 
							 | 
						            start_pos = None | 
					
					
						
						| 
							 | 
						            command = None   | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "L": | 
					
					
						
						| 
							 | 
						            x = elements.pop() | 
					
					
						
						| 
							 | 
						            y = elements.pop() | 
					
					
						
						| 
							 | 
						            pos = float(x) + float(y) * 1j | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                pos += current_pos | 
					
					
						
						| 
							 | 
						            pen.lineTo((pos.real, pos.imag)) | 
					
					
						
						| 
							 | 
						            current_pos = pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "H": | 
					
					
						
						| 
							 | 
						            x = elements.pop() | 
					
					
						
						| 
							 | 
						            pos = float(x) + current_pos.imag * 1j | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                pos += current_pos.real | 
					
					
						
						| 
							 | 
						            pen.lineTo((pos.real, pos.imag)) | 
					
					
						
						| 
							 | 
						            current_pos = pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "V": | 
					
					
						
						| 
							 | 
						            y = elements.pop() | 
					
					
						
						| 
							 | 
						            pos = current_pos.real + float(y) * 1j | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                pos += current_pos.imag * 1j | 
					
					
						
						| 
							 | 
						            pen.lineTo((pos.real, pos.imag)) | 
					
					
						
						| 
							 | 
						            current_pos = pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "C": | 
					
					
						
						| 
							 | 
						            control1 = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						            control2 = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						            end = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                control1 += current_pos | 
					
					
						
						| 
							 | 
						                control2 += current_pos | 
					
					
						
						| 
							 | 
						                end += current_pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            pen.curveTo( | 
					
					
						
						| 
							 | 
						                (control1.real, control1.imag), | 
					
					
						
						| 
							 | 
						                (control2.real, control2.imag), | 
					
					
						
						| 
							 | 
						                (end.real, end.imag), | 
					
					
						
						| 
							 | 
						            ) | 
					
					
						
						| 
							 | 
						            current_pos = end | 
					
					
						
						| 
							 | 
						            last_control = control2 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "S": | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if last_command not in "CS": | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                control1 = current_pos | 
					
					
						
						| 
							 | 
						            else: | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                control1 = current_pos + current_pos - last_control | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            control2 = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						            end = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                control2 += current_pos | 
					
					
						
						| 
							 | 
						                end += current_pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            pen.curveTo( | 
					
					
						
						| 
							 | 
						                (control1.real, control1.imag), | 
					
					
						
						| 
							 | 
						                (control2.real, control2.imag), | 
					
					
						
						| 
							 | 
						                (end.real, end.imag), | 
					
					
						
						| 
							 | 
						            ) | 
					
					
						
						| 
							 | 
						            current_pos = end | 
					
					
						
						| 
							 | 
						            last_control = control2 | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "Q": | 
					
					
						
						| 
							 | 
						            control = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						            end = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                control += current_pos | 
					
					
						
						| 
							 | 
						                end += current_pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            pen.qCurveTo((control.real, control.imag), (end.real, end.imag)) | 
					
					
						
						| 
							 | 
						            current_pos = end | 
					
					
						
						| 
							 | 
						            last_control = control | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "T": | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if last_command not in "QT": | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                control = current_pos | 
					
					
						
						| 
							 | 
						            else: | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                 | 
					
					
						
						| 
							 | 
						                control = current_pos + current_pos - last_control | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            end = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                end += current_pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            pen.qCurveTo((control.real, control.imag), (end.real, end.imag)) | 
					
					
						
						| 
							 | 
						            current_pos = end | 
					
					
						
						| 
							 | 
						            last_control = control | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						        elif command == "A": | 
					
					
						
						| 
							 | 
						            rx = abs(float(elements.pop())) | 
					
					
						
						| 
							 | 
						            ry = abs(float(elements.pop())) | 
					
					
						
						| 
							 | 
						            rotation = float(elements.pop()) | 
					
					
						
						| 
							 | 
						            arc_large = bool(int(elements.pop())) | 
					
					
						
						| 
							 | 
						            arc_sweep = bool(int(elements.pop())) | 
					
					
						
						| 
							 | 
						            end = float(elements.pop()) + float(elements.pop()) * 1j | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            if not absolute: | 
					
					
						
						| 
							 | 
						                end += current_pos | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						             | 
					
					
						
						| 
							 | 
						            if have_arcTo: | 
					
					
						
						| 
							 | 
						                pen.arcTo( | 
					
					
						
						| 
							 | 
						                    rx, | 
					
					
						
						| 
							 | 
						                    ry, | 
					
					
						
						| 
							 | 
						                    rotation, | 
					
					
						
						| 
							 | 
						                    arc_large, | 
					
					
						
						| 
							 | 
						                    arc_sweep, | 
					
					
						
						| 
							 | 
						                    (end.real, end.imag), | 
					
					
						
						| 
							 | 
						                ) | 
					
					
						
						| 
							 | 
						            else: | 
					
					
						
						| 
							 | 
						                arc = arc_class( | 
					
					
						
						| 
							 | 
						                    current_pos, rx, ry, rotation, arc_large, arc_sweep, end | 
					
					
						
						| 
							 | 
						                ) | 
					
					
						
						| 
							 | 
						                arc.draw(pen) | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						            current_pos = end | 
					
					
						
						| 
							 | 
						
 | 
					
					
						
						| 
							 | 
						     | 
					
					
						
						| 
							 | 
						    if start_pos is not None: | 
					
					
						
						| 
							 | 
						        pen.endPath() | 
					
					
						
						| 
							 | 
						
 |