A small Zig library for composing 2D geometry primitives and emitting SVG.
- Zig 0.16.0
No external dependencies — everything is built on std.
Fetch the package into your project:
zig fetch --save git+https://github.com/edmBernard/zsvgThen import it in your build.zig:
const zsvg = b.dependency("zsvg", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("zsvg", zsvg.module("zsvg"));const std = @import("std");
const zsvg = @import("zsvg");
pub fn main(init: std.process.Init) !void {
const arena = init.arena.allocator();
var doc: zsvg.Document = .init(arena, 200, 200, .fromHex(0x202124));
defer doc.deinit();
try doc.addCircle(
.{ .center = .{ .x = 100, .y = 100 }, .radius = 40 },
.{
.fill = .hex(0x8AB4F8, 0.9),
.stroke = .solidHex(0xFFFFFF, 2),
},
);
// Stream the document to any `std.Io.Writer`, or save to a file.
try doc.save(arena, init.io, "circle.svg");
}Geometry shapes are plain structs — build them with .{ .field = value }
syntax. Color, Fill, and Stroke ship with small helper constructors
(rgb, fromHex, solid, solidHex, hex) so common cases stay terse.
Point { x, y }withadd,sub,scale,divScalar,dot,norm,eql(epsilon-tolerant),lessThan(strict, sort-safe),transform(Matrix)Matrix— 2D affine transform matching SVG'smatrix(a b c d e f). Build one with.identity,.translation(dx, dy),.rotation(angle),.scaling(sx, sy), compose with.mul(right-to-left), apply with.apply.Line,Triangle,Quadrilateral— each carries itsverticesarray and exposescenter,eql, andtransform(Matrix). Vertices are emitted in the order you provide them (no implicit reordering).Circle { center, radius }witheqlandtransform(Matrix)(radius scaled by the isotropic factorsqrt(|det|)).Bezier— cubic bezier with four controlpoints, supportstransform(Matrix).
Every shape also implements format so you can print one with {f}:
std.debug.print("point = {f}\n", .{zsvg.Point{ .x = 1, .y = 2 }});To rotate a shape about the origin and then move it, compose the matrices:
const m = zsvg.Matrix.translation(cx, cy).mul(.rotation(angle));
const moved = shape.transform(m);Color { r, g, b }withrgb,fromHex,add/sub/scale(saturating atu8bounds), andlerp(a, b, t)for interpolationFill { color, opacity = 1 }withinit,solid,hex,solidHexStroke { color, width, opacity = 1, linecap = .butt, linejoin = .round }withinit,solid,hex,solidHex.linecapis aLineCap(.butt/.round/.square) andlinejoinaLineJoin(.miter/.round/.bevel).Style { fill: ?Fill = null, stroke: ?Stroke = null }— the paint configuration passed toaddLine/addCircle/addShape/addPath/addBezier. Both fields default tonull, so you only set what you need:.{ .stroke = ... },.{ .fill = ... }, or both.TextStyle { fill = opaque black, font_size = 16, font_family = null, anchor = .start }— passed toaddText;anchoris aTextAnchor(.start/.middle/.end). Every field has a default, soaddText(text, pos, .{})works.Document— owns an internal buffer. Construct it withDocument.init(allocator, width, height, background)wherebackgroundis an?Color: pass a color to paint a full-canvas backdrop<rect>, ornullto leave the canvas transparent (no backdrop is emitted). It exposes:- Output:
writeTo(writer),toOwnedString(allocator),save(allocator, io, path) - Shapes:
addLine(line, Style),addCircle(circle, Style),addBezier(bezier, Style),addText(text, position, TextStyle),addRaw - Generic:
addShape(shape, Style)— comptime dispatches on a singleLine,Triangle,Quadrilateral, orBezier - Batch:
addPath(shapes, Style)— same comptime dispatch over a slice/array of one of those shape types
- Output:
addText escapes &, <, and > in the supplied string; addRaw emits its
bytes verbatim, so the caller is responsible for well-formed markup there.
The primary API is writer-based (writeTo); toOwnedString and save are
convenience wrappers on top.
zig build test # run all unit tests
zig build examples # build + run all examples
zig build example-shapes # run just the shapes example
zig build example-gradient # run just the gradient example
zig build example-bezier_curves # run just the bezier flower exampleEach example writes its output into zig-out/ (e.g. zig-out/shapes.svg).
examples/basic.zig— two overlapping translucent circles, the smallest end-to-end snippet.examples/basic_shapes.zig— sampler of every primitive plus a row of translucent circles.examples/shapes.zig— line, triangle, quadrilateral, stroked translucent circle, cubic bezier, and text on one canvas.examples/bezier_curves.zig— 12-petal flower built by rotating a single cubic bezier around a center.examples/paths.zig—addPathover an array of triangles plus a standalone bezier wave.examples/gradient.zig— 10×10 grid of circles with colors linearly interpolated between two endpoints, showcasingColorarithmetic and streaming straight to a file withwriteTo.examples/text.zig— text, line, andaddRawfor inline SVG.examples/save.zig— minimalDocument.saveusage.
Distributed under the Apache License, Version 2.0. See LICENSE.
It has been completly vibe coded, but it's a port from this C++ header-only code that I hack on different projects : bg-generation-2d-blob.