Skip to content
svg
SvgCanvas

SvgCanvas

Mojo struct 🡭

SvgCanvas

@memory_only
struct SvgCanvas

Accumulates SVG body markup for a width x height document. width/height are public fields, as on Canvas.

Fields

  • width (Int)
  • height (Int)

Implemented traits

AnyType, Deinitable, DrawTarget, Movable

Methods

__init__

fn def __init__(out self, width: Int, height: Int)

An empty width x height SVG document.

Args:

  • width (Int): Document width in pixels.
  • height (Int): Document height in pixels.
  • self (Self)

Returns:

Self

save

fn def save(mut self)

Push the current transform, blend mode and clip depth for restore to put back, as Canvas.save does.

Args:

  • self (Self)

restore

fn def restore(mut self)

Pop the state save pushed. A no-op with nothing saved, matching Canvas.restore.

Args:

  • self (Self)

translate

fn def translate(mut self, tx: Float64, ty: Float64)

Shift subsequent elements by (tx, ty) in the current user space – see Canvas.translate.

Args:

  • self (Self)
  • tx (Float64): Horizontal shift.
  • ty (Float64): Vertical shift.

rotate

fn def rotate(mut self, angle: Float64)

Turn subsequent elements by angle radians about the current origin – see Canvas.rotate.

Args:

  • self (Self)
  • angle (Float64): Radians.

scale

fn def scale(mut self, sx: Float64, sy: Float64)

Scale subsequent elements about the current origin – see Canvas.scale.

Args:

  • self (Self)
  • sx (Float64): Horizontal factor.
  • sy (Float64): Vertical factor.

transform

fn def transform(mut self, matrix: Matrix2D)

Compose matrix into the current transform, applied first – the order Canvas.transform composes in.

Args:

  • self (Self)
  • matrix (Matrix2D): The map to apply first.

set_transform

fn def set_transform(mut self, matrix: Matrix2D)

Replace the current transform outright.

Args:

  • self (Self)
  • matrix (Matrix2D): The new map from user space to document pixels.

reset_transform

fn def reset_transform(mut self)

Back to the identity: elements carry no transform.

Args:

  • self (Self)

current_transform

fn def current_transform(self) -> Matrix2D

The map every element drawn now carries.

Args:

  • self (Self)

Returns:

Matrix2D: The current transform; the identity if none is set.

has_transform

fn def has_transform(self) -> Bool

Whether elements drawn now carry a transform attribute.

Args:

  • self (Self)

Returns:

Bool: True if the current transform is not the identity.

set_blend_mode

fn def set_blend_mode(mut self, mode: BlendMode)

Set how elements drawn from here on combine with what is already painted, the counterpart of Canvas.set_blend_mode.

A blend mode (MULTIPLY through LUMINOSITY) becomes a style="mix-blend-mode:..." attribute on each element. CSS has no keyword for the Porter-Duff operators – SOURCE, DESTINATION_IN, XOR, ADD and the rest – so those emit no attribute at all and render as ordinary source-over. A caller needing them has to use the raster backend.

save/restore carry the mode, as they do the transform.

Args:

  • self (Self)
  • mode (BlendMode): The blend mode later elements carry.

blend_mode

fn def blend_mode(self) -> BlendMode

The blend mode elements drawn now carry.

Args:

  • self (Self)

Returns:

BlendMode: The current mode, BlendMode.SOURCE_OVER until set_blend_mode says otherwise.

set_color_space

fn def set_color_space(mut self, space: ColorSpace)

Set the space elements drawn from here on composite in, the counterpart of Canvas.set_color_space: under LINEAR each element carries color-interpolation="linearRGB", which asks the viewer to composite it in linear light. Viewer support varies; a caller needing the result guaranteed has the raster backend. save/restore carry the space.

Args:

  • self (Self)
  • space (ColorSpace): The color space later elements carry.

color_space

fn def color_space(self) -> ColorSpace

The color space elements drawn now carry.

Args:

  • self (Self)

Returns:

ColorSpace: The current space, ColorSpace.SRGB until set_color_space says otherwise.

fill_rect

fn def fill_rect(mut self, x: Int, y: Int, width: Int, height: Int, color: Color)

Emit a <rect> element.

Args:

  • self (Self)
  • x (Int): Rectangle’s left edge.
  • y (Int): Rectangle’s top edge.
  • width (Int): Rectangle’s width.
  • height (Int): Rectangle’s height.
  • color (Color): Fill color.
