示例#1
0
    def __init__(self, shape, bbox=None):
        xfrm = shape.element.xfrm

        # From Section L.4.7.6 of ECMA-376 Part 1
        (Bx, By) = ((xfrm.chOff.x, xfrm.chOff.y) if xfrm.chOff is not None else
                    (0, 0))
        (Dx, Dy) = ((xfrm.chExt.cx,
                     xfrm.chExt.cy) if xfrm.chExt is not None else bbox)
        (Bx_, By_) = (xfrm.off.x, xfrm.off.y)
        (Dx_, Dy_) = (xfrm.ext.cx, xfrm.ext.cy)
        theta = radians(xfrm.rot)
        Fx = -1 if xfrm.flipH else 1
        Fy = -1 if xfrm.flipV else 1
        T_st = np.array(
            [[Dx_ / Dx, 0, Bx_ - (Dx_ / Dx) * Bx] if Dx != 0 else [1, 0, Bx_],
             [0, Dy_ / Dy, By_ - (Dy_ / Dy) * By] if Dy != 0 else [0, 1, By_],
             [0, 0, 1]])
        U = np.array([[1, 0, -(Bx_ + Dx_ / 2.0)], [0, 1, -(By_ + Dy_ / 2.0)],
                      [0, 0, 1]])
        R = np.array([[cos(theta), -sin(theta), 0],
                      [sin(theta), cos(theta), 0], [0, 0, 1]])
        Flip = np.array([[Fx, 0, 0], [0, Fy, 0], [0, 0, 1]])
        T_rf = np.linalg.inv(U) @ R @ Flip @ U
        super().__init__(T_rf @ T_st)
示例#2
0
 def __init__(self, transform):
     T = np.identity(3)
     if transform is not None:
         # A simple parser, assuming well-formed SVG
         tokens = transform.replace('(',
                                    ' ').replace(')',
                                                 ' ').replace(',',
                                                              ' ').split()
         pos = 0
         while pos < len(tokens):
             xfm = tokens[pos]
             pos += 1
             if xfm == 'matrix':
                 params = tuple(float(x) for x in tokens[pos:pos + 6])
                 pos += 6
                 T = T @ np.array([[params[0], params[2], params[4]],
                                   [params[1], params[3], params[5]],
                                   [0, 0, 1]])
             elif xfm == 'translate':
                 x = float(tokens[pos])
                 pos += 1
                 if pos >= len(tokens) or tokens[pos].isalpha():
                     y = 0
                 else:
                     y = float(tokens[pos])
                     pos += 1
                 T = T @ np.array([[1, 0, x], [0, 1, y], [0, 0, 1]])
             elif xfm == 'scale':
                 sx = float(tokens[pos])
                 pos += 1
                 if pos >= len(tokens) or tokens[pos].isalpha():
                     sy = sx
                 else:
                     sy = float(tokens[pos])
                     pos += 1
                 T = T @ np.array([[sx, 0, 0], [0, sy, 0], [0, 0, 1]])
             elif xfm == 'rotate':
                 a = radians(float(tokens[pos]))
                 pos += 1
                 if pos >= len(tokens) or tokens[pos].isalpha():
                     T = T @ np.array([[cos(a), -sin(a), 0],
                                       [sin(a), cos(a), 0], [0, 0, 1]])
                 else:
                     (cx, cy) = tuple(float(x) for x in tokens[pos:pos + 2])
                     pos += 2
                     T = T @ np.array([[
                         cos(a), -sin(a), -cx * cos(a) + cy * sin(a) + cx
                     ], [sin(a),
                         cos(a), -cx * sin(a) - cy * cos(a) + cy],
                                       [0, 0, 1]])
             elif xfm == 'skewX':
                 a = float(tokens[pos])
                 pos += 1
                 T = T @ np.array([[1, tan(a), 0], [0, 1, 0], [0, 0, 1]])
             elif xfm == 'skewY':
                 a = float(tokens[pos])
                 pos += 1
                 T = T @ np.array([[1, 0, 0], [tan(a), 1, 0], [0, 0, 1]])
             else:
                 raise ValueError(
                     'Invalid SVG transform: {}'.format(transform))
     super().__init__(T)
