SVG for C++
Loading...
Searching...
No Matches
SVG for C++

SVG for C++
See the C++ code for this logo
pink logo green logo blue logo red logo

SVG for C++

C++ codecov

Purpose

This a header-only library for generating SVG files from a simple C++ interface. It can also perform non-trivial tasks such as calculating a bounding box for an SVG's elements, or merging several graphics together.

Want to see more? Read the documentation.

Basic Usage

#include "svg.hpp"
#include <fstream>
int main() {
SVG::SVG root;
// Basic CSS support
auto black = SVG::Color::hex("#000000");
root.style("circle").set_attr("fill", black);
root.style("rect#my_rectangle").set_attr("fill", "red");
// Add elements with add_child<>()
auto shapes = root.add_child<SVG::Group>();
auto rect = shapes->add_child<SVG::Rect>("my_rectangle");
-100, -100, 100,
SVG::Attrs{{ "stroke", black }});
100, 100, 100,
SVG::Attrs{{ "stroke", black }});
// Reference elements by id, tag, class name, etc...
root.get_element_by_id("my_rectangle")
->set_attr("x", 20).set_attr("y", 20)
.set_attr("width", 40).set_attr("height", 40);
std::cout << "There are " << root.get_children<SVG::Circle>().size() <<
" circles." << std::endl;
// Automatically scale width and height to fit elements
root.autoscale();
// Output our drawing
std::ofstream outfile("my_drawing.svg");
outfile << std::string(root);
}
Definition svg.hpp:3174
static Color hex(std::string value)
Definition svg.hpp:189
T * add_child(Args &&... args)
Definition svg.hpp:1715
std::vector< T * > get_children()
Definition svg.hpp:1732
Definition svg.hpp:3103
Definition svg.hpp:3152
Definition svg.hpp:2615
AttributeMap & style(const std::string &key)
Definition svg.hpp:2685
Definition svg.hpp:129

autoscale() measures element geometry, including simple SVG rotate(...) transforms, visible element stroke-width attributes, local <use> references, and approximate Text bounds from x, y, dx, dy, font-size, text-anchor, and baseline attributes. It sets width, height, and viewBox; use responsive_autoscale() when the SVG should calculate only viewBox and let its container control display size. It does not inspect CSS transforms, rendered effects such as filters or shadows, stroke joins, font metrics, CSS class rules, media queries, or external stylesheets; put autoscale-critical stroke widths on elements or pass SVG::Margins when the drawing needs extra page space for those effects.

Nested SVG::SVG elements are supported as normal SVG viewports. By default, autoscale() first autoscales nested SVG children, then measures their viewport boxes in the parent coordinate system. Use AutoscaleOptions to preserve existing nested viewport sizes.

SVG::AutoscaleOptions options(SVG::NO_MARGINS, false);
root.autoscale(options);
Definition svg.hpp:137

Output

<svg height="421.0" viewBox="-210.5 -210.5 421.0 421.0" width="421.0" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
<![CDATA[
circle {
fill: #000000;
}
rect#my_rectangle {
fill: red;
}
]]>
</style>
<g>
<rect height="40" id="my_rectangle" width="40" x="20" y="20" />
<circle cx="-100.0" cy="-100.0" r="100.0" stroke="#000000" />
<circle cx="100.0" cy="100.0" r="100.0" stroke="#000000" />
</g>
</svg>

Higher-Level Workflows

Typed CSS Variables and Classes

Use set_vars() and SVG::Classes when a drawing has reusable theme tokens or repeated class names. Variable names are normalized with a leading --, class selectors can be built without hand-writing ".", and vars.format() is useful for CSS expressions that combine several variables.

enum class PlotVar { axis, background, text_size };
enum class PlotClass { axis_line, label, muted };
SVG::SVG root;
auto vars = root.set_vars<PlotVar>({
{ PlotVar::axis, "plot-axis", "#374151" },
{ PlotVar::background, "--plot-background", "#ffffff" },
{ PlotVar::text_size, "--plot-text-size", "12px" }
});
{ PlotClass::axis_line, "axis-line" },
{ PlotClass::label, ".axis-label" },
{ PlotClass::muted, "muted" }
});
root.style(classes.selector(PlotClass::axis_line))
.set_attr("stroke", vars.var(PlotVar::axis));
root.style(classes.selector(PlotClass::label, PlotClass::muted))
.set_attr("font-size", vars.var(PlotVar::text_size))
.set_attr("opacity", "0.72");
root.add_child<SVG::Line>(
0, 0, 100, 0,
SVG::Attrs{{ "class", classes.classes(PlotClass::axis_line) }});
Definition svg.hpp:1574
Definition svg.hpp:3116
Variables< T > set_vars(std::initializer_list< VariableSpec< T > > specs)
Definition svg.hpp:2706

Class Lists and Queries

Attribute names are stored and looked up case-sensitively, matching SVG/XML behavior. Use the exact SVG spelling for mixed-case names such as viewBox; viewbox is a separate attribute and will not be used by SVG viewers as a substitute.

