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
|
||||
|
||||
import enum
|
||||
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():
|
||||
__slots__ = 'commands',
|
||||
|
||||
def __init__(self, commands: Iterable):
|
||||
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):
|
||||
_, y = cr.get_current_point()
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue