SVG

SVG represents the root <svg> element. It manages the viewBox, default output dimensions, child elements, file loading, and file output.

SVG inherits the general element API from SvgElement, so it also supports attribute access, tree operations, searching, copying, and serialization.

Import

import pydreamplet as dp

Signatures

dp.SVG(width: Real, height: Real, **kwargs)
dp.SVG(x: Real, y: Real, width: Real, height: Real, **kwargs)
dp.SVG(viewbox: tuple[Real, ...] | list[Real], **kwargs)

viewbox can be a tuple or list with either two or four numeric values.

In the public type aliases, Real means int | float and AttributeValue means str | int | float | None.

dp.SVG((540, 360))
dp.SVG([0, 0, 540, 360])

Constructor Parameters

ParameterTypeRequiredDescription
widthRealyes, in the 2-value formThe viewBox width. Also used as the default SVG width attribute with a px suffix.
heightRealyes, in the 2-value formThe viewBox height. Also used as the default SVG height attribute with a px suffix.
xRealyes, in the 4-value formThe minimum x coordinate of the viewBox.
yRealyes, in the 4-value formThe minimum y coordinate of the viewBox.
viewboxtuple/list of Realyes, in the sequence formA 2-item or 4-item sequence.
**kwargsAnynoAdditional SVG attributes. Underscores are converted to hyphens, except known namespace prefixes. Values are serialized to SVG attributes.

The constructor accepts only two or four viewBox values. Passing any other count raises ValueError.

dp.SVG(540)              # ValueError
dp.SVG(0, 0, 540)        # ValueError
dp.SVG("540", "360")     # ValueError

Created Attributes

Every constructed SVG receives these attributes:

AttributeSourceExample
viewBoxThe provided viewBox values."0 0 540 360"
widthwidth kwarg or default from viewBox width."540px"
heightheight kwarg or default from viewBox height."360px"
svg = dp.SVG(540, 360)

print(svg.viewBox)  # 0 0 540 360
print(svg.width)    # 540px
print(svg.height)   # 360px

The root namespace is emitted by ElementTree when the SVG is serialized.

print(str(dp.SVG(120, 80)))
# <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 80" width="120px" height="80px" />

ViewBox Forms

Use two values for the common 0 0 width height coordinate system.

svg = dp.SVG(540, 360)

print(svg.viewBox)  # 0 0 540 360

Use four values when the coordinate system has an explicit origin.

svg = dp.SVG(-270, -180, 540, 360)

print(svg.viewBox)  # -270 -180 540 360

The four-value form is useful for centered coordinate systems.

svg = dp.SVG(-100, -100, 200, 200)
svg.append(dp.Circle(cx=0, cy=0, r=40, fill="#14b8a6"))

Width And Height

The width and height attributes control the rendered output size. They do not change drawing coordinates.

svg = dp.SVG(540, 360, width=1080, height=720)

print(svg.viewBox)  # 0 0 540 360
print(svg.width)    # 1080
print(svg.height)   # 720

You can also update them later because SVG attributes are exposed as Python attributes.

svg.width = 720
svg.height = 480

Properties

w

svg.w -> float

Returns the viewBox width.

svg = dp.SVG(300, 200, width=900, height=600)

print(svg.w)      # 300.0
print(svg.width)  # 900

If the SVG has no viewBox, w falls back to the numeric part of the width attribute. If that cannot be parsed, it returns 0.0. A malformed viewBox raises ValueError.

h

svg.h -> float

Returns the viewBox height.

svg = dp.SVG(300, 200, width=900, height=600)

print(svg.h)       # 200.0
print(svg.height)  # 600

If the SVG has no viewBox, h falls back to the numeric part of the height attribute. If that cannot be parsed, it returns 0.0. A malformed viewBox raises ValueError.

Attribute Handling

SVG inherits dynamic attribute handling from SvgElement.

You can pass SVG attributes as keyword arguments.

svg = dp.SVG(
    540,
    360,
    id="hero",
    class_name="chart",
    preserve_aspect_ratio="xMidYMid meet",
)

print(svg.id)                    # hero
print(svg.class_name)            # chart
print(svg.preserve_aspect_ratio) # xMidYMid meet

Attribute names are normalized:

Python nameSVG attribute
class_nameclass
stroke_widthstroke-width
preserve_aspect_ratiopreserve-aspect-ratio
xml_spacenamespaced XML attribute
xlink_hrefnamespaced XLink attribute