fn def fill_rect(mut self, x: Float64, y: Float64, width: Float64, height: Float64, color: Color)

fill_rect for a geometric box, snapped to whole pixels in user space – the same snap the raster backend applies, so both emit the same rectangle – and written as the Int form.

Args:

  • self (Self)
  • x (Float64): Left edge.
  • y (Float64): Top edge.
  • width (Float64): Width.
  • height (Float64): Height.
  • color (Color): Fill color.

fill_rect_gradient

fn def fill_rect_gradient(mut self, x: Float64, y: Float64, width: Float64, height: Float64, gradient: LinearGradient)

fill_rect_gradient for a geometric box, snapped to whole pixels as the Float64 fill_rect is.

Args:

  • self (Self)
  • x (Float64): Left edge.
  • y (Float64): Top edge.
  • width (Float64): Width.
  • height (Float64): Height.
  • gradient (LinearGradient): Fill source, projected across the rectangle.
fn def fill_rect_gradient(mut self, x: Int, y: Int, width: Int, height: Int, gradient: LinearGradient)

An SVG <linearGradient> with gradientUnits="userSpaceOnUse". LinearGradient’s (x0, y0)-(x1, y1) axis is already in the same absolute pixel space as this document’s <rect>, so userSpaceOnUse carries it over untranslated instead of SVG’s default shape-relative objectBoundingBox units.

Emits a fresh <defs><linearGradient id="gradN"> per call; <stop> elements come out in ascending-offset order.

Args:

  • self (Self)
  • x (Int): Rectangle’s left edge.
  • y (Int): Rectangle’s top edge.
  • width (Int): Rectangle’s width.
  • height (Int): Rectangle’s height.
  • gradient (LinearGradient): Fill source, projected across the rectangle.

fill_rect_radial_gradient

fn def fill_rect_radial_gradient(mut self, x: Int, y: Int, width: Int, height: Int, gradient: RadialGradient)

fill_rect_gradient for a RadialGradient: a fresh <defs><radialGradient id="gradN"> in the document’s pixel space, with the focal circle when the gradient has one, and a <rect> filled from it.

Args:

  • self (Self)
  • x (Int): Rectangle’s left edge.
  • y (Int): Rectangle’s top edge.
  • width (Int): Rectangle’s width.
  • height (Int): Rectangle’s height.
  • gradient (RadialGradient): Fill source, in the document’s pixel space.

fill_path_gradient_aa

fn def fill_path_gradient_aa(mut self, path: Path, gradient: LinearGradient, fill_rule: FillRule = FillRule.EVEN_ODD)

fill_path_aa filled from a LinearGradient: a fresh <defs><linearGradient> in the document’s pixel space and a <path> referencing it, with the fill rule written out as fill_path_aa writes it.

Args:

  • self (Self)
  • path (Path): Path to fill.
  • gradient (LinearGradient): Fill source, in the document’s pixel space.
  • fill_rule (FillRule): EVEN_ODD (default) or NONZERO – see FillRule.

fill_path_radial_gradient_aa

fn def fill_path_radial_gradient_aa(mut self, path: Path, gradient: RadialGradient, fill_rule: FillRule = FillRule.EVEN_ODD)

fill_path_gradient_aa for a RadialGradient.

Args:

  • self (Self)
  • path (Path): Path to fill.
  • gradient (RadialGradient): Fill source, in the document’s pixel space.
  • fill_rule (FillRule): EVEN_ODD (default) or NONZERO – see FillRule.

draw_line_aa

fn def draw_line_aa(mut self, x0: Int, y0: Int, x1: Int, y1: Int, color: Color, width: Float64 = 1, dashes: List[Float64] = List(), dash_offset: Float64 = 0, cap: LineCap = LineCap.ROUND, join: LineJoin = LineJoin.ROUND, miter_limit: Float64 = 4)

Emit a <line> element, round-capped by default.

Args:

  • self (Self)
  • x0 (Int): Start point x.
  • y0 (Int): Start point y.
  • x1 (Int): End point x.
  • y1 (Int): End point y.
  • color (Color): Line color.
  • width (Float64): Stroke width in pixels.
  • dashes (List[Float64]): On/off segment lengths in user-space pixels, cycled along the line. Empty (default) draws a solid line.
  • dash_offset (Float64): Distance into the dash pattern the line starts at.
  • cap (LineCap): How the two ends are finished – see LineCap.
  • join (LineJoin): Unused for a single segment, which has no corners.
  • miter_limit (Float64): Unused for a single segment.
