Search guides and the APICtrl K
  1. API reference
  2. Native GUI

gfx/svg.h

SVG, into Paths this rasteriser already knows how to draw.

#include <sonore/gfx/svg.h>
On this page

Design notes

Why an SVG and not a PNG

A plugin editor is resized by the host, and it is resized to whatever the host feels like. A PNG logo at 2x is soft and at 0.5x is mush; the same logo as paths is exact at every size, and costs a few hundred bytes rather than a few hundred kilobytes.

What arrives, and so what is read

The SVGs a plugin draws come out of Figma, Illustrator, Inkscape and the icon sets, and what those write is the list this reads:

  • shapes: path, rect (with rx and ry), circle, ellipse, line, polyline, polygon; groups, <a> and <switch>; nested <svg> viewports;
  • <defs>, <symbol> and <use>, which is how Figma and Illustrator write a shape used twice -- and drawn nowhere else, which is the point of them;
  • fill and stroke as colours (#rgb, #rgba, #rrggbb, #rrggbbaa, rgb(), rgba(), hsl(), hsla(), the CSS colour names), currentColor, and url(#...) to a linear or radial gradient: stops, objectBoundingBox and userSpaceOnUse, gradientTransform, spreadMethod, a focal point, and xlink:href between gradients;
  • stroke-width, -linecap, -linejoin, -miterlimit, -dasharray, -dashoffset; fill-rule; opacity, fill-opacity, stroke-opacity;
  • clip-path (clipPath with clipPathUnits and clip-rule) and mask (luminance or alpha, maskUnits, maskContentUnits), which is what Figma wraps every frame in;
  • group opacity, drawn as a layer, so overlaps inside the group do not show through each other;
  • presentation attributes, style="", and <style> blocks with type, class and id selectors -- Illustrator's default export is all classes;
  • transform with matrix, translate, scale, rotate, skewX and skewY; viewBox and preserveAspectRatio; lengths in px, pt, pc, mm, cm, in, em and percentages; display and visibility;
  • <text> and <tspan> (and <a> inside them): x, y, dx and dy, singly or as a list per character; text-anchor per chunk; font-family, -size, -weight and -style, matched to a face by FontMatcher (font_match.h) with a variable font's weight axis used where it has one; fill and stroke and every paint above, opacity, transforms, letter- and word-spacing, xml:space; text inside a clipPath. Each run of text is also kept as a DrawableText, so what an SVG said can be read back.

Not read, and named rather than left to be discovered: <textPath>, rotate= and textLength on text, vertical text, <image>, patterns, markers, filters (a Figma drop shadow is a filter: the shape draws, without its shadow), blend modes, and CSS selectors beyond a type, classes and an id. An element with a filter is drawn as if it had none, which is nearer what was meant than not drawing it at all.

Text, and the machine it is drawn on

Text is turned into glyph outlines at parse time and drawn as shapes, so a clip, a mask, a gradient or a layer treats a letter exactly as it treats a path. WHICH outlines depends on the fonts: a family list is matched against the faces a plugin registered and then the machine's, as a browser does (font_match.h says how). Two machines with different fonts draw the same SVG differently -- which is why an SVG whose text must not move registers the face it was designed with and parses with a FontMatcher that looks at nothing else, or has its text converted to outlines before export.

How it is drawn

Parsed ONCE: the document becomes a list of shapes, already transformed into the root's coordinates, with their paints resolved -- and a short list of operations saying where a clip, a mask or a layer begins and ends. Drawing is a walk over that list. An editor repaints thirty times a second, and a logo must not be re-parsed, re-cascaded or re-transformed on any of them.

Types 3

One filled and/or stroked shape out of an SVG.

Everything is in the document's coordinates -- the root viewBox's -- with every transform above the shape already applied: the path, the stroke width and the dashes, and the gradients.

A line of text in a box: the text, the Font it is drawn in, its colour, and a Parallelogram it sits in -- turned or sheared with the box.

Laid out as Font::drawIn lays a line out: justified across the box's top edge, centred down its left edge by the font's ascent and descent rather than by the glyphs a particular string happens to use. The box's edges are measured in the font's own pixels, so a box twice as wide as the text is room for justification, not a stretch.

Drawable keeps one of these for every run of text in an SVG -- each piece that has one style and one position -- so a caller can ask what an SVG said, in which face, and where, after the parse has turned it to shapes.

An SVG, parsed into shapes.

Nothing here holds text or a document tree after the parse: drawing is a loop over shapes, with the clips, masks and layers between them, and costs nothing per frame that a hand-written paint() would not.

↑↓ to move↵ to openEvery public name in the headers, and every guide