Assigning None removes an attribute.

svg.id = "draft"
svg.id = None

print(svg.has_attr("id"))  # False

String values that look like numbers are converted when read through dynamic attributes. The id attribute is always kept as a string.

svg.opacity = 0.5
svg.tabindex = 1
svg.id = "001"

print(svg.opacity)   # 0.5
print(svg.tabindex)  # 1
print(svg.id)        # 001

Core Methods

from_element

dp.SVG.from_element(element: xml.etree.ElementTree.Element) -> dp.SVG

Creates an SVG wrapper around an existing xml.etree.ElementTree.Element.

import xml.etree.ElementTree as ET
import pydreamplet as dp

element = ET.Element("{http://www.w3.org/2000/svg}svg")
element.set("viewBox", "0 0 120 80")
element.set("width", "120px")
element.set("height", "80px")

svg = dp.SVG.from_element(element)

This method does not rebuild the element or apply constructor defaults. It wraps the element as it is.

from_file

dp.SVG.from_file(filename: str) -> dp.SVG

Parses an existing SVG file and returns an SVG instance.

from importlib.resources import files
from pydreamplet import SVG, resources

svg = SVG.from_file(files(resources) / "hummingbird.svg").attrs(
    {"width": 96, "height": 84}
)
svg.find("path").fill = "darkgreen"

Namespaces declared in the source file are registered before serialization. This helps prefixes such as xlink or custom source prefixes round-trip without being rewritten to generated prefixes.

If the source SVG has numeric width and height but no viewBox, from_file() adds a matching viewBox.

<svg width="120px" height="80px"></svg>

After loading:

svg = dp.SVG.from_file("icon.svg")

print(svg.viewBox)  # 0 0 120 80
print(svg.w)        # 120.0
print(svg.h)        # 80.0

Only simple numeric lengths are used for the fallback. Values such as "120px", "120", and "100%" can be parsed for their leading numeric part. Non-numeric values cannot produce a fallback viewBox.

ensure_defs

svg.ensure_defs() -> dp.Defs

Returns the first existing <defs> child. If none exists, creates one as the first child and returns it.

svg = dp.SVG(300, 200)
defs = svg.ensure_defs()

gradient = dp.LinearGradient(id="accent", x1="0%", y1="0%", x2="100%", y2="0%")
gradient.add_stop("0%", "#14b8a6")
gradient.add_stop("100%", "#38bdf8")

defs.append(gradient)

The returned Defs object is a live wrapper around the SVG tree. Mutating it changes the SVG.

style

svg.style(file_path: str, overwrite: bool = True, minify: bool = True) -> None

Loads CSS from file_path and inserts it as a <style> element.

ParameterTypeDefaultDescription
file_pathstrrequiredPath to a CSS file.
overwriteboolTrueRemoves existing direct <style> children and inserts the new style as the first child.
minifyboolTrueRemoves comments, collapses whitespace, and trims spacing around common CSS punctuation before insertion.
svg = dp.SVG(200, 200)
svg.append(dp.Circle(cx=100, cy=100, r=50, class_name="mark"))

svg.style("drawing.css")

Example drawing.css:

.mark {
  fill: #14b8a6;
  stroke: currentColor;
  stroke-width: 8;
}

When overwrite=False, the new style is appended after existing children.

svg.style("base.css")
svg.style("theme.css", overwrite=False)

display

svg.display() -> None

Displays the SVG in an IPython environment.

svg.display()

This method requires the optional notebook dependencies. If they are not installed, it raises RuntimeError with installation instructions.

pip install "pydreamplet[notebook]"
uv add pydreamplet --extra notebook

save

svg.save(filename: str, pretty_print: bool = False) -> None

Writes the SVG markup to a file using UTF-8.

svg.save("drawing.svg")

By default, save() writes compact markup. Pass pretty_print=True for indented output.

svg.save("drawing.svg", pretty_print=True)

Inherited Element Methods

attrs

svg.attrs(attributes: dict[str, object]) -> dp.SVG

Sets multiple attributes and returns the SVG instance.

svg.attrs({
    "id": "chart",
    "role": "img",
    "aria_label": "Generated chart",
})

Values set to None remove the corresponding attribute.

svg.attrs({"role": None})

set_attr

svg.set_attr(name: str, value: AttributeValue) -> dp.SVG

