Files
librsvg/doc/recommendations.md

244 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
Title: 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:
```c
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 */
```
1. Load the SVG document.
2. Create an image surface of the size you want.
3. Declare a viewport of that size. If you want a non-zero `(x, y)` offset you
can set it right there.
4. 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:
```xml
<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:
```xml
<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:
```xml
<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 [method@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:
```c
gboolean rsvg_handle_get_intrinsic_size_in_pixels (RsvgHandle *handle,
gdouble *out_width,
gdouble *out_height);
```
However, the documentation for [that
function](method.Handle.get_intrinsic_size_in_pixels.html) 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:
```xml
<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
[method@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
[method@Rsvg.Handle.get_intrinsic_dimensions] and make your own choice
about what to do with the proportions that come in the `viewBox`:
```c
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
[method@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:
```xml
<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 [method@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 [method@Rsvg.Handle.get_geometry_for_element] by
passing `NULL` for the target element's `id`; 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 [method@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 [method@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
[method@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
[method@Rsvg.Handle.set_stylesheet]. During the CSS cascade, the specified
stylesheet will be used with a ["User"
origin](https://drafts.csswg.org/css-cascade-3/#cascading-origins).