fn def draw_line_aa(mut self, x0: Float64, y0: Float64, x1: Float64, y1: Float64, color: Color, width: Float64 = 1, dashes: List[Float64] = List(), dash_offset: Float64 = 0, cap: LineCap = LineCap.ROUND, join: LineJoin = LineJoin.ROUND, miter_limit: Float64 = 4)

Emit a <line> element, round-capped by default.

Args:

  • self (Self)
  • x0 (Float64): Start point x.
  • y0 (Float64): Start point y.
  • x1 (Float64): End point x.
  • y1 (Float64): End point y.
  • color (Color): Line color.
  • width (Float64): Stroke width in pixels.
  • dashes (List[Float64]): On/off segment lengths in user-space pixels, cycled along the line. Empty (default) draws a solid line.
  • dash_offset (Float64): Distance into the dash pattern the line starts at.
  • cap (LineCap): How the two ends are finished – see LineCap.
  • join (LineJoin): Unused for a single segment, which has no corners.
  • miter_limit (Float64): Unused for a single segment.

fill_circle_aa

fn def fill_circle_aa(mut self, cx: Float64, cy: Float64, radius: Float64, color: Color)

A <circle> at a sub-pixel center and radius.

Args:

  • self (Self)
  • cx (Float64): Center x.
  • cy (Float64): Center y.
  • radius (Float64): Circle radius in pixels.
  • color (Color): Fill color.
fn def fill_circle_aa(mut self, cx: Int, cy: Int, radius: Int, color: Color)

Emit a <circle> element.

Args:

  • self (Self)
  • cx (Int): Center x.
  • cy (Int): Center y.
  • radius (Int): Circle radius in pixels.
  • color (Color): Fill color.

fill_circles_aa

fn def fill_circles_aa(mut self, centers: List[FPoint], radius: Float64, color: Color)

DrawTarget’s batched disks. This backend emits per marker whichever entry point is used, so the batch is the loop and the output is unchanged by construction.

Args:

  • self (Self)
  • centers (List[FPoint]): Sub-pixel centre of each marker, in draw order.
  • radius (Float64): Radius shared by every marker, in pixels.
  • color (Color): Fill color shared by every marker.

Raises:

fn def fill_circles_aa(mut self, centers: List[FPoint], radius: Float64, colors: List[Color])

fill_circles_aa with a color per marker.

Args:

  • self (Self)
  • centers (List[FPoint]): Sub-pixel centre of each marker, in draw order.
  • radius (Float64): Radius shared by every marker, in pixels.
  • colors (List[Color]): One color per centre, same length as centers.

Raises:

Error: If colors is not the same length as centers.

fill_mesh

fn def fill_mesh(mut self, points: List[FPoint], faces: List[Int], colors: List[Color])

DrawTarget’s mesh: one <path> per face. Browsers seam adjacent anti-aliased fills the way the raster backend did, so an opaque face also carries stroke in its own color at half a pixel, which covers the seam without changing the face’s extent by more than that. A translucent face is not stroked: the stroke would double its alpha along every edge, which is a darker seam in place of a lighter one.

Args:

  • self (Self)
  • points (List[FPoint]): The vertices, in user space.
  • faces (List[Int]): Index triples into points, in draw order.
  • colors (List[Color]): One color per triangle.

Raises:

Error: faces is not whole triples, an index is out of range, or colors is not one per triangle.

fill_mesh_shaded

fn def fill_mesh_shaded(mut self, points: List[FPoint], faces: List[Int], vertex_colors: List[Color])

DrawTarget’s smooth-shaded mesh, flat here: each face at the mean of its three corner colors. SVG has no shipping way to interpolate color across a triangle – mesh gradients are SVG 2 and unimplemented in browsers – so this is the same faceted approximation every SVG exporter makes, and the one place this backend’s picture differs from the raster one by design. The faces are seam-treated as fill_mesh’s are.

Args:

  • self (Self)
  • points (List[FPoint]): The vertices, in user space.
  • faces (List[Int]): Index triples into points, in draw order.
  • vertex_colors (List[Color]): One color per vertex.

