# NanoSVG: SVG parsed to cubic bezier paths in a single C header

> NanoSVG is a Zlib-licensed single-header SVG parser for C and C++ that converts SVG shapes to lists of cubic bezier control points. Its README opens with a disclaimer that the project is not actively maintained, and the rasterizer component handles only flat filled shapes.

**memononen/nanosvg** — Simple stupid SVG parser

- Repository: https://github.com/memononen/nanosvg
- Stars: 2,027 · Forks: 419
- Language: C
- License: Zlib
- Published: 2026-10-11 · Updated: 2026-10-11 · Language: en
- Canonical page: https://hysenlabs.com/projects/memononen-nanosvg

## SVG shapes converted to cubic bezier control points, not pixels

NanoSVG parses SVG files and produces a list of cubic bezier shapes, not rendered pixels. Each SVG path element becomes a linked list of NSVGpath structures, and each path holds an array of control points laid out as consecutive groups of 8 floats representing 4 bezier control points. Turning that data into pixels is the application's job, not the parser's.

This design matches two use cases the README names: rendering scalable icons in an editor application, and prototyping games. Both use cases need the geometry of an SVG, not a pixel buffer, and they typically want to feed that geometry into their own rendering pipeline.

Shapes in the SVG are transformed by the viewBox and converted to the specified units, so the output coordinates match what was designed in the original application. That transformation step means a shape designed at 100x100 in an SVG editor arrives in the correct proportions regardless of the SVG's internal coordinate system. The README notes that a wide range of SVG features is supported, with the caveat that something may be missing.

## Single-header setup with NANOSVG_IMPLEMENTATION in one translation unit

Integration requires copying nanosvg.h into the project, then defining NANOSVG_IMPLEMENTATION in exactly one C or C++ file before including the header. That file also needs stdio.h, string.h and math.h included before it:

```c
#include <stdio.h>
#include <string.h>
#include <math.h>
#define NANOSVG_IMPLEMENTATION	// Expands implementation
#include "nanosvg.h"
```

By default, NanoSVG parses only the most common SVG colors. For the full list of SVG color keywords, define NANOSVG_ALL_COLOR_KEYWORDS before the implementation define:

```c
#include <stdio.h>
#include <string.h>
#include <math.h>
#define NANOSVG_ALL_COLOR_KEYWORDS	// Include full list of color keywords.
#define NANOSVG_IMPLEMENTATION		// Expands implementation
#include "nanosvg.h"
```

Alternatively, CMake integration is available. After installing the library with CMake, find_package brings it into a target:

```cmake
add_executable(myexe main.c)
find_package(NanoSVG REQUIRED)
target_link_libraries(myexe NanoSVG::nanosvg NanoSVG::nanosvgrast)
```

NanoSVG::nanosvgrast links the rasterizer component alongside the parser. Both are available through the same CMake package.

## Parsing a file and walking shapes, paths and bezier control points

Loading an SVG and reading its bezier geometry follows a three-step pattern the README illustrates: parse the file, iterate the shape and path linked lists, draw the curves, then free the memory. Passing "px" and 96 as the unit and DPI arguments is the README's own recommendation for cases where the output unit does not matter:

```c
struct NSVGimage* image;
image = nsvgParseFromFile("test.svg", "px", 96);
printf("size: %f x %f\n", image->width, image->height);
for (shape = image->shapes; shape != NULL; shape = shape->next) {
	for (path = shape->paths; path != NULL; path = path->next) {
		for (i = 0; i < path->npts-1; i += 3) {
			float* p = &path->pts[i*2];
			drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]);
		}
	}
}
nsvgDelete(image);
```

Each call to drawCubicBez receives 8 float values: the start point, 2 control points and the end point of one cubic bezier segment. Path iteration increments by 3 because each segment shares its start point with the previous segment's end point. Calling nsvgDelete at the end frees the parsed image structure; skipping it leaks memory.

## Units from 'px' to 'mm', DPI and the color keyword switch

NanoSVG accepts 6 unit strings for the output coordinate space: 'px', 'pt', 'pc', 'mm', 'cm', and 'in'. The README gives a practical example for each direction: pixel units for screen rendering, millimeters for feeding data into a CNC cutter. DPI controls the conversion factor between screen and physical units, with 96 as the default for display work.

For an application where the unit choice is irrelevant, the README recommends 'px' and 96 directly. The unit affects only the coordinate values in the output, not the shape topology. Shapes parsed with different unit values produce the same bezier count with proportionally different coordinates.

Color support has two modes controlled at compile time. Without NANOSVG_ALL_COLOR_KEYWORDS, only the most common SVG colors are parsed. Defining that macro before the implementation expands the full SVG color keyword table from the W3C specification, which the README links directly.

