Recommendations for Applications
Recommendations for Applications
Let’s consider two common cases for rendering SVG documents:
-
Your application uses fixed-size assets, for example, “all icons at 16×16 pixels”.
-
Your application needs to accept arbitrarily-sized SVG documents, to either render them at a fixed size, or to render them at a “natural” size.
In either case, librsvg assumes that for rendering you have already obtained a Cairo surface, and a Cairo context to draw on the surface. For the case of fixed-size assets, this is easy; you create a surface of the size you know you want, and tell librsvg to render to it at that exact size. For the case of wanting to use a “natural” size, you first have to ask librsvg about the document’s size so you can create an appropriately-sized surface. Let’s see how to do both cases.
Rendering SVG assets at a fixed size
This is the case when you have a known WIDTH
and HEIGHT
, and you
want to tell librsvg to render an SVG at that size:
GError *error = NULL;
GFile *file = g_file_new_for_path ("hello.svg");
RsvgHandle *handle = rsvg_handle_new_from_gfile_sync (file, RSVG_HANDLE_FLAGS_NONE, NULL, &error); /* 1 */
if (!handle)
{
g_error ("could not load: %s", error->message);
}
cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT); /* 2 */
cairo_t *cr = cairo_create (surface);
/* Render the handle scaled proportionally into that whole surface */
RsvgRectangle viewport = { /* 3 */
.x = 0.0,
.y = 0.0,
.width = WIDTH,
.height = HEIGHT,
};
if (!rsvg_handle_render_document (handle, cr, &viewport, &error)) /* 4 */
{
g_error ("could not render: %s", error->message);
}
/* The surface is now rendered */
-
Load the SVG document.
-
Create an image surface of the size you want.
-
Declare a viewport of that size. If you want a non-zero
(x, y)
offset you can set it right there. -
Render the document within that viewport. Done!
This will scale the SVG document proportionally to make it fit in
WIDTH×HEIGHT
pixels.
Picking a "natural" size for SVG documents
In some cases, your application may not want to use a predefined size, but instead query the SVG for a “natural” size at which to render. Some SVG documents make this easy, and some don’t.
SVG documents with intrinsic dimensions in absolute units
Consider an SVG document that starts like this:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
This is completely unambiguous; the SVG says that its intrinsic size is 200×100 pixels. It can be scaled arbitrarily, but of course most of the time you’ll want to scale it proportionally at that 2:1 ratio.
Here is a slightly more complicated case:
<svg xmlns="http://www.w3.org/2000/svg" width="10cm" height="5cm">
The SVG says that its intrinsic size is 10cm × 5cm. This is not hard to convert to pixels if you know the Dots-Per-Inch (DPI) at which you want to render things, but it is a small inconvenience.
And here is a more complicated case still:
<svg xmlns="http://www.w3.org/2000/svg" width="10em" height="5em">
<style>
* { font-size: 2cm; }
</style>
This means that the width is 10 times the font size of 2cm, and the height is 5 times the font size.
In general an application cannot figure this out easily, since it
would need a CSS parser and cascading engine to even be able to know
what the font size is for the toplevel <svg>
. Fortunately, librsvg
already does that!
In all those cases, the width and height are in physical units (px,
cm, mm, etc.), or font-based units (em, ex) that can be resolved to
pixels. You can use rsvg_handle_get_intrinsic_size_in_pixels()
to do the conversion easily if all you want to do is to create a
surface with the “natural” number of pixels:
gboolean rsvg_handle_get_intrinsic_size_in_pixels (RsvgHandle *handle,
gdouble *out_width,
gdouble *out_height);
However, the documentation for that
function indicates
that it may return FALSE
in some cases. Let’s see what those ugly
cases are.
SVG documents with just proportions
What if we have this:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100">
This document has no intrinsic dimensions, but it clearly states that
its proportions are 200:100, or 2:1. It will look good when scaled
to a rectangle that is twice as wide as it is tall. Our old friend
rsvg_handle_render_document()
will still scale the SVG
proportionally to fit the viewport size you pass in. But what if you
must pick a size yourself, instead of having a predefined viewport?
In that case you bite the bullet, call
rsvg_handle_get_intrinsic_dimensions()
and make your own choice
about what to do with the proportions that come in the viewBox
:
typedef enum {
RSVG_UNIT_PERCENT,
RSVG_UNIT_PX,
RSVG_UNIT_EM,
RSVG_UNIT_EX,
RSVG_UNIT_IN,
RSVG_UNIT_CM,
RSVG_UNIT_MM,
RSVG_UNIT_PT,
RSVG_UNIT_PC
} RsvgUnit;
typedef struct {
double length;
RsvgUnit unit;
} RsvgLength;
void rsvg_handle_get_intrinsic_dimensions (RsvgHandle *handle,
gboolean *out_has_width,
RsvgLength *out_width,
gboolean *out_has_height,
RsvgLength *out_height,
gboolean *out_has_viewbox,
RsvgRectangle *out_viewbox);
You’ll cleverly note that I have not answered your question. You have
an SVG with only a viewBox
, and you want to pick a reasonable size
to render it.
And here is where I want to say, SVG documents are scalable. Pick a size, any size for a viewport! Here are some suggestions:
-
The size of your window’s visible area.
-
The size of your device’s screen.
-
The size of your sheet of paper, minus the margins.
Take that size, pass it as the viewport size to
rsvg_handle_render_document()
, and be done with it.
SVG documents without any usable sizing information at all
This is pretty nasty, but possibly not useless for doing special effects in web browsers:
<svg xmlns="http://www.w3.org/2000/svg">
That’s right, no width
, no height
, no viewBox
. There is no easy
way to figure out a suitable size for this. You have two options:
-
Shrug your shoulders, and
rsvg_handle_render_document()
with a comfortable viewport size like in the last section. -
Do a best-effort job of actually computing the geometries of all the elements in the document. You can use
rsvg_handle_get_geometry_for_element()
by passingNULL
for the target element’sid
; this will measure all the elements in the document. This is not expensive for typical SVGs, but it is not “almost instantaneous” like just asking for intrinsic dimensions would be.
If this is starting to sound too complicated, please remember that
SVG documents are scalable. That’s their whole reason for being!
Pick a size for a viewport, and ask librsvg to render the document
within that viewport with rsvg_handle_render_document()
.
Recommendations for applications with SVG assets
Before librsvg 2.46, applications would normally load an SVG asset, then they would query librsvg for the SVG’s size, and then they would compute the dimensions of their user interface based on the SVG’s size.
With librsvg 2.46 and later, applications may have an easier time by
letting the UI choose whatever size it wants, or by hardcoding a size
for SVG assets, and then asking librsvg to render SVG assets at that
particular size. Applications can use rsvg_handle_render_document()
,
which takes a destination viewport, to do this in a single step.
To extract individual elements from an SVG document and render them in
arbitrary locations — for example, to extract a single icon from a
document full of icons —, applications can use
rsvg_handle_render_element()
.
Injecting a user stylesheet
It is sometimes convenient for applications to inject an extra
stylesheet while rendering an SVG document. You can do this with
rsvg_handle_set_stylesheet()
. During the CSS cascade, the specified
stylesheet will be used with a “User”
origin.