Raises:

Error: faces is not whole triples, an index is out of range, or vertex_colors is not one per vertex.

fill_ellipses_aa

fn def fill_ellipses_aa(mut self, centers: List[FPoint], rx: Float64, ry: Float64, color: Color)

DrawTarget’s batched ellipses. This backend emits per marker whichever entry point is used, so the batch is the loop and the output is unchanged by construction.

Args:

  • self (Self)
  • centers (List[FPoint]): Sub-pixel centre of each marker, in draw order.
  • rx (Float64): Horizontal radius shared by every marker, in pixels.
  • ry (Float64): Vertical radius shared by every marker, in pixels.
  • color (Color): Fill color shared by every marker.

Raises:

fn def fill_ellipses_aa(mut self, centers: List[FPoint], rx: Float64, ry: Float64, colors: List[Color])

fill_ellipses_aa with a color per marker.

Args:

  • self (Self)
  • centers (List[FPoint]): Sub-pixel centre of each marker, in draw order.
  • rx (Float64): Horizontal radius shared by every marker, in pixels.
  • ry (Float64): Vertical radius shared by every marker, in pixels.
  • colors (List[Color]): One color per centre, same length as centers.

Raises:

Error: If colors is not the same length as centers.

draw_circle_aa

fn def draw_circle_aa(mut self, cx: Float64, cy: Float64, radius: Float64, color: Color, width: Float64 = 1)

Emit an unfilled <circle> element, at a sub-pixel center and radius.

Args:

  • self (Self)
  • cx (Float64): Center x, sub-pixel.
  • cy (Float64): Center y, sub-pixel.
  • radius (Float64): Circle radius in pixels, to the middle of the stroke.
  • color (Color): Outline color.
  • width (Float64): Stroke width in pixels.

fill_ellipse_aa

fn def fill_ellipse_aa(mut self, cx: Float64, cy: Float64, rx: Float64, ry: Float64, color: Color)

An <ellipse> at a sub-pixel center and radii.

Args:

  • self (Self)
  • cx (Float64): Center x.
  • cy (Float64): Center y.
  • rx (Float64): Horizontal radius in pixels.
  • ry (Float64): Vertical radius in pixels.
  • color (Color): Fill color.
fn def fill_ellipse_aa(mut self, cx: Int, cy: Int, rx: Int, ry: Int, color: Color)

Emit an <ellipse> element.

Args:

  • self (Self)
  • cx (Int): Center x.
  • cy (Int): Center y.
  • rx (Int): Horizontal radius in pixels.
  • ry (Int): Vertical radius in pixels.
  • color (Color): Fill color.

draw_ellipse_aa

fn def draw_ellipse_aa(mut self, cx: Int, cy: Int, rx: Int, ry: Int, color: Color)

Emit an unfilled <ellipse> element, ~1px stroke.

Args:

  • self (Self)
  • cx (Int): Center x.
  • cy (Int): Center y.
  • rx (Int): Horizontal radius in pixels.
  • ry (Int): Vertical radius in pixels.
  • color (Color): Outline color.
fn def draw_ellipse_aa(mut self, cx: Float64, cy: Float64, rx: Float64, ry: Float64, color: Color, width: Float64 = 1)

Emit an unfilled <ellipse> element, at a sub-pixel center and radii.

Args:

  • self (Self)
  • cx (Float64): Center x, sub-pixel.
  • cy (Float64): Center y, sub-pixel.
  • rx (Float64): Horizontal radius in pixels, to the middle of the stroke.
  • ry (Float64): Vertical radius in pixels, to the middle of the stroke.
  • color (Color): Outline color.
  • width (Float64): Stroke width in pixels.

fill_arc_aa

fn def fill_arc_aa(mut self, cx: Float64, cy: Float64, radius: Float64, start_angle: Float64, end_angle: Float64, color: Color)

A wedge, drawn as M center L start-point A ... end-point Z. sweep_flag=1 with no sign flip: SVG’s space is y-down like the raster canvas’s, so increasing angle sweeps clockwise in both.

Args:

  • self (Self)
  • cx (Float64): Center x.
  • cy (Float64): Center y.
  • radius (Float64): Wedge radius in pixels.
  • start_angle (Float64): Sweep start, radians, 0 pointing along +x.
  • end_angle (Float64): Sweep end, radians. Must be >= start_angle.
  • color (Color): Fill color.