## Rasterizer: flat filled shapes only, and when that is not enough

NanoSVG ships a separate rasterizer alongside the parser. According to the README, it renders only flat filled shapes. Gradients, strokes and filter effects are outside what the rasterizer handles. A typical use case, as the README describes it, is baking icons of different sizes into a texture atlas.

The README states plainly that the rasterizer is 'not particular fast or accurate, but it's small and packed in one header file.' This is not a disclaimer to ignore: for any production path that needs pixel-accurate SVG output at scale, the rasterizer's stated limitations are real constraints. A project that needs gradient fills, stroked paths or SVG filter primitives must either write a custom renderer on top of the bezier output from the parser, or choose a library designed to handle the full SVG specification.

For game icons and editor toolbar sprites that are simple, flat, filled shapes, the rasterizer is a match. The distinction between the parser and rasterizer components matters at integration time: NanoSVG::nanosvg (CMake) links the parser, and NanoSVG::nanosvgrast adds the rasterizer.

## Not actively maintained, but AI_POLICY.md is in the repository

NanoSVG's README opens with a statement in italics: 'This project is not actively maintained.' That is the first line a contributor or adopter reads, and it sets a specific expectation. No maintainer has committed to reviewing pull requests on any schedule, so patches may wait indefinitely.

One detail that stands out against that disclaimer is the presence of AI_POLICY.md at the repository's top level. This file type, defining project policy around AI-generated contributions, is a recent convention. Its appearance in a repository whose maintenance disclaimer predates it by some time is a concrete sign that at least one update occurred after the project was described as not actively maintained.

The last push to the repository was on 2026-07-09. NanoSVG is distributed under the Zlib licence; the text is in LICENSE.txt at the repository root.

## Building the demo with GLFW and premake4, and what the examples show

An example project ships under the example/ directory. Compiling it requires GLFW and premake4. The README gives platform-specific premake4 invocations: `premake4 xcode4` on macOS, `premake4 vs2010` on Windows, and `premake4 gmake` on Linux. An OS X build sequence the README gives runs as:

```bash
$ premake4 gmake
$ cd build/
$ make
$ ./example
```

The example directory contains two C source files (example1.c, example2.c) and several SVG files: 23.svg, drawing.svg, nano.svg and the tiger SVG that appears in the rasterizer screenshot. Running the example with GLFW renders those SVG files using the parser's bezier output, which shows the parser against real SVG content rather than trivial test shapes.

The CMakeLists.txt at the repository root covers installation of the library itself. Building the demo is a separate step and is not part of the CMake install target. Anyone integrating NanoSVG into a project and not planning to modify the example code does not need GLFW or premake4.

## Conclusion

NanoSVG is the right choice for a C or C++ project that needs SVG paths as cubic bezier data and values a near-zero dependency count. The rasterizer is explicitly described as not particularly fast or accurate, making it unsuitable for production rendering beyond flat icon baking. Read the README's opening disclaimer before committing; it states the project is not actively maintained. For SVG content that uses gradients, strokes or filters, NanoSVG's rasterizer cannot handle those, and the parser output alone requires a custom renderer. CMake integration via find_package(NanoSVG REQUIRED) is the cleanest path for build system adoption.

## FAQ

### What does NanoSVG output?

NanoSVG converts SVG shapes into a list of cubic bezier control points. Each SVG path becomes a linked list of NSVGpath structures, and each path holds float arrays of bezier control point coordinates. Rendering those points to pixels is left to the application.

### How do I include NanoSVG in a C project?

Copy nanosvg.h into the project, include stdio.h, string.h and math.h, then define NANOSVG_IMPLEMENTATION in exactly one file before including nanosvg.h. Alternatively, install via CMake and use find_package(NanoSVG REQUIRED).

### Is NanoSVG still maintained?

The README's first line states: 'This project is not actively maintained.' The last push to the repository was on 2026-07-09.

### Can NanoSVG handle SVG gradients?

The rasterizer component renders only flat filled shapes. Gradients, strokes and filter effects are outside its scope. The parser outputs bezier control points for all shapes regardless, but rendering non-flat content requires a custom renderer.

### What units does NanoSVG support?

NanoSVG accepts 'px', 'pt', 'pc', 'mm', 'cm', or 'in' as the output unit argument. DPI controls the conversion between screen and physical units; the README gives 96 as the default for display work.

## Sources

- [Issues](https://github.com/memononen/nanosvg/issues)
- [License: Zlib](https://github.com/memononen/nanosvg/blob/master/LICENSE)
- [memononen/nanosvg on GitHub](https://github.com/memononen/nanosvg)
- [README](https://github.com/memononen/nanosvg/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/memononen-nanosvg
