LunaSVG
| Language | C++ (C++17) |
|---|---|
| License | MIT |
| Repository | github.com/sammycage/lunasvg |
| Documentation | README and comments in lunasvg.h |
| Depends on | plutovg (bundled as a git submodule) |
| Read / write | read (renders to RGBA bitmaps, writes PNG) / no |
LunaSVG parses SVG and renders it with plutovg, a small 2D vector rasterizer by the same author. It supports nearly all static features of SVG 1.1 and SVG Tiny 1.2: shapes, paths, gradients, patterns, clipping, masks, markers, <use>, <style> CSS and text. It doesn’t support filters, animation or scripts. Dear ImGui, RmlUi, OpenSiv3D and node-canvas use it.
Beyond rendering it has a small DOM: find elements by id or CSS selector, read and set attributes, get bounding boxes and find the element under a point.
Installation#
Build and install with CMake (the submodule is fetched with --recursive):
git clone --recursive https://github.com/sammycage/lunasvg.git
cd lunasvg
cmake -B build .
cmake --build build
cmake --install build
It’s also in vcpkg (vcpkg install lunasvg) and Conan. Use it from CMake:
find_package(lunasvg REQUIRED)
target_link_libraries(myapp PRIVATE lunasvg::lunasvg)
Or without installing, with FetchContent:
include(FetchContent)
FetchContent_Declare(lunasvg GIT_REPOSITORY https://github.com/sammycage/lunasvg.git GIT_TAG master)
FetchContent_MakeAvailable(lunasvg)
target_link_libraries(myapp PRIVATE lunasvg::lunasvg)
Basic usage: convert SVG to PNG#
#include <lunasvg.h>
#include <iostream>
using namespace lunasvg;
int main() {
auto document = Document::loadFromFile("in.svg");
if (!document) {
std::cerr << "cannot load in.svg\n";
return 1;
}
std::cout << document->width() << " x " << document->height() << "\n";
Bitmap bitmap = document->renderToBitmap(); // intrinsic size, transparent background
if (bitmap.isNull()) {
std::cerr << "render failed\n";
return 1;
}
bitmap.writeToPng("out.png");
return 0;
}
How to render SVG at a given size or with a background#
renderToBitmap(width, height, backgroundColor): pass -1 for one dimension to keep the aspect ratio. The color is 0xRRGGBBAA:
Bitmap icon = document->renderToBitmap(256, -1); // 256 px wide
Bitmap opaque = document->renderToBitmap(512, 512, 0xFFFFFFFF); // white background
opaque.writeToPng("icon-512.png");
How to get raw RGBA pixels#
Bitmaps hold premultiplied ARGB32 (the Cairo layout). Convert in place before passing them to code that expects plain RGBA:
Bitmap bitmap = document->renderToBitmap(128, 128);
bitmap.convertToRGBA();
const uint8_t* pixels = bitmap.data(); // R, G, B, A per pixel
int stride = bitmap.stride(); // bytes per row
// upload to a texture, etc.
To draw into your own buffer, wrap it with Bitmap(data, width, height, stride) and call document->render(bitmap, Matrix::scaled(2, 2)).
How to recolor an SVG with CSS#
auto document = Document::loadFromFile("icon.svg");
document->applyStyleSheet("path { fill: #d33; }");
document->renderToBitmap(64, 64).writeToPng("icon-red.png");
How to change attributes and find elements#
auto document = Document::loadFromFile("in.svg");
if (Element logo = document->getElementById("logo")) {
logo.setAttribute("fill", "green");
Box box = logo.getBoundingBox();
std::cout << "logo at " << box.x << "," << box.y << " size " << box.w << "x" << box.h << "\n";
}
for (Element circle : document->querySelectorAll("circle")) {
circle.setAttribute("stroke", "black");
}
if (Element hit = document->elementFromPoint(30, 30)) {
std::cout << "clicked: " << hit.getAttribute("id") << "\n";
}
document->renderToBitmap().writeToPng("modified.png");
Tips#
- Text needs fonts: register them with
lunasvg_add_font_face_from_file("Inter", false, false, "Inter-Regular.ttf")before rendering. Document::loadFromDataparses SVG from a string in memory.- The repository’s
svg2pngexample is a ready command line converter:svg2png input.svg 512x512 0xffffffff.