示例#3
0
    def __path_from_tokens(self, tokens, transform):
        #===============================================
        moved = False
        first_point = None
        current_point = None
        closed = False
        path = skia.Path()
        pos = 0
        while pos < len(tokens):
            if isinstance(tokens[pos], str) and tokens[pos].isalpha():
                cmd = tokens[pos]
                pos += 1
            # Else repeat previous command with new coordinates
            # with `moveTo` becoming `lineTo`
            elif cmd == 'M':
                cmd = 'L'
            elif cmd == 'm':
                cmd = 'l'

            if cmd not in ['s', 'S']:
                second_cubic_control = None
            if cmd not in ['t', 'T']:
                second_quad_control = None

            if cmd in ['a', 'A']:
                params = [float(x) for x in tokens[pos:pos + 7]]
                pos += 7
                pt = params[5:7]
                if cmd == 'a':
                    pt[0] += current_point[0]
                    pt[1] += current_point[1]
                phi = radians(params[2])
                if moved:
                    path.moveTo(*transform.transform_point(current_point))
                    moved = False
                (rx, ry) = transform.scale_length(params[0:2])
                path.arcTo(
                    rx, ry, degrees(transform.rotate_angle(phi)),
                    skia.Path.ArcSize.kSmall_ArcSize if params[3] == 0 else
                    skia.Path.ArcSize.kLarge_ArcSize, skia.PathDirection.kCCW
                    if params[4] == 0 else skia.PathDirection.kCW,
                    *transform.transform_point(pt))
                current_point = pt

            elif cmd in ['c', 'C', 's', 'S']:
                if moved:
                    path.moveTo(*transform.transform_point(current_point))
                    moved = False
                if cmd in ['c', 'C']:
                    n_params = 6
                    coords = []
                else:
                    n_params = 4
                    if second_cubic_control is None:
                        coords = list(transform.transform_point(current_point))
                    else:
                        coords = list(
                            transform.transform_point(
                                reflect_point(second_cubic_control,
                                              current_point)))
                params = [float(x) for x in tokens[pos:pos + n_params]]
                pos += n_params
                for n in range(0, n_params, 2):
                    pt = params[n:n + 2]
                    if cmd.islower():
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                    if n == (n_params - 4):
                        second_cubic_control = pt
                    coords.extend(transform.transform_point(pt))
                path.cubicTo(*coords)
                current_point = pt

            elif cmd in ['l', 'L', 'h', 'H', 'v', 'V']:
                if cmd in ['l', 'L']:
                    params = [float(x) for x in tokens[pos:pos + 2]]
                    pos += 2
                    pt = params[0:2]
                    if cmd == 'l':
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                else:
                    param = float(tokens[pos])
                    pos += 1
                    if cmd == 'h':
                        param += current_point[0]
                    elif cmd == 'v':
                        param += current_point[1]
                    if cmd in ['h', 'H']:
                        pt = [param, current_point[1]]
                    else:
                        pt = [current_point[0], param]
                if moved:
                    path.moveTo(*transform.transform_point(current_point))
                    moved = False
                path.lineTo(*transform.transform_point(pt))
                current_point = pt

            elif cmd in ['m', 'M']:
                params = [float(x) for x in tokens[pos:pos + 2]]
                pos += 2
                pt = params[0:2]
                if first_point is None:
                    # First `m` in a path is treated as `M`
                    first_point = pt
                else:
                    if cmd == 'm':
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                current_point = pt
                moved = True

            elif cmd in ['q', 'Q', 't', 'T']:
                if moved:
                    path.moveTo(*transform.transform_point(current_point))
                    moved = False
                if cmd in ['t', 'T']:
                    n_params = 4
                    coords = []
                else:
                    n_params = 2
                    if second_quad_control is None:
                        coords = list(transform.transform_point(current_point))
                    else:
                        coords = list(
                            transform.transform_point(
                                reflect_point(second_quad_control,
                                              current_point)))
                params = [float(x) for x in tokens[pos:pos + n_params]]
                pos += n_params
                for n in range(0, n_params, 2):
                    pt = params[n:n + 2]
                    if cmd.islower():
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                    if n == (n_params - 4):
                        second_quad_control = pt
                    coords.extend(transform.transform_point(pt))
                path.quadTo(*coords)
                current_point = pt

            elif cmd in ['z', 'Z']:
                if first_point is not None and current_point != first_point:
                    pass
                    #path.close()
                closed = True
                first_point = None

            else:
                print('Unknown path command: {}'.format(cmd))
        return path
