Initial implementation of SVG path parser
This commit is contained in:
parent
61abfc73ac
commit
c23a868513
1 changed files with 118 additions and 0 deletions
|
@ -1,13 +1,131 @@
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
import enum
|
||||||
import cairo
|
import cairo
|
||||||
|
|
||||||
|
class State(enum.Enum):
|
||||||
|
NONE = 0
|
||||||
|
COMMAND = 1
|
||||||
|
INT = 2
|
||||||
|
FLOAT = 3
|
||||||
|
|
||||||
|
class ParseError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class PathCommand():
|
||||||
|
__slots__ = 'command', 'args', 'arg',
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.command = None
|
||||||
|
self.args = list()
|
||||||
|
self.arg = ''
|
||||||
|
|
||||||
|
def set_command(self, command: str):
|
||||||
|
self.command = command
|
||||||
|
|
||||||
|
def arg_addch(self, ch: str):
|
||||||
|
self.arg += ch
|
||||||
|
|
||||||
|
def arg_next(self, ch: str):
|
||||||
|
if len(self.arg) == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.arg += ch
|
||||||
|
self.args.append(self.arg)
|
||||||
|
self.arg = ''
|
||||||
|
|
||||||
|
def arg_finish(self):
|
||||||
|
if len(self.arg) > 0:
|
||||||
|
self.args.append(self.arg)
|
||||||
|
|
||||||
|
self.arg = ''
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
if len(self.arg) > 0:
|
||||||
|
self.args.append(self.arg)
|
||||||
|
|
||||||
|
ret = [self.command, self.args]
|
||||||
|
|
||||||
|
self.command = None
|
||||||
|
self.args = list()
|
||||||
|
self.arg = ''
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
class Path():
|
class Path():
|
||||||
__slots__ = 'commands',
|
__slots__ = 'commands',
|
||||||
|
|
||||||
def __init__(self, commands: Iterable):
|
def __init__(self, commands: Iterable):
|
||||||
self.commands = commands
|
self.commands = commands
|
||||||
|
|
||||||
|
def is_command(c):
|
||||||
|
return (c >= ord('a') and c <= ord('z')) or (c >= ord('A') and c <= ord('Z'))
|
||||||
|
|
||||||
|
def is_number(c):
|
||||||
|
return (c >= ord('0') and c <= ord('9'))
|
||||||
|
|
||||||
|
def parse(text: str) -> list:
|
||||||
|
commands = list()
|
||||||
|
command = PathCommand()
|
||||||
|
state = State.NONE
|
||||||
|
|
||||||
|
for ch in text:
|
||||||
|
c = ord(ch)
|
||||||
|
|
||||||
|
if state is State.NONE:
|
||||||
|
if Path.is_command(c):
|
||||||
|
command.set_command(ch)
|
||||||
|
state = State.COMMAND
|
||||||
|
elif ch == ' ':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise ParseError()
|
||||||
|
elif state is State.COMMAND:
|
||||||
|
if Path.is_command(c):
|
||||||
|
commands.append(command.finish())
|
||||||
|
command.set_command(ch)
|
||||||
|
elif Path.is_number(c) or ch == '-':
|
||||||
|
state = State.INT
|
||||||
|
command.arg_addch(ch)
|
||||||
|
elif ch == '.':
|
||||||
|
state = State.FLOAT
|
||||||
|
command.arg_addch(ch)
|
||||||
|
elif ch == ' ':
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise ParseError()
|
||||||
|
elif state is State.INT:
|
||||||
|
if Path.is_command(c):
|
||||||
|
commands.append(command.finish())
|
||||||
|
command.set_command(ch)
|
||||||
|
elif Path.is_number(c):
|
||||||
|
command.arg_addch(ch)
|
||||||
|
elif ch == '.':
|
||||||
|
command.arg_addch(ch)
|
||||||
|
state = State.FLOAT
|
||||||
|
elif ch == '-':
|
||||||
|
command.arg_next(ch)
|
||||||
|
elif ch == ' ' or ch == ',':
|
||||||
|
command.arg_finish()
|
||||||
|
else:
|
||||||
|
raise ParseError()
|
||||||
|
elif state is State.FLOAT:
|
||||||
|
if Path.is_command(c):
|
||||||
|
commands.append(command.finish())
|
||||||
|
command.set_command(ch)
|
||||||
|
elif Path.is_number(c):
|
||||||
|
command.arg_addch(ch)
|
||||||
|
elif ch == ' ' or ch == ',':
|
||||||
|
state = State.INT
|
||||||
|
command.arg_finish()
|
||||||
|
else:
|
||||||
|
raise ParseError(ch)
|
||||||
|
|
||||||
|
if command.command is not None:
|
||||||
|
commands.append(command.finish())
|
||||||
|
|
||||||
|
return commands
|
||||||
|
|
||||||
def horiz_to(self, cr: cairo.Context, x2: float):
|
def horiz_to(self, cr: cairo.Context, x2: float):
|
||||||
_, y = cr.get_current_point()
|
_, y = cr.get_current_point()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue