Exemple #1
0
def parse_root(root: ElementTree.Element,
               transform_origin=True,
               canvas_height=None,
               draw_hidden=False,
               visible_root=True,
               root_transformation=None) -> List[Curve]:
    """
    Recursively parse an etree root's children into geometric curves.

    :param root: The etree element who's children should be recursively parsed. The root will not be drawn.
    :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root
    does not contain the height attribute, it must be either manually specified or transform must be False.
    :param transform_origin: Whether or not to transform input coordinates from the svg coordinate system to standard
    cartesian system. Depends on canvas_height for calculations.
    :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes.
    :param visible_root: Specifies whether or the root is visible. (Inheritance can be overridden)
    :param root_transformation: Specifies whether the root's transformation. (Transformations are inheritable)
    :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode.
    """

    if canvas_height is None:
        height_str = root.get("height")
        canvas_height = float(height_str) if height_str.isnumeric() else float(
            height_str[:-2])

    curves = []

    # Draw visible elements (Depth-first search)
    for element in list(root):

        # display cannot be overridden by inheritance. Just skip the element
        display = _has_style(element, "display", "none")

        if display or element.tag == "{%s}defs" % NAMESPACES["svg"]:
            continue

        transformation = deepcopy(
            root_transformation) if root_transformation else None

        transform = element.get('transform')
        if transform:
            transformation = Transformation(
            ) if transformation is None else transformation
            transformation.add_transform(transform)

        # Is the element and it's root not hidden?
        visible = visible_root and not (
            _has_style(element, "visibility", "hidden")
            or _has_style(element, "visibility", "collapse"))
        # Override inherited visibility
        visible = visible or (_has_style(element, "visibility", "visible"))

        transparent = _has_style(element, "opacity", "0")

        # If the current element is opaque and visible, draw it
        if draw_hidden or (visible and not transparent):
            if element.tag == "{%s}path" % NAMESPACES["svg"]:
                path = Path(element.attrib['d'], canvas_height,
                            transform_origin, transformation)
                curves.extend(path.curves)

        # Continue the recursion
        curves.extend(
            parse_root(element, transform_origin, canvas_height, draw_hidden,
                       visible, transformation))

    # ToDo implement shapes class
    return curves
Exemple #2
0
def parse_root(root: ElementTree.Element, transform_origin=True, canvas_height=None, draw_hidden=False,
               visible_root=True, root_transformation=None) -> List[Curve]:

    """
    Recursively parse an etree root's children into geometric curves.

    :param root: The etree element who's children should be recursively parsed. The root will not be drawn.
    :param canvas_height: The height of the canvas. By default the height attribute of the root is used. If the root
    does not contain the height attribute, it must be either manually specified or transform must be False.
    :param transform_origin: Whether or not to transform input coordinates from the svg coordinate system to standard
    cartesian system. Depends on canvas_height for calculations.
    :param draw_hidden: Whether or not to draw hidden elements based on their display, visibility and opacity attributes.
    :param visible_root: Specifies whether or the root is visible. (Inheritance can be overridden)
    :param root_transformation: Specifies whether the root's transformation. (Transformations are inheritable)
    :return: A list of geometric curves describing the svg. Use the Compiler sub-module to compile them to gcode.
    """

    if canvas_height is None:
        height_str = root.get("height")
        viewBox_str = root.get("viewBox")
        if height_str is None and viewBox_str:
            # "viewBox" attribute: <min-x, min-y, width, height>
            height_str = viewBox_str.split()[3]
        (number, scale_factor) = _parse_length(height_str)
        canvas_height = number * scale_factor

    if root.get("viewBox") is None:
        height_str = root.get("height")
        if height_str is not None:
            scale = 25.4/96.0
            root_transformation = root_transformation if root_transformation else Transformation()
            root_transformation.add_scale(scale, scale)
    else:
        #print("We have a viewBox of >>%s<<" % (root.get("viewBox")))
        # Calulate the transform, as described in https://www.w3.org/TR/SVG/coords.html#ComputingAViewportsTransform
        p = re.compile(r'([\d\.\-e]+)[,\s]+([\d\.\-e]+)[,\s]+([\d\.\-e]+)[,\s]+([\d\.\-e]+)')
        if p.search(root.get("viewBox")):
            parts = p.search(root.get("viewBox"))
            # TODO Can these values be anything other than numbers?  "123mm" maybe?
            #      The spec says they're "number"s, so no units, but possibly + or - or e-notation
            vb_x = float(parts[1])
            vb_y = float(parts[2])
            vb_width = float(parts[3])
            vb_height = float(parts[4])
            # TODO handle the preserveAspectRatio attribute
            # Defaults if not otherwise specified
            align = "xMidYMid"
            meet_or_slice = "meet"

            e_x = 0.0
            e_y = 0.0
            width_str = root.get("width")
            (e_number, e_multiply) = _parse_length(width_str)
            e_width = e_number * e_multiply
            e_height = canvas_height
            scale_x = e_width/vb_width
            scale_y = e_height/vb_height
            #print("vb_x: %f, vb_y: %f, vb_width: %f, vb_height: %f" % (vb_x, vb_y, vb_width, vb_height))
            #print("e_x: %f, e_y: %f, e_width: %f, e_height: %f, scale_x: %f, scale_y: %f" % (e_x, e_y, e_width, e_height, scale_x, scale_y))
            if align != "none" and meet_or_slice == "meet":
                if scale_x > scale_y:
                    scale_x = scale_y
                else:
                    scale_y = scale_x
            if align != "none" and meet_or_slice == "slice":
                if scale_x < scale_y:
                    scale_x = scale_y
                else:
                    scale_y = scale_x
            translate_x = e_x - (vb_x * scale_x)
            translate_y = e_y - (vb_y * scale_y)
            # Now apply the viewBox transformations
            root_transformation = root_transformation if root_transformation else Transformation()
            if translate_x != 0 or translate_y != 0:
                root_transformation.add_translation(translate_x, translate_y)
            if scale_x != 1.0 or scale_y != 1.0:
                root_transformation.add_scale(scale_x, scale_y)

    curves = []

    # Draw visible elements (Depth-first search)
    for element in list(root):

        # display cannot be overridden by inheritance. Just skip the element
        display = _has_style(element, "display", "none")

        if display or element.tag == "{%s}defs" % NAMESPACES["svg"]:
            continue

        transformation = deepcopy(root_transformation) if root_transformation else None

        transform = element.get('transform')
        if transform:
            transformation = Transformation() if transformation is None else transformation
            transformation.add_transform(transform)

        # Is the element and it's root not hidden?
        visible = visible_root and not (_has_style(element, "visibility", "hidden")
                                        or _has_style(element, "visibility", "collapse"))
        # Override inherited visibility
        visible = visible or (_has_style(element, "visibility", "visible"))

        # If the current element is opaque and visible, draw it
        if draw_hidden or visible:
            if element.tag == "{%s}path" % NAMESPACES["svg"]:
                path = Path(element.attrib['d'], canvas_height, transform_origin, transformation)
                curves.extend(path.curves)

        # Continue the recursion
        curves.extend(parse_root(element, transform_origin, canvas_height, draw_hidden, visible, transformation))

    # ToDo implement shapes class
    return curves