fill_arcs_aa

fn def fill_arcs_aa(mut self, centers: List[FPoint], radius: Float64, start_angle: Float64, end_angle: Float64, color: Color)

DrawTarget’s batched wedges. This backend emits one element per wedge whichever entry point is used, so the batch is the loop and the output is unchanged by construction.

Args:

  • self (Self)
  • centers (List[FPoint]): Sub-pixel centre of each wedge, in draw order.
  • radius (Float64): Radius shared by every wedge, in pixels.
  • start_angle (Float64): Start of the sweep, radians, shared.
  • end_angle (Float64): End of the sweep, radians, shared.
  • color (Color): Fill color shared by every wedge.

Raises:

fn def fill_arcs_aa(mut self, centers: List[FPoint], radius: Float64, start_angle: Float64, end_angle: Float64, colors: List[Color])

fill_arcs_aa with a color per wedge.

Args:

  • self (Self)
  • centers (List[FPoint]): Sub-pixel centre of each wedge, in draw order.
  • radius (Float64): Radius shared by every wedge, in pixels.
  • start_angle (Float64): Start of the sweep, radians, shared.
  • end_angle (Float64): End of the sweep, radians, shared.
  • colors (List[Color]): One color per centre, same length as centers.

Raises:

Error: If colors is not the same length as centers.

fill_ring_sector_aa

fn def fill_ring_sector_aa(mut self, cx: Float64, cy: Float64, inner_radius: Float64, outer_radius: Float64, start_angle: Float64, end_angle: Float64, color: Color)

A donut wedge: M outer-start A ... outer-end L inner-end A ... inner-start Z. The outer arc sweeps forward (sweep_flag=1), a radial line runs inward, then the inner arc sweeps backward (sweep_flag=0), closing the ring in one loop.

Args:

  • self (Self)
  • cx (Float64): Center x.
  • cy (Float64): Center y.
  • inner_radius (Float64): Ring’s inner edge, in pixels.
  • outer_radius (Float64): Ring’s outer edge, in pixels. Must exceed inner_radius.
  • start_angle (Float64): Sweep start, radians, 0 pointing along +x.
  • end_angle (Float64): Sweep end, radians. Must be >= start_angle.
  • color (Color): Fill color.

stroke_path_aa

fn def stroke_path_aa(mut self, path: Path, color: Color, width: Float64 = 1, dashes: List[Float64] = List(), dash_offset: Float64 = 0, cap: LineCap = LineCap.ROUND, join: LineJoin = LineJoin.ROUND, miter_limit: Float64 = 4)

Emit a <path> element, stroked only.

Args:

  • self (Self)
  • path (Path): Path to stroke.
  • color (Color): Stroke color.
  • width (Float64): Stroke width in pixels.
  • dashes (List[Float64]): On/off segment lengths in user-space pixels, cycled along the stroke. Empty (default) draws a solid line.
  • dash_offset (Float64): Distance into the dash pattern the stroke starts at.
  • cap (LineCap): How an open sub-path’s two ends are finished – see LineCap.
  • join (LineJoin): How corners are turned – see LineJoin.
  • miter_limit (Float64): Ratio past which a MITER join falls back to BEVEL, as a multiple of half the stroke width.

fill_path_aa

fn def fill_path_aa(mut self, path: Path, color: Color, fill_rule: FillRule = FillRule.EVEN_ODD)

Emit a <path> element, filled only. The default rule is written out as fill-rule="evenodd", since SVG’s own default is nonzero and the raster backend’s is even-odd; a path with a hole has to come out the same on both. NONZERO adds nothing, being what a renderer does when the attribute is absent.

Args:

  • self (Self)
  • path (Path): Path to fill.
  • color (Color): Fill color.
  • fill_rule (FillRule): EVEN_ODD (default) or NONZERO – see FillRule.

push_clip

fn def push_clip(mut self, x: Int, y: Int, width: Int, height: Int)

Restrict subsequent elements to a rectangle, the counterpart of Canvas.push_clip: a <clipPath id="clipN"> holding the <rect> (in the current user space, so it carries the transform attribute an element drawn now would) and a <g clip-path="url(#clipN)"> every element until the matching pop lands inside. Clips nest, and the viewer intersects them.

