def svg_path_to_painter_path(d): ''' Convert a tiny SVG 1.2 path into a QPainterPath. :param d: The value of the d attribute of an SVG <path> tag ''' from PyQt5.Qt import QPainterPath cmd = last_cmd = b'' path = QPainterPath() moveto_abs, moveto_rel = b'Mm' closepath1, closepath2 = b'Zz' lineto_abs, lineto_rel = b'Ll' hline_abs, hline_rel = b'Hh' vline_abs, vline_rel = b'Vv' curveto_abs, curveto_rel = b'Cc' smoothcurveto_abs, smoothcurveto_rel = b'Ss' quadcurveto_abs, quadcurveto_rel = b'Qq' smoothquadcurveto_abs, smoothquadcurveto_rel = b'Tt' # Store the last parsed values # x/y = end position # x1/y1 and x2/y2 = bezier control points x = y = x1 = y1 = x2 = y2 = 0 data = d.replace(b',', b' ').replace(b'\n', b' ') if isinstance(data, type('')): data = data.encode('ascii') end = len(data) data = ReadOnlyFileBuffer(data) def parse_float(): chars = [] while data.tell() < end: c = data.read(1) if c == b' ' and not chars: continue if c == b'-' or b'0' <= c[0] <= b'9' or c == b'.': chars.append(c[0]) else: break if not chars: raise ValueError('Premature end of input while expecting a number') return float(b''.join(chars)) def parse_floats(num, x_offset=0, y_offset=0): for i in range(num): val = parse_float() yield val + (x_offset if i % 2 == 0 else y_offset) repeated_command = None while data.tell() < end: last_cmd = cmd cmd = data.read(1) if repeated_command is None else repeated_command repeated_command = None if cmd == b' ': continue elif cmd == moveto_abs: x, y = parse_float(), parse_float() path.moveTo(x, y) elif cmd == moveto_rel: x += parse_float() y += parse_float() path.moveTo(x, y) elif cmd == closepath1 or cmd == closepath2: path.closeSubpath() elif cmd == lineto_abs: x, y = parse_floats(2) path.lineTo(x, y) elif cmd == lineto_rel: x += parse_float() y += parse_float() path.lineTo(x, y) elif cmd == hline_abs: x = parse_float() path.lineTo(x, y) elif cmd == hline_rel: x += parse_float() path.lineTo(x, y) elif cmd == vline_abs: y = parse_float() path.lineTo(x, y) elif cmd == vline_rel: y += parse_float() path.lineTo(x, y) elif cmd == curveto_abs: x1, y1, x2, y2, x, y = parse_floats(6) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == curveto_rel: x1, y1, x2, y2, x, y = parse_floats(6, x, y) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == smoothcurveto_abs: if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel: x1 = 2 * x - x2 y1 = 2 * y - y2 else: x1, y1 = x, y x2, y2, x, y = parse_floats(4) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == smoothcurveto_rel: if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel: x1 = 2 * x - x2 y1 = 2 * y - y2 else: x1, y1 = x, y x2, y2, x, y = parse_floats(4, x, y) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == quadcurveto_abs: x1, y1, x, y = parse_floats(4) path.quadTo(x1, y1, x, y) elif cmd == quadcurveto_rel: x1, y1, x, y = parse_floats(4, x, y) path.quadTo(x1, y1, x, y) elif cmd == smoothquadcurveto_abs: if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): x1 = 2 * x - x1 y1 = 2 * y - y1 else: x1, y1 = x, y x, y = parse_floats(2) path.quadTo(x1, y1, x, y) elif cmd == smoothquadcurveto_rel: if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): x1 = 2 * x - x1 y1 = 2 * y - y1 else: x1, y1 = x, y x, y = parse_floats(2, x, y) path.quadTo(x1, y1, x, y) elif cmd[0] in b'-.' or b'0' <= cmd[0] <= b'9': # A new number begins # In this case, multiple parameters tuples are specified for the last command # We rewind to reparse data correctly data.seek(-1, os.SEEK_CUR) # Handle extra parameters if last_cmd == moveto_abs: repeated_command = cmd = lineto_abs elif last_cmd == moveto_rel: repeated_command = cmd = lineto_rel elif last_cmd in (closepath1, closepath2): raise ValueError('Extra parameters after close path command') elif last_cmd in (lineto_abs, lineto_rel, hline_abs, hline_rel, vline_abs, vline_rel, curveto_abs, curveto_rel, smoothcurveto_abs, smoothcurveto_rel, quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): repeated_command = cmd = last_cmd else: raise ValueError('Unknown path command: %s' % cmd) return path
def svg_path_to_painter_path(d): ''' Convert a tiny SVG 1.2 path into a QPainterPath. :param d: The value of the d attribute of an SVG <path> tag ''' from PyQt5.Qt import QPainterPath cmd = last_cmd = b'' path = QPainterPath() moveto_abs, moveto_rel = b'M', b'm' closepath1, closepath2 = b'Z', b'z' lineto_abs, lineto_rel = b'L', b'l' hline_abs, hline_rel = b'H', b'h' vline_abs, vline_rel = b'V', b'v' curveto_abs, curveto_rel = b'C', b'c' smoothcurveto_abs, smoothcurveto_rel = b'S', b's' quadcurveto_abs, quadcurveto_rel = b'Q', b'q' smoothquadcurveto_abs, smoothquadcurveto_rel = b'T', b't' # Store the last parsed values # x/y = end position # x1/y1 and x2/y2 = bezier control points x = y = x1 = y1 = x2 = y2 = 0 if isinstance(d, unicode_type): d = d.encode('ascii') d = d.replace(b',', b' ').replace(b'\n', b' ') end = len(d) pos = [0] def read_byte(): p = pos[0] pos[0] += 1 return d[p:p + 1] def parse_float(): chars = [] while pos[0] < end: c = read_byte() if c == b' ' and not chars: continue if c in b'-.0123456789': chars.append(c) else: break if not chars: raise ValueError('Premature end of input while expecting a number') return float(b''.join(chars)) def parse_floats(num, x_offset=0, y_offset=0): for i in range(num): val = parse_float() yield val + (x_offset if i % 2 == 0 else y_offset) repeated_command = None while pos[0] < end: last_cmd = cmd cmd = read_byte() if repeated_command is None else repeated_command repeated_command = None if cmd == b' ': continue if cmd == moveto_abs: x, y = parse_float(), parse_float() path.moveTo(x, y) elif cmd == moveto_rel: x += parse_float() y += parse_float() path.moveTo(x, y) elif cmd == closepath1 or cmd == closepath2: path.closeSubpath() elif cmd == lineto_abs: x, y = parse_floats(2) path.lineTo(x, y) elif cmd == lineto_rel: x += parse_float() y += parse_float() path.lineTo(x, y) elif cmd == hline_abs: x = parse_float() path.lineTo(x, y) elif cmd == hline_rel: x += parse_float() path.lineTo(x, y) elif cmd == vline_abs: y = parse_float() path.lineTo(x, y) elif cmd == vline_rel: y += parse_float() path.lineTo(x, y) elif cmd == curveto_abs: x1, y1, x2, y2, x, y = parse_floats(6) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == curveto_rel: x1, y1, x2, y2, x, y = parse_floats(6, x, y) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == smoothcurveto_abs: if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel: x1 = 2 * x - x2 y1 = 2 * y - y2 else: x1, y1 = x, y x2, y2, x, y = parse_floats(4) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == smoothcurveto_rel: if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel: x1 = 2 * x - x2 y1 = 2 * y - y2 else: x1, y1 = x, y x2, y2, x, y = parse_floats(4, x, y) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == quadcurveto_abs: x1, y1, x, y = parse_floats(4) path.quadTo(x1, y1, x, y) elif cmd == quadcurveto_rel: x1, y1, x, y = parse_floats(4, x, y) path.quadTo(x1, y1, x, y) elif cmd == smoothquadcurveto_abs: if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): x1 = 2 * x - x1 y1 = 2 * y - y1 else: x1, y1 = x, y x, y = parse_floats(2) path.quadTo(x1, y1, x, y) elif cmd == smoothquadcurveto_rel: if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): x1 = 2 * x - x1 y1 = 2 * y - y1 else: x1, y1 = x, y x, y = parse_floats(2, x, y) path.quadTo(x1, y1, x, y) elif cmd in b'-.0123456789': # A new number begins # In this case, multiple parameters tuples are specified for the last command # We rewind to reparse data correctly pos[0] -= 1 # Handle extra parameters if last_cmd == moveto_abs: repeated_command = cmd = lineto_abs elif last_cmd == moveto_rel: repeated_command = cmd = lineto_rel elif last_cmd in (closepath1, closepath2): raise ValueError('Extra parameters after close path command') elif last_cmd in (lineto_abs, lineto_rel, hline_abs, hline_rel, vline_abs, vline_rel, curveto_abs, curveto_rel, smoothcurveto_abs, smoothcurveto_rel, quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): repeated_command = cmd = last_cmd else: raise ValueError('Unknown path command: %s' % cmd) return path
def svg_path_to_painter_path(d): ''' Convert a tiny SVG 1.2 path into a QPainterPath. :param d: The value of the d attribute of an SVG <path> tag ''' from PyQt5.Qt import QPainterPath cmd = last_cmd = b'' path = QPainterPath() moveto_abs, moveto_rel = b'Mm' closepath1, closepath2 = b'Zz' lineto_abs, lineto_rel = b'Ll' hline_abs, hline_rel = b'Hh' vline_abs, vline_rel = b'Vv' curveto_abs, curveto_rel = b'Cc' smoothcurveto_abs, smoothcurveto_rel = b'Ss' quadcurveto_abs, quadcurveto_rel = b'Qq' smoothquadcurveto_abs, smoothquadcurveto_rel = b'Tt' # Store the last parsed values # x/y = end position # x1/y1 and x2/y2 = bezier control points x = y = x1 = y1 = x2 = y2 = 0 data = d.replace(b',', b' ').replace(b'\n', b' ') if isinstance(data, type('')): data = data.encode('ascii') end = len(data) data = ReadOnlyFileBuffer(data) def parse_float(): chars = [] while data.tell() < end: c = data.read(1) if c == b' ' and not chars: continue if c == b'-' or b'0' <= c[0] <= b'9' or c == b'.': chars.append(c[0]) else: break if not chars: raise ValueError('Premature end of input while expecting a number') return float(b''.join(chars)) def parse_floats(num, x_offset=0, y_offset=0): for i in xrange(num): val = parse_float() yield val + (x_offset if i % 2 == 0 else y_offset) repeated_command = None while data.tell() < end: last_cmd = cmd cmd = data.read(1) if repeated_command is None else repeated_command repeated_command = None if cmd == b' ': continue elif cmd == moveto_abs: x, y = parse_float(), parse_float() path.moveTo(x, y) elif cmd == moveto_rel: x += parse_float() y += parse_float() path.moveTo(x, y) elif cmd == closepath1 or cmd == closepath2: path.closeSubpath() elif cmd == lineto_abs: x, y = parse_floats(2) path.lineTo(x, y) elif cmd == lineto_rel: x += parse_float() y += parse_float() path.lineTo(x, y) elif cmd == hline_abs: x = parse_float() path.lineTo(x, y) elif cmd == hline_rel: x += parse_float() path.lineTo(x, y) elif cmd == vline_abs: y = parse_float() path.lineTo(x, y) elif cmd == vline_rel: y += parse_float() path.lineTo(x, y) elif cmd == curveto_abs: x1, y1, x2, y2, x, y = parse_floats(6) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == curveto_rel: x1, y1, x2, y2, x, y = parse_floats(6, x, y) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == smoothcurveto_abs: if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel: x1 = 2 * x - x2 y1 = 2 * y - y2 else: x1, y1 = x, y x2, y2, x, y = parse_floats(4) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == smoothcurveto_rel: if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel: x1 = 2 * x - x2 y1 = 2 * y - y2 else: x1, y1 = x, y x2, y2, x, y = parse_floats(4, x, y) path.cubicTo(x1, y1, x2, y2, x, y) elif cmd == quadcurveto_abs: x1, y1, x, y = parse_floats(4) path.quadTo(x1, y1, x, y) elif cmd == quadcurveto_rel: x1, y1, x, y = parse_floats(4, x, y) path.quadTo(x1, y1, x, y) elif cmd == smoothquadcurveto_abs: if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): x1 = 2 * x - x1 y1 = 2 * y - y1 else: x1, y1 = x, y x, y = parse_floats(2) path.quadTo(x1, y1, x, y) elif cmd == smoothquadcurveto_rel: if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel): x1 = 2 * x - x1 y1 = 2 * y - y1 else: x1, y1 = x, y x, y = parse_floats(2, x, y) path.quadTo(x1, y1, x, y) elif cmd[0] in b'-.' or b'0' <= cmd[0] <= b'9': # A new number begins # In this case, multiple parameters tuples are specified for the last command # We rewind to reparse data correctly data.seek(-1, os.SEEK_CUR) # Handle extra parameters if last_cmd == moveto_abs: repeated_command = cmd = lineto_abs elif last_cmd == moveto_rel: repeated_command = cmd = lineto_rel elif last_cmd in (closepath1, closepath2): raise ValueError('Extra parameters after close path command') elif last_cmd in ( lineto_abs, lineto_rel, hline_abs, hline_rel, vline_abs, vline_rel, curveto_abs, curveto_rel,smoothcurveto_abs, smoothcurveto_rel, quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel ): repeated_command = cmd = last_cmd else: raise ValueError('Unknown path command: %s' % cmd) return path