After the librsvg team finished the rustification of
librsvg's main library, I wanted to start porting the high-level test
suite to Rust. This is mainly to be able to run tests in parallel,
cargo test does automatically in order to reduce test times.
However, this meant that librsvg needed a Rust API that would exercise
the same code paths as the C entry points.
At the same time, I wanted the Rust API to make it impossible to
misuse the library. From the viewpoint of the C API, an
has different stages:
- Just initialized
- Loaded, or in an error state after a failed load
- Ready to render
To ensure consistency, the public API checks that you cannot render an
RsvgHandle that is not completely loaded yet, or one that resulted
in a loading error. But wouldn't it be nice if it were impossible to
call the API functions in the wrong order?
This is exactly what the Rust API does. There is a
to which you give a filename or a stream, and it will return a
SvgHandle or an error. Then, you can only create a
CairoRenderer if you have an
For historical reasons, the C API in librsvg is not perfectly
consistent. For example, some functions which return an error will
actually return a proper
GError, but some others will just
gboolean with no further explanation of what went wrong.
In contrast, all the Rust API functions that can fail will actually
Result, and the error case will have a meaningful
error value. In the Rust API, there is no "wrong order" in which the
various API functions and methods can be called; it tries to do the
whole "make invalid states unrepresentable".
To implement the Rust API, I had to do some refactoring of the internals that hook to the public entry points. This made me realize that librsvg could be a lot easier to use. The C API has always forced you to call it in this fashion:
- Ask the SVG for its dimensions, or how big it is.
- Based on that, scale your Cairo context to the size you actually want.
- Render the SVG to that context's current transformation matrix.
But first, (1) gives you inadequate information because
rsvg_handle_get_dimensions() returns a
int fields for the width and
height. The API is similar to gdk-pixbuf's in that it always wants to
think in whole pixels. However, an SVG is not necessarily
Then, (2) forces you to calculate some geometry in almost all cases, as most apps want to render SVG content scaled proportionally to a certain size. This is not hard to do, but it's an inconvenience.
Let's look at (1) again. The question, "how big is the SVG" is a bit meaningless when we consider that SVGs can be scaled to any size; that's the whole point of them!
When you ask
RsvgHandle how big it is, in reality it should look at
you and whisper in your ear, "how big do you want it to be?".
And that's the thing. The HTML/CSS/SVG model is that one embeds content into viewports of a given size. The software is responsible for scaling the content to fit into that viewport.
In the end, what we want is a rendering function that takes a Cairo context and a Rectangle for a viewport, and that's it. The function should take care of fitting the SVG's contents within that viewport.
There is now an open bug about exactly this sort of API. In the end, programs should just have to load their SVG handle, and directly ask it to render at whatever size they need, instead of doing the size computations by hand.
When will this be available?
I'm in the middle of a rather large refactor to make this viewport concept really work. So far this involves:
Defining APIs that take a viewport.
Refactoring all the geometry computation to support the semantics of the C API, plus the new
Fixing the code that kept track of an internal offset for all temporary images.
Refactoring all the code that mucks around with the Cairo context's affine transformation matrix, which is a big mutable mess.
Tests, examples, documentation.
I want to make the Rust API available for the 2.46 release, which is hopefully not too far off. It should be ready for the next GNOME release. In the meantime, you can check out the open bugs for the 2.46.0 milestone. Help is appreciated; the deadline for the first 3.33 tarballs is approximately one month from now!