Clips and annotated groups do not interleave: pushing or popping a clip closes an open annotated group first, so the markup stays well-formed.

Known issue for anyone asserting on the markup: the <clipPath>’s <rect> is an element of the document like any other, so a test counting <rect> elements sees one more per clip pushed. Count inside <defs> separately, or strip the defs before counting; a consumer adopting the clip had eight tests change their counts this way.

Args:

  • self (Self)
  • x (Int): Clip rectangle’s left edge.
  • y (Int): Clip rectangle’s top edge.
  • width (Int): Clip rectangle’s width.
  • height (Int): Clip rectangle’s height.

push_clip_path

fn def push_clip_path(mut self, path: Path, fill_rule: FillRule = FillRule.EVEN_ODD)

Restrict subsequent elements to path’s interior, the counterpart of Canvas.push_clip_path: a <clipPath> holding the <path> with its fill rule as clip-rule, and a <g> referencing it. See push_clip for how clips nest and how they relate to annotated groups.

Args:

  • self (Self)
  • path (Path): Shape to clip to. Its interior is what stays visible.
  • fill_rule (FillRule): EVEN_ODD (default) or NONZERO – see FillRule.

pop_clip

fn def pop_clip(mut self)

Remove the most recently pushed clip, rectangle or path: this backend keeps one stack of <g> wrappers, so the two pops are the same operation. A no-op with nothing pushed, matching Canvas.pop_clip.

Args:

  • self (Self)

pop_clip_path

fn def pop_clip_path(mut self)

Remove the most recently pushed clip, rectangle or path – see pop_clip.

Args:

  • self (Self)

draw_image

fn def draw_image(mut self, image: Canvas, x: Float64, y: Float64, width: Float64 = 0, height: Float64 = 0)

Emit an <image> element with image as a PNG in a base64 data: URI, its top-left at (x, y), scaled to width x height (its own pixel size when 0): DrawTarget’s image primitive. preserveAspectRatio="none" stretches the pixels to the box, and image-rendering:pixelated keeps the cells hard-edged when a viewer scales them, so a heatmap reads as it does on the raster backend. The transform and blend attributes are the ones every element carries. A 512x512 array is tens of kilobytes this way against megabytes as a <rect> per cell, which is the case the primitive exists for.

Args:

  • self (Self)
  • image (Canvas): The pixels to draw. Unchanged.
  • x (Float64): Left edge.
  • y (Float64): Top edge.
  • width (Float64): Drawn width, or 0 for image.width.
  • height (Float64): Drawn height, or 0 for image.height.

Raises:

Error: Never in practice; the PNG encoder’s signature.

begin_batch

fn def begin_batch(mut self)

Nothing to defer: every element goes into the markup in order as it is drawn. Here so a caller generic over DrawTarget can batch for the raster backend; see Canvas.begin_batch.

Args:

  • self (Self)

end_batch

fn def end_batch(mut self)

The other half of begin_batch, also nothing.

Args:

  • self (Self)

begin_annotated_group

fn def begin_annotated_group(mut self, title: String)

Open <g><title>title</title>, labeling every element emitted until end_annotated_group. Browsers show a <title> inside a <g> as a hover tooltip over anything in the group, which is what gives a chart per-datum tooltips without any scripting.

title is escaped as element content, so it may contain &, < and > freely.

Groups do not nest: opening one while another is open closes the first. See the DrawTarget docstring for why the operation is scoped rather than a parameter on each primitive.

Args:

  • self (Self)
  • title (String): Human-readable label, escaped here. Pass it raw.

end_annotated_group

fn def end_annotated_group(mut self)

Close the group begin_annotated_group opened, a no-op when none is open. Emitting a stray </g> would make the document malformed, which is worse than ignoring an unbalanced call, and it matches Canvas.pop_clip treating an unbalanced close as nothing to undo.

Args:

  • self (Self)

draw_text

fn def draw_text(mut self, x: Int, y: Int, text: String, color: Color, size: Float64, align: TextAlign, family: String = "sans-serif", rotation: Float64 = 0, weight: FontWeight = FontWeight.NORMAL)

Draw a <text> element at a whole-pixel anchor with a literal CSS font-family. The DrawTarget form below takes a sub-pixel anchor, a slant and the raster backend’s family names; this one predates it and stays for callers that write the CSS stack themselves.

