NanoSVG
| Language | C (usable from C++) |
|---|---|
| License | zlib license |
| Repository | github.com/memononen/nanosvg |
| Documentation | README and comments in nanosvg.h |
| Depends on | nothing (C standard library) |
| Read / write | read (parse to paths, rasterize flat fills) / no |
NanoSVG is 2 header files: nanosvg.h parses SVG into a list of shapes, each a list of paths made of cubic Bézier segments, already transformed and converted to the units you ask for (px, mm, in, …). nanosvgrast.h is an optional rasterizer that renders those shapes to an RGBA buffer.
It handles paths, basic shapes, transforms, solid colors and simple linear and radial gradients. It doesn’t support text, CSS stylesheets, clipping, masks, filters, patterns or <use>. The README states the project is not actively maintained, but it remains widely embedded (Blender, many game engines, plotters and CNC tools) because it’s tiny and has no dependencies. Use it to get geometry or bake simple icons; use librsvg or LunaSVG for general SVG files.
Installation#
Copy src/nanosvg.h (and src/nanosvgrast.h for rasterizing) into your project. In exactly one .c or .cpp file define the implementation macros before including:
#include <stdio.h>
#include <string.h>
#include <math.h>
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
It’s also in vcpkg (vcpkg install nanosvg) and has a CMake package (find_package(NanoSVG), targets NanoSVG::nanosvg and NanoSVG::nanosvgrast).
Basic usage: read shapes and paths#
#include <stdio.h>
#include <string.h>
#include <math.h>
#define NANOSVG_IMPLEMENTATION
#include "nanosvg.h"
int main(void) {
NSVGimage *image = nsvgParseFromFile("in.svg", "px", 96.0f);
if (!image) {
fprintf(stderr, "cannot parse in.svg\n");
return 1;
}
printf("size: %.1f x %.1f\n", image->width, image->height);
for (NSVGshape *shape = image->shapes; shape; shape = shape->next) {
if (shape->fill.type == NSVG_PAINT_COLOR)
printf("shape '%s' fill 0x%08x\n", shape->id, shape->fill.color); // ABGR
for (NSVGpath *path = shape->paths; path; path = path->next) {
// pts holds x0,y0 then 3 points (6 floats) per cubic segment
for (int i = 0; i < path->npts - 1; i += 3) {
float *p = &path->pts[i * 2];
printf(" cubic (%g,%g) (%g,%g) (%g,%g) (%g,%g)\n",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
}
}
}
nsvgDelete(image);
return 0;
}
How to rasterize an SVG to RGBA#
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvgrast.h"
NSVGimage *image = nsvgParseFromFile("icon.svg", "px", 96.0f);
float scale = 64.0f / image->width; // render 64 px wide
int w = (int)(image->width * scale);
int h = (int)(image->height * scale);
unsigned char *rgba = malloc((size_t)w * h * 4);
NSVGrasterizer *rast = nsvgCreateRasterizer();
nsvgRasterize(rast, image, 0, 0, scale, rgba, w, h, w * 4);
// rgba: non-premultiplied RGBA; save with stb_image_write, upload to a texture, ...
nsvgDeleteRasterizer(rast);
nsvgDelete(image);
free(rgba);
How to get coordinates in millimeters#
Pass the unit and DPI: shapes come back converted, which is handy for plotters and laser cutters:
NSVGimage *image = nsvgParseFromFile("cut.svg", "mm", 96.0f);
for (NSVGshape *s = image->shapes; s; s = s->next)
printf("bounds mm: %g %g %g %g\n", s->bounds[0], s->bounds[1], s->bounds[2], s->bounds[3]);
How to parse SVG from memory#
nsvgParse modifies the string, so pass a writable copy:
char *copy = strdup(svg_text);
NSVGimage *image = nsvgParse(copy, "px", 96.0f);
free(copy);
Tips#
- Colors are packed as
0xAABBGGRR(red in the low byte). - Define
NANOSVG_ALL_COLOR_KEYWORDSbefore the implementation to support all 147 named SVG colors; by default only the common ones are known. - Check
shape->flags & NSVG_FLAGS_VISIBLEto skip hidden shapes.