SVG

Introduction

Scalable Vector Graphics (svg) support in azul is built around a small, copy-friendly geometry model. You construct paths in memory, tessellate them once, and either upload the result to a GPU buffer or rasterize it via the CPU SVG path. Higher-level XML-driven SVG (image embedding, gradients, full document parsing) goes through the Svg helper.

Geometry types

pub enum SvgPathElement {
    Line(SvgLine),
    QuadraticCurve(SvgQuadraticCurve),
    CubicCurve(SvgCubicCurve),
}

pub struct SvgPath { pub items: SvgPathElementVec }
pub struct SvgMultiPolygon { pub rings: SvgPathVec }

An SvgPath is one open or closed contour built from line and Bézier segments. Several paths combine into an SvgMultiPolygon. Interior holes are represented by reversing the winding order of a ring. The fill rule defaults to Winding. Switch to EvenOdd via SvgFillStyle.fill_rule when interior overlaps need to cancel.

Construct a path with SvgPath::create(items) and a multi-polygon with SvgMultiPolygon::create(rings). Inspect with SvgPath::is_closed, get_start, get_end, get_bounds. Modify with SvgPath::close, reverse, join_with.

SvgRect covers analytic rectangles with optional corner radii (x, y, width, height, radius_top_left, radius_top_right, radius_bottom_left, radius_bottom_right). Use SvgRect::expand, get_center for layout helpers.

Closed and open paths

SvgPath::is_closed() returns true when the last segment's end equals the first segment's start. close() appends an explicit line segment if the path isn't already closed (matching the SVG Z command). Tessellation treats open paths as strokes only. A fill on an open path produces an empty triangle list.

Tessellation

A polygon becomes triangles via tessellate_fill and tessellate_stroke on SvgMultiPolygon. Each returns a TessellatedSvgNode:

pub struct TessellatedSvgNode {
    pub vertices: SvgVertexVec,  // (x, y) pairs
    pub indices: U32Vec,          // triangle list
}

SvgFillStyle controls fill behavior:

  • fill_rule: SvgFillRule. Winding (default) or EvenOdd.
  • tolerance: f32. Curve subdivision tolerance in CSS pixels.
  • anti_alias: bool, high_quality_aa: bool.
  • line_join: SvgLineJoin, miter_limit: f32.
  • transform. An optional pre-tessellation transform.
use azul::svg::{SvgFillStyle, SvgMultiPolygon};

fn polygon() -> SvgMultiPolygon { panic!() }

fn main() {
    let mut style = SvgFillStyle::default();
    style.tolerance = 0.5;
    let mesh = polygon().tessellate_fill(style);   
}

Stroke options live in SvgStrokeStyle:

  • line_width. Stroke width in user units.
  • start_cap / end_cap. SvgLineCap::Butt, SvgLineCap::Square, SvgLineCap::Round.
  • line_join. SvgLineJoin::Miter, MiterClip, Round, Bevel.
  • miter_limit. Ratio of miter length to stroke width.
  • dash_pattern. Optional SvgDashPattern for dashed strokes.

Drawing on the GPU

Once tessellated, a node can be uploaded once and drawn many times by wrapping it in a TessellatedGPUSvgNode:

use azul::svg::*;
use azul::gl::GlContextPtr;

fn ctx() -> GlContextPtr { panic!() }
fn mesh() -> TessellatedSvgNode { panic!() }

let gpu_mesh = TessellatedGPUSvgNode::create(&mesh(), ctx());

Per-frame drawing happens inside an image-rendering callback (see Canvas and GL Textures) by calling Texture::draw_tesselated_svg_gpu_node, optionally with a transform list to translate, rotate, or scale the geometry.

XML-level SVG

Svg is a handle to a parsed SVG document. The constructors Svg::from_string and Svg::from_bytes read a full SVG document with SvgParseOptions:

  • dpi. Default 96.0.
  • default_font_family.
  • font_size.
  • shape_rendering, text_rendering, image_rendering.
  • fontdb.
  • keep_named_groups, languages, relative_image_path.

The result is rendered to a RawImage via SvgRenderOptions, which lets you fix the output size (SvgFitTo::Width, SvgFitTo::Height, SvgFitTo::Zoom, SvgFitTo::Original) or use the document's intrinsic viewport, plus target_size, background_color, and transform. Wrap the returned image in ImageRef::new_rawimage and insert it like any other raster image.

Combining many polygons

TessellatedSvgNode::from_nodes flattens a slice of meshes into a single vertex/index buffer. This matters for performance when drawing thousands of tiny polygons (for example, the opengl example tessellates thousands of map polygons and uploads exactly one fill mesh and one stroke mesh).

Rendering SVG headlessly

The whole SVG-to-pixels pipeline runs without a window. There are two shapes of „headless SVG“:

Inline SVG inside an XHTML document. The XML parser recognises <svg> tags inside <body> and turns them into vector nodes on the returned Dom. Build the Dom with Dom::create_from_parsed_xml, launch the binary with AZ_BACKEND=headless, and the framework rasterises into an in-memory framebuffer instead of opening a window. Pair with AZ_DEBUG=<port> and a take_screenshot request returns a base64 PNG.

AZ_BACKEND=headless AZ_DEBUG=8765 ./my_svg_renderer &
curl -s -X POST http://localhost:8765/ \
    -d '{"type":"take_screenshot"}' \
  | jq -r '.data.value' | base64 -d > out.png

This is the path covered in Headless Rendering. The DOM-side example is in Document Object Model.

Standalone SVG file. When you have an .svg on disk and want a raster out, parse with Svg::from_string (or Svg::from_bytes) and let the renderer produce a RawImage directly. This skips the Dom entirely:

use azul::prelude::*;

let svg_bytes = std::fs::read("logo.svg").unwrap();
let svg = Svg::from_bytes(svg_bytes.into(), SvgParseOptions::default()).unwrap();
let raw = svg.render(SvgRenderOptions::default()).unwrap();
let image = ImageRef::new_rawimage(raw);
// `image` is now embeddable as `Dom::create_image(image)`, or you
// can write the raw RGBA buffer to a PNG with the `image` crate.

Both paths share the SVG renderer described above. The choice is just about whether you want the SVG rendered next to other Dom content (use the inline form) or as an isolated raster image (use the standalone form).

Coming Up Next

  • GL Canvas — Embedding an OpenGL canvas inside a Dom node
  • Images — Loading raster images and CSS backgrounds
  • Styling with CSS — Stylesheets, selectors, and the cascade

Back to guide index