family is always emitted, defaulting to "sans-serif", since a viewer without one falls back to its own varying default. It is a different kind of value from raster draw_text’s family despite the shared name: raster’s resolves to one concrete font file, while this is a literal CSS font-family – keyword, face name, or comma-separated stack – interpreted by whatever renders the SVG.

(x, y) is the baseline anchor, matching raster draw_text, since SVG <text> anchors y to the alphabetic baseline already. text-anchor (start/middle/end) is the equivalent of align’s three values.

rotation is radians and rotates the whole <text> around its (x, y) anchor via transform="rotate(<degrees> <x> <y>)", omitted at 0.0. No sign flip: raster and SVG viewport space both put y downward.

weight emits font-weight="bold" for FontWeight.BOLD and is omitted at NORMAL.

Args:

  • self (Self)
  • x (Int): Anchor x – baseline left end for TextAlign.LEFT.
  • y (Int): Anchor y – baseline.
  • text (String): Text to draw. No line-break handling for embedded “\n”.
  • color (Color): Text color.
  • size (Float64): Font size in pixels.
  • align (TextAlign): Horizontal alignment relative to (x, y).
  • family (String): A literal CSS font-family value (keyword, face name, or comma-separated stack), not a font-matching query.
  • rotation (Float64): Radians, rotating the whole <text> element around (x, y).
  • weight (FontWeight): Normal/bold weight.
fn def draw_text(mut self, x: Float64, y: Float64, text: String, color: Color, size: Float64, family: String = "Sans", slant: FontSlant = FontSlant.NORMAL, weight: FontWeight = FontWeight.NORMAL, rotation: Float64 = 0, align: TextAlign = TextAlign.LEFT, *, mut cache: FontCache)

The DrawTarget form: a <text> element at a sub-pixel anchor, with family in the raster backend’s terms. The generic names ("Sans", "Serif", "Monospace", and their CSS spellings) become the CSS generic keywords; a face name or an explicit CSS stack passes through verbatim, so a caller can still write the stack itself. slant emits font-style (italic or oblique), omitted when upright.

cache is unused: a <text> element carries no glyphs, so there is nothing to resolve. It is in the signature so one generic call serves every backend.

Everything the whole-pixel overload says about the anchor, text-anchor, rotation and weight holds here.

Args:

  • self (Self)
  • x (Float64): Anchor x, sub-pixel – baseline left end for TextAlign.LEFT.
  • y (Float64): Anchor y, sub-pixel – baseline.
  • text (String): Text to draw. No line-break handling for embedded “\n”.
  • color (Color): Text color.
  • size (Float64): Font size in pixels.
  • family (String): Font family name or generic alias, as the raster backend takes it.
  • slant (FontSlant): Upright, italic or oblique.
  • weight (FontWeight): Normal/bold weight.
  • rotation (Float64): Radians, rotating the whole <text> element around (x, y).
  • align (TextAlign): Horizontal alignment relative to (x, y).
  • cache (FontCache): Unused here; see above.

Raises:

Error: Never; the signature is the trait’s.

draw_text_runs

fn def draw_text_runs(mut self, x: Float64, y: Float64, runs: List[TextRun], color: Color, family: String = "Sans", weight: FontWeight = FontWeight.NORMAL, rotation: Float64 = 0, align: TextAlign = TextAlign.LEFT, *, mut cache: FontCache)

One <text> element with a <tspan> per run: SVG’s own form for a label whose pieces differ in size or slant, and what keeps the label one string in a viewer – one selection, one copy with its spaces in place, one announcement – where the same runs as separate draw_text calls are as many elements. The element carries the family, weight, fill, anchor and transform as draw_text’s does; each <tspan> carries its font-size, its font-style when slanted, its dx, and a dy that is the difference from the previous run’s baseline, since a <tspan>’s dy shifts the current position for everything after it where TextRun.dy names a baseline. Nothing is written between the tspans: whitespace there would be a space in the rendered text.

The viewer’s own font metrics advance the pen from run to run and resolve align, as they do for a single draw_text; a run’s kern and rise are what this backend carries, not the anchor the other backends measure. A run with no text is left out, and a label of nothing but those writes nothing.

cache is unused, as in draw_text.