示例#4
0
    def __get_geometry(self, element, properties, transform):
    #=======================================================
    ##
    ## Returns path element as a `shapely` object.
    ##
        coordinates = []
        bezier_segments = []
        moved = False
        first_point = None
        current_point = None
        closed = False
        path_tokens = []

        T = transform@SVGTransform(element.attrib.get('transform'))
        if element.tag == SVG_NS('path'):
            path_tokens = list(parse_svg_path(element.attrib.get('d', '')))

        elif element.tag == SVG_NS('rect'):
            x = length_as_pixels(element.attrib.get('x', 0))
            y = length_as_pixels(element.attrib.get('y', 0))
            width = length_as_pixels(element.attrib.get('width', 0))
            height = length_as_pixels(element.attrib.get('height', 0))
            rx = length_as_pixels(element.attrib.get('rx'))
            ry = length_as_pixels(element.attrib.get('ry'))
            if width == 0 or height == 0: return None

            if rx is None and ry is None:
                rx = ry = 0
            elif ry is None:
                ry = rx
            elif rx is None:
                rx = ry
            rx = min(rx, width/2)
            ry = min(ry, height/2)
            if rx == 0 and ry == 0:
                path_tokens = ['M', x, y,
                               'H', x+width,
                               'V', y+height,
                               'H', x,
                               'V', y,
                               'Z']
            else:
                path_tokens = ['M', x+rx, y,
                               'H', x+width-rx,
                               'A', rx, ry, 0, 0, 1, x+width, y+ry,
                               'V', y+height-ry,
                               'A', rx, ry, 0, 0, 1, x+width-rx, y+height,
                               'H', x+rx,
                               'A', rx, ry, 0, 0, 1, x, y+height-ry,
                               'V', y+ry,
                               'A', rx, ry, 0, 0, 1, x+rx, y,
                               'Z']

        elif element.tag == SVG_NS('line'):
            x1 = length_as_pixels(element.attrib.get('x1', 0))
            y1 = length_as_pixels(element.attrib.get('y1', 0))
            x2 = length_as_pixels(element.attrib.get('x2', 0))
            y2 = length_as_pixels(element.attrib.get('y2', 0))
            path_tokens = ['M', x1, y1, x2, y2]

        elif element.tag == SVG_NS('polyline'):
            points = element.attrib.get('points', '').replace(',', ' ').split()
            path_tokens = ['M'] + points

        elif element.tag == SVG_NS('polygon'):
            points = element.attrib.get('points', '').replace(',', ' ').split()
            path_tokens = ['M'] + points + ['Z']

        elif element.tag == SVG_NS('circle'):
            cx = length_as_pixels(element.attrib.get('cx', 0))
            cy = length_as_pixels(element.attrib.get('cy', 0))
            r = length_as_pixels(element.attrib.get('r', 0))
            if r == 0: return None
            path_tokens = ['M', cx+r, cy,
                           'A', r, r, 0, 0, 0, cx, cy-r,
                           'A', r, r, 0, 0, 0, cx-r, cy,
                           'A', r, r, 0, 0, 0, cx, cy+r,
                           'A', r, r, 0, 0, 0, cx+r, cy,
                           'Z']

        elif element.tag == SVG_NS('ellipse'):
            cx = length_as_pixels(element.attrib.get('cx', 0))
            cy = length_as_pixels(element.attrib.get('cy', 0))
            rx = length_as_pixels(element.attrib.get('rx', 0))
            ry = length_as_pixels(element.attrib.get('ry', 0))
            if rx == 0 or ry == 0: return None
            path_tokens = ['M', cx+rx, cy,
                           'A', rx, ry, 0, 0, 0, cx, cy-ry,
                           'A', rx, ry, 0, 0, 0, cx-rx, cy,
                           'A', rx, ry, 0, 0, 0, cx, cy+ry,
                           'A', rx, ry, 0, 0, 0, cx+rx, cy,
                           'Z']

        elif element.tag == SVG_NS('image'):
            if 'id' in properties or 'class' in properties:
                width = length_as_pixels(element.attrib.get('width', 0))
                height = length_as_pixels(element.attrib.get('height', 0))
                path_tokens = ['M', 0, 0,
                               'H', width,
                               'V', height,
                               'H', 0,
                               'V', 0,
                               'Z']

        pos = 0
        while pos < len(path_tokens):
            if isinstance(path_tokens[pos], str) and path_tokens[pos].isalpha():
                cmd = path_tokens[pos]
                pos += 1
            # Else repeat previous command with new coordinates
            # with `moveTo` becoming `lineTo`
            elif cmd == 'M':
                cmd = 'L'
            elif cmd == 'm':
                cmd = 'l'

            if cmd not in ['s', 'S']:
                second_cubic_control = None
            if cmd not in ['t', 'T']:
                second_quad_control = None

            if cmd in ['a', 'A']:
                params = [float(x) for x in path_tokens[pos:pos+7]]
                pos += 7
                pt = params[5:7]
                if cmd == 'a':
                    pt[0] += current_point[0]
                    pt[1] += current_point[1]
                phi = radians(params[2])
                path = bezier_path_from_arc_endpoints(tuple2(*params[0:2]), phi, *params[3:5],
                                                        tuple2(*current_point), tuple2(*pt), T)
                bezier_segments.extend(path.asSegments())
                coordinates.extend(bezier_sample(path))
                current_point = pt

            elif cmd in ['c', 'C', 's', 'S']:
                coords = [BezierPoint(*T.transform_point(current_point))]
                if cmd in ['c', 'C']:
                    n_params = 6
                else:
                    n_params = 4
                    if second_cubic_control is None:
                        coords.append(BezierPoint(*T.transform_point(current_point)))
                    else:
                        coords.append(BezierPoint(*T.transform_point(
                            reflect_point(second_cubic_control, current_point))))
                params = [float(x) for x in path_tokens[pos:pos+n_params]]
                pos += n_params
                for n in range(0, n_params, 2):
                    pt = params[n:n+2]
                    if cmd.islower():
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                    if n == (n_params - 4):
                        second_cubic_control = pt
                    coords.append(BezierPoint(*T.transform_point(pt)))
                bz = CubicBezier(*coords)
                bezier_segments.append(bz)
                coordinates.extend(bezier_sample(bz))
                current_point = pt

            elif cmd in ['l', 'L', 'h', 'H', 'v', 'V']:
                if cmd in ['l', 'L']:
                    params = [float(x) for x in path_tokens[pos:pos+2]]
                    pos += 2
                    pt = params[0:2]
                    if cmd == 'l':
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                else:
                    param = float(path_tokens[pos])
                    pos += 1
                    if cmd == 'h':
                        param += current_point[0]
                    elif cmd == 'v':
                        param += current_point[1]
                    if cmd in ['h', 'H']:
                        pt = [param, current_point[1]]
                    else:
                        pt = [current_point[0], param]
                if moved:
                    coordinates.append(T.transform_point(current_point))
                    moved = False
                coordinates.append(T.transform_point(pt))
                current_point = pt

            elif cmd in ['m', 'M']:
                params = [float(x) for x in path_tokens[pos:pos+2]]
                pos += 2
                pt = params[0:2]
                if first_point is None:
                    # First `m` in a path is treated as `M`
                    first_point = pt
                else:
                    if cmd == 'm':
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                current_point = pt
                moved = True

            elif cmd in ['q', 'Q', 't', 'T']:
                coords = [BezierPoint(*T.transform_point(current_point))]
                if cmd in ['q', 'Q']:
                    n_params = 4
                else:
                    n_params = 2
                    if second_quad_control is None:
                        coords.append(BezierPoint(*T.transform_point(current_point)))
                    else:
                        coords.append(BezierPoint(*T.transform_point(
                            reflect_point(second_quad_control, current_point))))
                params = [float(x) for x in path_tokens[pos:pos+n_params]]
                pos += n_params
                for n in range(0, n_params, 2):
                    pt = params[n:n+2]
                    if cmd.islower():
                        pt[0] += current_point[0]
                        pt[1] += current_point[1]
                    if n == (n_params - 4):
                        second_quad_control = pt
                    coords.append(BezierPoint(*T.transform_point(pt)))
                bz = QuadraticBezier(*coords)
                bezier_segments.append(bz)
                coordinates.extend(bezier_sample(bz))
                current_point = pt

            elif cmd in ['z', 'Z']:
                if first_point is not None and current_point != first_point:
                    coordinates.append(T.transform_point(first_point))
                closed = True
                first_point = None

            else:
                log.warn('Unknown path command: {}'.format(cmd))

        if len(bezier_segments) > 0:
            properties['bezier-path'] = BezierPath.fromSegments(bezier_segments)

        if closed and len(coordinates) >= 3:
            geometry = shapely.geometry.Polygon(coordinates)
        elif properties.get('closed', False) and len(coordinates) >= 3:
            # Return a polygon if flagged as `closed`
            coordinates.append(coordinates[0])
            geometry = shapely.geometry.Polygon(coordinates)
        elif len(coordinates) >= 2:
            geometry = shapely.geometry.LineString(coordinates)
        else:
            geometry = None
        return geometry