class_list() treats the class attribute as a normalized token list, so repeated whitespace and duplicate class tokens are cleaned up. Use get_elements_by_class() when you want token-aware matches instead of substring checks.

auto* group = root.add_child<SVG::Group>();
auto* first = group->add_child<SVG::Circle>();
auto* second = group->add_child<SVG::Rect>();
first->class_list().add("chart").add("selected");
second->set_attr("class", " chart-muted ");
for (auto* element : root.get_elements_by_class("chart")) {
element->set_attr("opacity", "0.9");
}
ClassList & add(const std::string &token)
Definition svg.hpp:817

Depth-First Iteration

Range-for over any SVG element visits that element and its descendants in depth-first document order. Use descendants() when you want to skip the current element. The iterator also exposes the accumulated supported SVG transform for each visited element through it.transform().

for (auto* element : root) {
if (element->has_attr("data-debug")) {
element->set_attr("stroke", "#ff00aa");
}
}
auto pass = root.descendants();
for (auto it = pass.begin(); it != pass.end(); ++it) {
SVG::Element* element = *it;
const auto origin = it.transform().apply({ 0, 0 });
if (element->has_attr("data-debug")) {
element->set_attr("data-origin-x", origin.first);
element->set_attr("data-origin-y", origin.second);
element->set_attr("stroke", "#ff00aa");
}
}
Abstract base class for all SVG elements.
Definition svg.hpp:1631
DepthFirstRange descendants()
Definition svg.hpp:2286

For type-specific searches, get_children<T>() returns matching descendants and get_immediate_children<T>() limits the lookup to direct children.

for (auto* circle : root.get_children<SVG::Circle>()) {
circle->set_attr("r", 8);
}
Main namespace for SVG for C++.
Definition svg.hpp:60

Gradients

Use defs() helpers to define paint servers without manually constructing XML-style gradient elements. The helpers reuse an existing definition when the id is already present.

auto& gradient = root.defs()->linear_gradient("gym-mtb")
.horizontal()
.solid_segments({ "#2563eb", "#f97316" });
rect->set_attr("fill", gradient.url());
auto& radial = root.defs()->radial_gradient("spot")
.center("50%", "50%")
.radius("50%")
.stop("0%", "#ffffff")
.stop("100%", "#000000");
circle->set_attr("fill", radial.url());
RadialGradient & radial_gradient(std::string id)
Definition svg.hpp:2968
LinearGradient & linear_gradient(std::string id)
Definition svg.hpp:2961
Defs * defs()
Definition svg.hpp:2734

Responsive ViewBoxes

Use autoscale() when the SVG should set its own display width, height, and viewBox. Use responsive_autoscale() when you want only the viewBox updated, leaving sizing to CSS or the embedding page.

root.set_attr("width", "100%").set_attr("height", "auto");
root.responsive_autoscale({ 8, 8, 8, 8 });
void responsive_autoscale(const Margins &margins=DEFAULT_MARGINS)
Definition svg.hpp:3810

When an element uses SVG features the library cannot measure well enough, provide an explicit layout bounding box for autoscale without changing the element's own get_bbox() result.

auto* label = root.add_child<SVG::Text>(0, 0, user_supplied_label);
label->layout_bbox({ -4, 96, -18, 6 });
root.autoscale();
Element & layout_bbox(const BoundingBox &bbox)
Definition svg.hpp:3413
Definition svg.hpp:3054

Text layout bounds are estimates, not exact font shaping or browser measurements. The built-in estimator is UTF-8 aware, treats combining marks as zero-width, and uses conservative advances for ASCII, CJK/full-width text, emoji, and bold text. If an element only needs a little extra layout space, use bbox_padding(); an explicit layout_bbox() still takes precedence.

title->bbox_padding({ 2, 2, 4, 4 });

Use snap_to() to position measured elements against each other with SVG transforms. Passing only RelativeAlignment uses Anchor::Center, so offsets do not require spelling out the center anchor. Combine the two enums with | when you need start or end alignment along the shared edge. Use align_to() when elements should share an axis without becoming neighbors.

legend->layout_bbox({ 0, 120, 0, 28 });
legend->snap_to(plot_area, SVG::RelativeAlignment::Right, { 12, 0 });
title->layout_bbox({ 0, 240, 0, 24 });
title->snap_to(plot_area, SVG::RelativeAlignment::Top | SVG::Anchor::Start, { 0, -8 });
callout->layout_bbox({ 0, 80, 0, 18 });
callout->align_to(plot_area, SVG::Axis::Y, SVG::Anchor::Center, { 16, 0 });

Use rotate_about_bbox() when a label or annotation should rotate around a measured corner or center while still using a normal SVG transform attribute that autoscale can measure.

label->transform().rotate_about_bbox(-45, SVG::Anchor::End, SVG::Anchor::End);

Simple Animations

This package supports creating basic animations via CSS keyframes via the frame_animate() function.