Donner's root @donner library uses the tiny-skia renderer by default. A separate Bazel module can use a version from the Bazel Central Registry once that version is published, or build against a checked-out source tree. The checked-in consumer example exercises the latter path and is also used by BCR preflight against the release candidate.
For the current v0.8 source tree, put this in your MODULE.bazel:
Use standard rules_cc targets in BUILD.bazel:
Set C++20 in your .bazelrc, as the checked-in consumer does:
Build against an adjacent checkout with a module override (replace the path with your Donner checkout):
Once a BCR version is published, set the dependency to the version listed in the registry and run bazel build //:my_app without the override. Donner is built and tested against the Bazel version pinned in its .bazelversion (currently 8.8.1).
CMake support is generated from Donner's Bazel build metadata. Start from a Donner source checkout and generate the CMake files once before configuring your application:
Then add Donner to your own CMakeLists.txt with add_subdirectory() and link the exported donner target:
Use the same public headers as the Bazel build:
The complete runnable example is under examples/cmake_consumer/. It parses a small SVG, queries the DOM, renders it, and exits non-zero if any step fails. From the Donner checkout:
From another source tree, pass the Donner checkout explicitly:
Regenerate the CMake files after updating Donner or changing its Bazel build graph. Generated CMakeLists.txt files are intentionally ignored by Git; handwritten examples under examples/ are the exception.
First include the core SVG module, donner/svg/SVG.h. Then use SVGParser to load an SVG from a string, which may have been read from a file:
ParseResult contains either the document or an error, which can be checked with hasError() and error():
Then get the SVGDocument and start using it. For example, to get the SVGElement for the <path>:
The document tree can be traversed and modified in memory:
For multi-threaded DOM access and removed-element lifetime behavior, see SVG DOM Threading And Lifetime.
The complete example prints the path geometry, applies style edits, and then prints the computed style.
Include donner/svg/renderer/Renderer.h and use the Renderer class, which resolves to the backend selected at build time. Drawing renders the document in memory, and save() writes it to a PNG file:
Pixel data can also be read back with a snapshot:
The quickstart example parses a document, edits it, and renders it in one short program. See Building Donner for details on choosing between tiny_skia (the compact CPU default) and Geode (the GPU backend).
The default parser recognizes the declarative SVG animation elements, <animate>, <set>, <animateTransform>, <animateMotion> and <mpath>, with no extra option or build flag. See Animation Elements for the supported timing and interpolation features.
Donner has no animation clock of its own. The host sets the document time in seconds with SVGDocument::setTime(), typically from its frame clock, and draws again. Each draw() samples every animation at the document's current time; nothing else needs to be called between frames. Any finite time is accepted, so a host can step forward, seek backward or render frames out of order:
Pointer input stays with the host. To start begin="id.click" and similar event-based timing, hit test the pointer, then deliver the event to the element under it. The triggered interval starts at the current document time plus any offset in the trigger, such as click+1s, and the call returns false when no animation timing changed:
SVGDocument::dispatchAnimationEvent() delivers other named events, and SVGDocument::beginAnimation() and SVGDocument::endAnimation() schedule an animation element's begin or end at an offset from the current time. Donner has no "animation finished" callback: the host chooses how long to play, and SVGDocument::animationEvents() lists the begin, repeat and end boundaries in a time interval so the host can react to them without sampling every frame:
SVGDocument::restartAnimations() returns the document to time zero and discards event-started intervals, for example when playback starts over:
The document's processing mode, set with SVGDocument::Settings::processingMode when parsing, limits animation for untrusted content:
| Mode | Authored animation | Host events and begin/end scheduling |
|---|---|---|
| DynamicInteractive (default) | Runs | Accepted |
| SecureAnimated | Runs | Rejected |
| SecureStatic | Never applies | Rejected |
SVG documents loaded through <image> are parsed in SecureStatic mode, so they render their static content, unless the host replaces SVGDocument::Settings::svgParseCallback with its own parser callback.
The complete program, examples/svg_animation.cc, checks that the click starts an animation and that the lifecycle boundaries match the document; run it with bazel run //examples:svg_animation. The editor's Preview mode plays animations with this same API, and donner-svg --gif exports an animation as an animated GIF (see donner-svg CLI Tool).
Donner bundles several third-party libraries (EnTT, stb, tiny-skia-cpp, zlib, libpng, FreeType, HarfBuzz, woff2, brotli). Most are under permissive licenses that require you to reproduce their copyright notice and license text when redistributing binaries built from Donner. Donner ships a donner_notice_file rule that aggregates every required license into a single NOTICE.txt that you can embed in your application.
Pick the variant that matches your build configuration:
| Variant | Bazel target |
|---|---|
| Default (tiny-skia) | @donner//third_party/licenses:notice_default |
| tiny-skia + --config=text-full | @donner//third_party/licenses:notice_text_full |
To see the exact text your users will receive, build the variant target and print its output. When Donner is a dependency of your project the files land under bazel-bin/external/donner+/third_party/licenses/:
Inside the Donner repository itself, drop the external/donner+ prefix:
Each variant produces two files next to each other:
The donner_notice_file rule exposes its text file (notice_default.txt for notice_default) under output_group = "notice"; the .json manifest is not needed at runtime. Select the text with a filegroup and pass it through Donner's //tools:embed_resources helper to produce a linkable C++ symbol. With donner_cc_binary loaded from @donner//build_defs:rules.bzl, the recipe is:
includes = ["."] lets sources include the generated header as embedded/notice_embedded.h from any package. Donner builds this recipe as //examples:third_party_notice.
The generated .cpp filename is the input filename with non-alphanumeric characters replaced by underscores, so notice_default.txt becomes notice_default_txt.cpp.
embed_resources generates a header that exposes each resource as a std::span<const unsigned char> in the donner::embedded namespace. After #include "embedded/notice_embedded.h", the application can show the text behind an --about flag or a menu item:
Alternatively, declare the notice target as a data dependency of your binary and read the file at runtime through Bazel runfiles.
The variant lists live in third_party/licenses/BUILD.bazel and are kept in sync with Donner's //examples:svg_to_png dependency graph. When you change build configuration (for example, enabling --config=text-full), update your consumer BUILD files to reference the matching notice_* target. The build report enumerates every third-party dep per variant alongside its SPDX identifier and upstream link.