Args:

  • self (Self)
  • x (Float64): Anchor x, sub-pixel – the label’s left end for TextAlign.LEFT.
  • y (Float64): Anchor y, sub-pixel – the label’s baseline.
  • runs (List[TextRun]): The label’s runs, in reading order.
  • color (Color): Fill color.
  • family (String): Font family name or generic alias, as the raster backend takes it; mapped as draw_text maps it.
  • weight (FontWeight): Normal/bold weight, on the element.
  • rotation (Float64): Radians, rotating the whole <text> element around (x, y).
  • align (TextAlign): text-anchor for the whole label.
  • cache (FontCache): Unused here; see above.

Raises:

Error: Never; the signature is the trait’s.

stroke_text

fn def stroke_text(mut self, x: Int, y: Int, text: String, color: Color, size: Float64, align: TextAlign, width: Float64 = 1, family: String = "sans-serif", rotation: Float64 = 0, weight: FontWeight = FontWeight.NORMAL, join: LineJoin = LineJoin.ROUND, miter_limit: Float64 = 4)

Draw a <text> element outlined rather than filled: fill="none" plus the stroke attributes. The vector counterpart of raster stroke_text; not part of DrawTarget, which carries only the filled draw_text.

Everything draw_text says about (x, y), family, align, rotation and weight holds here unchanged; only the paint differs.

No stroke-linecap is emitted: a glyph outline is a set of closed contours, so it has no ends to cap, and the raster side takes no cap parameter for the same reason.

SVG applies stroke-width in the element’s user space, so an outlined label under a scale thickens with it, as on Canvas.

Args:

  • self (Self)
  • x (Int): Anchor x – baseline left end for TextAlign.LEFT.
  • y (Int): Anchor y – baseline.
  • text (String): Text to draw. No line-break handling for embedded “\n”.
  • color (Color): Stroke color.
  • size (Float64): Font size in pixels.
  • align (TextAlign): Horizontal alignment relative to (x, y).
  • width (Float64): Stroke width in user-space pixels.
  • family (String): A literal CSS font-family value, not a font-matching query – see draw_text.
  • rotation (Float64): Radians, rotating the whole <text> element around (x, y).
  • weight (FontWeight): Normal/bold weight.
  • join (LineJoin): How a corner of the outline is turned – see LineJoin.
  • miter_limit (Float64): Ratio past which a MITER join falls back to BEVEL, as a multiple of half the stroke width.

draw_text_on_path

fn def draw_text_on_path(mut self, path: Path, text: String, color: Color, size: Float64, align: TextAlign, offset: Float64 = 0, family: String = "sans-serif", weight: FontWeight = FontWeight.NORMAL)

Draw text along path, as a <textPath> referring to a <path> in a <defs> block emitted just before it. The vector counterpart of raster draw_text_on_path.

Each call mints a fresh id (tpN), so two calls never collide and a path drawn as well as labeled is written twice rather than shared – the visible path carries its own paint.

offset becomes startOffset, a distance along the path, for all three alignments: SVG anchors a <textPath> at startOffset and then applies text-anchor about it, which is exactly what the raster side’s align does.

A renderer drops the glyphs that do not fit on the path, the rule the raster side applies to a glyph whose center falls past an end.

Args:

  • self (Self)
  • path (Path): Curve the baseline follows.
  • text (String): Text to draw, one line.
  • color (Color): Text color.
  • size (Float64): Font size in pixels.
  • align (TextAlign): Where offset sits in the string – its start, middle or end.
  • offset (Float64): Distance along the path the string is placed at.
  • family (String): A literal CSS font-family value, not a font-matching query – see draw_text.
  • weight (FontWeight): Normal/bold weight.

set_title

fn def set_title(mut self, title: String, description: String = "")

Give the document an accessible title: the root <svg> gains role="img" and aria-label, and <title> (and <desc> when description is non-empty) become its first children, which is what screen readers that walk an SVG’s accessible tree look for. The document-level counterpart of begin_annotated_group.

Both strings are escaped here; pass them raw. Calling again replaces the previous title. An empty title removes it.

This helps where the SVG’s accessible tree is walked: inline markup, a standalone file, or an <object>/<iframe> embed. A plain <img src="chart.svg"> treats the SVG as an opaque image and reads the <img>’s alt text instead.

Args:

  • self (Self)
  • title (String): Short accessible name for the whole document.
  • description (String): Longer description, optional.

to_string

fn def to_string(self) -> String

Args:

  • self (Self)

Returns:

String