summaryrefslogtreecommitdiff
path: root/doc/recommendations.md
blob: b1f97634317a4c41fb18881356c645e9ff3a6743 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
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).