svg
@profile(t1);
import fs from "fs";
import io from "io";
import gfx from "gfx";
import svg from "svg";
fn main() -> i32 {
let bytes: string = fs.read_file("icons/house.svg") or { return 1; };
var icon: svg.Image = svg.parse(bytes) or {
io.println("not an SVG image: {err}");
return 2;
};
let size: svg.Size = icon.size(); // 24 x 24 for a Lucide icon
// Rendered at 48 px in dark grey: a new Argb surface, as image.decode gives
var picture: gfx.Surface = icon.render(48, 48, #202020) or { return 3; };
// ... and drawn with gfx like any image
var screen: gfx.Surface = gfx.new_surface(320, 200, gfx.Format.Rgbx) or { return 4; };
screen.clear(#F4F4F4);
screen.draw(16, 16, &picture);
io.println("{size.w} x {size.h}, checksum {screen.checksum()}");
picture.free();
screen.free();
return 0;
}svg draws SVG files at any size. render gives a gfx.Surface in the
Argb format, exactly what image.decode gives for a PNG,
so an icon set in SVG and one in PNG are drawn the same way. svg is a
pure module: it reads no files, you hand it bytes.
| Function | Does |
|---|---|
svg.parse(bytes) -> Image or SvgError | Read an SVG document |
svg.from_doc(&doc) -> Image or SvgError | An image from an xml tree you read or built |
image.size() -> Size | Its natural size in CSS pixels (w, h, f64) |
image.render(w, h, current) -> gfx.Surface or SvgError | Draw it at w x h on a new Argb surface; free it with free() |
image.render_into(&surface, current) | Draw it over a surface you have, at that surface’s size |
svg.add_face(family, weight, italic, bytes) -> bool | Give text a font (below) |
current is the colour currentColor stands for: icon sets such as
Lucide stroke and fill with it, so one file draws in any colour. An image
with a viewBox is fitted into w x h keeping its aspect ratio (as its
preserveAspectRatio says); ask size() to pick a w and h that match.
SvgError | Prints |
|---|---|
Malformed | not well-formed XML |
NotSvg | not an SVG document (no svg root element) |
InvalidSize | invalid image size |
OutOfMemory | out of memory |
A file with an XML error is Malformed, as browsers refuse it. To draw
what can be read of one anyway:
let doc: xml.Doc = xml.parse_lenient(bytes);
var im: svg.Image = svg.from_doc(&doc) or { return 1; };What draws
Shapes (rect with rounded corners, circle, ellipse, line,
polyline, polygon) and paths with every command, arcs included;
groups, use and symbol, nested svg with viewBox; transforms;
fills and strokes in colours, currentColor, linear and radial gradients;
stroke widths, caps, joins, miter limits and dashes; opacity on shapes
and groups; clip paths and masks; markers (arrowheads) at a path’s ends
and corners; text and tspan with anchors and baselines.
Styles come from attributes (fill="red"), <style> sheets and style
attributes, with inheritance and !important. The sheets understand the
CSS that SVG files use: type, .class, #id, * and [attribute]
selectors, the descendant, >, + and ~ combinators, comma lists,
specificity; colours by name, #hex, rgb() and hsl(). A rule with a
selector beyond that (:hover, :first-child) is skipped, and @media
blocks are ignored.
Not drawn: image, pattern, filter, foreignObject, textPath,
per-letter positions and rotations.
Text
svg draws text with TrueType fonts you register, since it reads no
files itself. Read the font with fs and register it once; a
family name in the file that has no face of its own falls back to
sans-serif:
let regular: string = fs.read_file("C:/Windows/Fonts/arial.ttf") or { return 1; };
svg.add_face("sans-serif", 400, false, regular);
let bold: string = fs.read_file("C:/Windows/Fonts/arialbd.ttf") or { return 1; };
svg.add_face("sans-serif", 700, false, bold);The faces are kept for the whole program and every image uses them. A character a face lacks is taken from the first registered face that has it. Without any face, text is not drawn.
Last modified: