Esempio n. 1
0
File: io.py Progetto: tatarize/vpype
def _extract_paths(group: svgelements.Group, recursive) -> _PathListType:
    """Extract everything from the provided SVG group."""

    if recursive:
        everything = group.select()
    else:
        everything = group
    paths = []
    for elem in everything:
        if hasattr(elem, "values") and elem.values.get("visibility", "") in (
                "hidden",
                "collapse",
        ):
            continue

        if isinstance(elem, svgelements.Path):
            if len(elem) != 0:
                paths.append(elem)
        elif isinstance(elem, (svgelements.Polyline, svgelements.Polygon)):
            # Here we add a "fake" path containing just the Polyline/Polygon,
            # to be treated specifically by _convert_flattened_paths.
            path = [svgelements.Move(elem.points[0]), elem]
            if isinstance(elem, svgelements.Polygon):
                path.append(svgelements.Close(elem.points[-1], elem.points[0]))
            paths.append(path)
        elif isinstance(elem, svgelements.Shape):
            e = svgelements.Path(elem)
            e.reify(
            )  # In some cases the shape could not have reified, the path must.
            if len(e) != 0:
                paths.append(e)

    return paths
Esempio n. 2
0
File: engine.py Progetto: ertpoi/smt
 def _path(self):
     path = SE.Path(self._glyph)
     path *= f"scale({self.xscale * _scale()}, {self.yscale * _scale()})"
     # First rotate at 00,
     path *= f"rotate({self.rotate}deg)"
     # then move.
     path *= f"translate({self.x}, {self.y})"
     return path
Esempio n. 3
0
def extract_shapely_polygons(svg_document, tolerance):
    height, width = get_sheet_dimensions(svg_document)
    s = io.StringIO(svg_document)
    svg_object = svgelements.SVG.parse(s, width=width, height=height)

    # Gather all paths in the document
    elements = []
    paths = []
    for element in svg_object.elements():
        # Ignore hidden elements
        if 'hidden' in element.values and element.values[
                'visibility'] == 'hidden':
            continue
        # Treat paths as is, but convert other shapes into paths
        if isinstance(element, svgelements.Path):
            if len(element) != 0 and is_closed(element, tolerance):
                elements.append(element)
                paths.append(element)
        elif isinstance(element, svgelements.Shape):
            e = svgelements.Path(element)
            e.reify()
            if len(e) != 0 and is_closed(e, tolerance):
                elements.append(element)
                paths.append(e)

    # Extract outlines and holes from paths
    shapely_polygons = []
    for path in paths:
        # Dilate the boundary, since the discrete path may actually unapproximate
        # and we want to ensure that the polygon always overapproximates the shape
        #
        # Note that the amounts are chosen because of the following reasons:
        # - We discretize at a spacing of (tolerance). This means that the discrete
        #   polygon is wrong (missing points or containing extra points) at a
        #   distance at most (tolerance/2) from the original shape
        # - By dilating the polygon by (1.5*tolerance), it is guaranteed to contain
        #   all of the points in the original shape, plus at least (tolerance)
        #   extra breathing room of buffering
        # - Simplifying by (tolerance) therefore results in a polygon that still
        #   contains the original shape, and overapproximates it by points at a
        #   distance at most 3*tolerance
        boundary = dilate(discretize_path(path.subpath(0), tolerance),
                          1.5 * tolerance).simplify(tolerance)

        # Erode the holes to ensure that the resulting polygon with holes is an
        # overapproximation of the original shape. Note that this might split a
        # hole into a MultiPolygon (handled below), or even make it empty
        hole_paths = list(path.as_subpaths())[1:]
        holes = list(
            erode(discretize_path(p, tolerance), 1.5 *
                  tolerance).simplify(tolerance) for p in hole_paths
            if is_closed(p, tolerance))

        shapely_polygons.append((boundary, holes))

    assert (len(elements) == len(shapely_polygons))
    return elements, shapely_polygons
