librsvg
| Language | Rust, with a C API (usable from C and C++); also a Rust crate |
|---|---|
| License | LGPL-2.1-or-later |
| Repository | gitlab.gnome.org/GNOME/librsvg |
| Documentation | C API reference, development guide |
| Depends on | Cairo, Pango, GLib/GIO, libxml2, FreeType, HarfBuzz |
| Read / write | read (renders to any Cairo surface: PNG, PDF, PS, SVG, screen) / no |
librsvg renders static SVG to a Cairo context. It is the SVG engine of the GNOME desktop and gdk-pixbuf, and ImageMagick, libvips, GIMP and Wikipedia’s thumbnailer use it. It supports most of SVG 1.1 and a growing part of SVG 2, CSS stylesheets, filters and text through Pango. It does not run scripts or animations.
The library was ported from C to Rust but keeps its stable C API (rsvg.h). The rsvg-convert command line tool ships with it and converts SVG to PNG, PDF, PS, EPS and SVG.
Installation#
# Debian / Ubuntu
sudo apt install librsvg2-dev librsvg2-bin # -bin has rsvg-convert
# Fedora
sudo dnf install librsvg2-devel librsvg2-tools
# macOS
brew install librsvg
# Windows (vcpkg)
vcpkg install librsvg
Compile with pkg-config:
cc render.c $(pkg-config --cflags --libs librsvg-2.0 cairo) -o render
With CMake:
find_package(PkgConfig REQUIRED)
pkg_check_modules(RSVG REQUIRED IMPORTED_TARGET librsvg-2.0)
target_link_libraries(myapp PRIVATE PkgConfig::RSVG)
Basic usage: render SVG to PNG#
#include <librsvg/rsvg.h>
#include <cairo.h>
#include <math.h>
#include <stdio.h>
int main(void) {
GError *error = NULL;
RsvgHandle *handle = rsvg_handle_new_from_file("in.svg", &error);
if (!handle) {
fprintf(stderr, "load error: %s\n", error->message);
g_error_free(error);
return 1;
}
gdouble width = 0, height = 0;
if (!rsvg_handle_get_intrinsic_size_in_pixels(handle, &width, &height)) {
width = 512; // no width/height in the file (only a viewBox)
height = 512;
}
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
(int)ceil(width), (int)ceil(height));
cairo_t *cr = cairo_create(surface);
RsvgRectangle viewport = {0, 0, width, height};
if (!rsvg_handle_render_document(handle, cr, &viewport, &error)) {
fprintf(stderr, "render error: %s\n", error->message);
g_error_free(error);
}
cairo_surface_write_to_png(surface, "out.png");
cairo_destroy(cr);
cairo_surface_destroy(surface);
g_object_unref(handle);
return 0;
}
rsvg_handle_render_document scales the SVG to fit the viewport, keeping the aspect ratio (preserveAspectRatio).
How to render SVG at a given width#
Pass a larger viewport; the image is scaled, not blurred:
gdouble w, h;
rsvg_handle_get_intrinsic_size_in_pixels(handle, &w, &h);
int out_w = 1024;
int out_h = (int)ceil(h * out_w / w);
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, out_w, out_h);
cairo_t *cr = cairo_create(surface);
RsvgRectangle viewport = {0, 0, out_w, out_h};
rsvg_handle_render_document(handle, cr, &viewport, NULL);
rsvg_handle_set_dpi(handle, 300) changes how physical units (mm, in, pt) convert to pixels; the default is 96.
How to convert SVG to PDF#
Render to a Cairo PDF surface instead of an image surface. Text and shapes stay vectors:
#include <cairo-pdf.h>
gdouble w, h;
rsvg_handle_get_intrinsic_size_in_pixels(handle, &w, &h);
// PDF units are points (1/72 inch); SVG pixels are 1/96 inch
cairo_surface_t *pdf = cairo_pdf_surface_create("out.pdf", w * 72.0 / 96.0, h * 72.0 / 96.0);
cairo_t *cr = cairo_create(pdf);
cairo_scale(cr, 72.0 / 96.0, 72.0 / 96.0);
RsvgRectangle viewport = {0, 0, w, h};
rsvg_handle_render_document(handle, cr, &viewport, NULL);
cairo_destroy(cr);
cairo_surface_destroy(pdf); // writes the file
How to apply a CSS stylesheet#
rsvg_handle_set_stylesheet (librsvg 2.48+) adds user CSS before rendering, for example to recolor an icon:
const char *css = "path { fill: #d33 !important; }";
if (!rsvg_handle_set_stylesheet(handle, (const guint8 *)css, strlen(css), &error)) {
fprintf(stderr, "css error: %s\n", error->message);
g_clear_error(&error);
}
How to get the bounds of an element and render only it#
RsvgRectangle ink, logical;
if (rsvg_handle_get_geometry_for_element(handle, "#logo", &ink, &logical, &error)) {
printf("ink box: %g,%g %gx%g\n", ink.x, ink.y, ink.width, ink.height);
cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
(int)ceil(ink.width), (int)ceil(ink.height));
cairo_t *cr = cairo_create(s);
RsvgRectangle vp = {0, 0, ink.width, ink.height};
rsvg_handle_render_element(handle, cr, "#logo", &vp, NULL);
cairo_surface_write_to_png(s, "logo.png");
cairo_destroy(cr);
cairo_surface_destroy(s);
}
How to convert from the command line#
rsvg-convert -w 512 in.svg -o out.png # PNG, 512 px wide
rsvg-convert -f pdf -o out.pdf in.svg # vector PDF
rsvg-convert -b white -z 2 in.svg -o out.png # white background, 2x zoom
Tips#
- librsvg refuses files over its size and nesting limits and doesn’t load external resources outside the file’s directory. Only pass
RSVG_HANDLE_FLAG_UNLIMITED(withrsvg_handle_new_from_gfile_sync/rsvg_handle_new_from_stream_sync) for trusted, huge files. - Load from memory with
rsvg_handle_new_from_data, or withrsvg_handle_new_from_stream_syncwhen relative references (<image href="pic.png">) need a base location. .svgzfiles load directly: librsvg detects gzip.- Only versions in the supported list get fixes; older distributions ship old releases with fewer features.