Sets a single attribute and returns the SVG instance.

svg.set_attr("data-state", "ready")

set_id

svg.set_id(value: str | None) -> dp.SVG

Sets or removes the id attribute.

svg.set_id("main-svg")
svg.set_id(None)

set_class

svg.set_class(value: str | None) -> dp.SVG

Sets or removes the SVG class attribute.

svg.set_class("responsive")

set_fill

svg.set_fill(value: AttributeValue) -> dp.SVG

Sets the fill attribute on the root SVG element.

svg.set_fill("none")

For most drawings, fill is usually set on child shapes instead of the root.

set_stroke

svg.set_stroke(
    value: AttributeValue,
    width: AttributeValue = None,
    linecap: str | None = None,
    linejoin: str | None = None,
) -> dp.SVG

Sets stroke and optional stroke-related attributes on the root SVG element.

svg.set_stroke("currentColor", width=2, linecap="round", linejoin="round")

This writes stroke, stroke-width, stroke-linecap, and stroke-linejoin.

set_style

svg.set_style(value: str | Mapping[str, AttributeValue] | None) -> dp.SVG

Sets the inline style attribute. A string is used directly.

svg.set_style("display: block; max-width: 100%")

When passed a dictionary, underscores in property names become hyphens and None values are skipped.

svg.set_style({
    "display": "block",
    "max_width": "100%",
    "background": None,
})

Passing None removes the inline style attribute.

set_position

svg.set_position(x: PointLike | Real, y: Real | None = None) -> dp.SVG

Sets x and y attributes on the root SVG element. If the first argument is a point-like object, y can be omitted.

svg.set_position(24, 32)

This is usually more useful for nested SVG fragments than for the top-level document.

set_size

svg.set_size(width: AttributeValue, height: AttributeValue) -> dp.SVG

Sets the rendered width and height attributes. It does not change the viewBox.

svg.set_size(720, 480)

append

svg.append(*children) -> dp.SVG

Appends one or more children to the SVG and returns the SVG instance.

svg.append(
    dp.Rect(x=40, y=40, width=160, height=96, rx=12, fill="#38bdf8"),
    dp.Circle(cx=260, cy=88, r=48, fill="#95cf20"),
)

Children can be pyDreamplet objects with an .element attribute or raw ElementTree elements.

remove

svg.remove(*children) -> dp.SVG

Removes one or more children and returns the SVG instance.

circle = dp.Circle(cx=100, cy=100, r=40)
svg.append(circle)
svg.remove(circle)

to_string

svg.to_string(pretty_print: bool = True) -> str

Serializes the SVG to a string.

markup = svg.to_string()
compact = svg.to_string(pretty_print=False)

str(svg) is equivalent to svg.to_string(pretty_print=False).

has_attr

svg.has_attr(name: str) -> bool

Returns True when the normalized attribute exists.

svg.id = "chart"

print(svg.has_attr("id"))          # True
print(svg.has_attr("class_name"))  # False

find

svg.find(tag: str, nested: bool = False, id: str | None = None)

Finds the first matching child and wraps it in the registered pyDreamplet class when possible.

svg.append(dp.G(id="marks"))

group = svg.find("g", id="marks")

By default, only direct children are searched. Use nested=True to search descendants.

circle = svg.find("circle", nested=True)

find_all

svg.find_all(tag: str, nested: bool = False, class_name: str | None = None) -> list

Finds all matching children and wraps them in registered pyDreamplet classes when possible.

marks = svg.find_all("circle", nested=True, class_name="mark")

copy

svg.copy() -> dp.SVG

Returns a deep copy of the SVG wrapper and the underlying ElementTree element. Mutating the copy does not affect the original.

base = dp.SVG(200, 200)
variant = base.copy()

variant.width = 400

Common Patterns

Create a larger rendered SVG while keeping a stable coordinate system.

svg = dp.SVG(540, 360, width=1080, height=720)

Use svg.w and svg.h for center-based placement.

label = dp.Text(
    "pyDreamplet",
    x=svg.w / 2,
    y=svg.h / 2,
    text_anchor="middle",
    alignment_baseline="middle",
)

Load an SVG, edit a path, and save a copy.

svg = dp.SVG.from_file("logo.svg")

path = svg.find("path", nested=True)
if path:
    path.fill = "currentColor"

svg.save("logo-current-color.svg", pretty_print=True)