Esempio n. 4
0
def path_to_polylines(path_or_svgd, start=0j, tolerance=0.1):
    def subdivide_cubicBezier(cubic):
        for x, y in islice(
                aggsubdivision.bezier(
                    (cubic.start.real, cubic.start.imag),
                    (cubic.control1.real, cubic.control1.imag),
                    (cubic.control2.real, cubic.control2.imag),
                    (cubic.end.real, cubic.end.imag),
                    distance_tolerance=tolerance), 1, None):
            yield complex(x, y)

    def subpath_to_polyline(subpath):
        for seg in subpath:
            if isinstance(seg, svgelements.Move):
                yield complex(*seg.end)
            elif isinstance(seg, svgelements.Line):
                yield complex(*seg.end)
            elif isinstance(seg, svgelements.CubicBezier):
                yield from subdivide_cubicBezier(seg)
            elif isinstance(seg, svgelements.QuadraticBezier):
                cubic = svgelements.CubicBezier(
                    seg.start, 1 / 3 * seg.start + 2 / 3 * seg.control,
                    2 / 3 * seg.control + 1 / 3 * seg.end, seg.end)
                yield from subdivide_cubicBezier(cubic)
            elif isinstance(seg, svgelements.Arc):
                for cubic in seg.as_cubic_curves():
                    yield from subdivide_cubicBezier(cubic)
            elif isinstance(seg, svgelements.Close):
                yield complex(*seg.end)
            else:
                logging.warn('unimplemented segement type %s', type(seg))

    if isinstance(path_or_svgd, svgelements.Path):
        path = path_or_svgd
    else:
        path = svgelements.Path()
        path.append(svgelements.Move(start))
        path.parse(path_or_svgd)
        path.pop(0)
    for subpath in path.as_subpaths():
        if subpath:
            yield list(subpath_to_polyline(subpath))
Esempio n. 5
0
def find_paths(svgfn, scale=1):
    svg = ET.parse(svgfn).getroot()

    try:
        x0, y0, w, h = map(float, svg.attrib['viewBox'].split(' '))
        sx = Quantity(svg.attrib['width']).m_as('mm') / w
        sy = Quantity(svg.attrib['height']).m_as('mm') / h
        xform0 = 'scale(%s %s)' % (sx * scale, sy * scale)
    except KeyError:
        logging.warn('could not determine SVG units, assuming mm')
        xform0 = 'scale(%s %s)' % (scale, scale)

    for e, ancestors in walk_svg(svg):
        tag = simple_tag(e)
        if tag == 'path':
            path = svgelements.Path(e.get('d', ''))
            transform_str = ' '.join(
                e2.get('transform', '') for e2 in ancestors)
            transform = svgelements.Matrix(xform0 + transform_str)
            transformed_path = path * transform
            transformed_path.reify()
            yield transformed_path
Esempio n. 6
0
File: engine.py Progetto: ertpoi/smt
def install_font1(path, overwrite=False):
    name, ext = os.path.splitext(os.path.basename(path))
    if os.path.exists(f"./fonts/json/{name}.json") and not overwrite:
        raise FileExistsError(f"{name} is already installed.")
    else:
        D = {}
        D[name] = {}
        if ext == ".svg":
            with open(f"./fonts/json/{name}.json", "w") as file_:
                font = ET.parse(path).getroot().find("ns:defs", _SVGNS).find(
                    "ns:font", _SVGNS)
                for glyph in font.findall("ns:glyph", _SVGNS):
                    try:
                        path = SE.Path(glyph.attrib["d"],
                                       transform="scale(1 -1)")
                        # .scaled(sx=1, sy=-1)

                        # svgpathtools' scaled() method has a bug which deforms shapes. It offers however good bbox support.
                        # svgelements has unreliable bbox functionality, but transformations seem to be more safe than in pathtools.
                        # Bypass: apply transformations in svgelements and pass the d() to pathtools to get bboxes when needed.
                        min_x, min_y, max_x, max_y = path.bbox()
                        D[name][glyph.get("glyph-name")] = {
                            "d": path.d(),
                            "left": min_x,
                            "right": max_x,
                            "top": min_y,
                            "bottom": max_y,
                            "width": max_x - min_x,
                            "height": max_y - min_y
                        }
                        # D[name][glyph.get("glyph-name")] = glyph.attrib["d"]
                    except KeyError:
                        pass
                json.dump(D[name], file_, indent=2)
                del path
                del glyph
        else:
            raise NotImplementedError("Non-svg fonts are not supported!")
Esempio n. 7
0
def get_boundary(path):
    return svgelements.Path(svgelements.Path(path).subpath(0))
Esempio n. 8
0
def discretize_path(path, spacing):
    path = svgelements.Path(path)  # Convert Subpath to Path
    length = path.length()
    n_points = max(3, math.ceil(length / spacing))
    points = [path.point(i * (1 / n_points)) for i in range(n_points)]
    return to_shapely_polygon(points)