diff options
-rw-r--r-- | doc/recommendations.md | 274 |
1 files changed, 215 insertions, 59 deletions
diff --git a/doc/recommendations.md b/doc/recommendations.md index 247bc718..b1f97634 100644 --- a/doc/recommendations.md +++ b/doc/recommendations.md @@ -2,65 +2,221 @@ Title: Recommendations for Applications # Recommendations for Applications -## How sizing works in SVG - -SVG documents are *scalable*. The conventional way to position SVG -documents, which comes from the web platform, is to consider a -*viewport* in which to place the SVG document — that is, a rectangular -region to where the SVG will be scaled and positioned. - -SVG renderers are supposed to use the viewport provided by the -application, plus the SVG document's `width`, `height`, and `viewBox` -attributes, to compute the position and size for the rendered document. - -Ideally, the toplevel `<svg>` element of an SVG document will contain -`width` and `height` attributes, that indicate the proportions and -"natural size" of the document. When those attributes are present, the -SVG renderer can unambiguously figure out the natural aspect ratio of -the document, and can also suggest a natural size for the document. For -example, `<svg width="100px" height="50px">` has a natural size of -100×50 pixels, but it could also be rendered scaled at 200×100 pixels. -Since SVGs are scalable, it is not mandatory to actually use its natural -size; it can be scaled arbitrarily. Of course, it is up to each -application how an SVG document will be scaled: a web browser would want -to consider the semantics of embedding images in HTML, which may be -different from a GUI toolkit loading SVG assets with hard-coded sizes. - -If an SVG document's toplevel `<svg>` element does not have `width` and -`height` attributes, then the SVG renderer can try to figure out the -document's aspect ratio from the `viewBox` attribute. For example, -there is no natural size for `<svg viewBox="0 0 100 50">`, but it has a -2:1 (or 100:50) aspect ratio, so it is twice as wide as it is tall. - -If there is no `viewBox` either, then the SVG renderer cannot easily -figure out the natural size of the document. It can either set a 1:1 -scaling matrix within the application's viewport and render the SVG -there, or it can actually try to compute the size of each object in the -SVG document to figure out the size. The latter is a moderately -expensive operation, and can be avoided by having the SVG document -specify `width` and `height` attributes. Try not to have SVG documents -that just start with `<svg>` without any of those attributes. - -### How librsvg computes document sizes - -Librsvg looks for the `width` and `height` attributes in the toplevel -`<svg>` element. If they are present, librsvg uses them for the -"natural" size of the SVG, and this also defines the aspect ratio. The -size has actual units (pixels, centimeters, etc.) depending on the value -of the `width` and `height` attributes. - -If there are no `width` or `height` attributes in the toplevel `<svg>`, -librsvg looks for the `viewBox` attribute. If present, this defines the -aspect ratio and a "natural" size in pixels. - -In both cases above (with `width`/`height` and/or `viewBox`), librsvg -can determine the "natural" size and aspect ratio of an SVG document -immediately after loading. - -Otherwise, if none of those attributes are present in the toplevel -`<svg>` element, librsvg must actually compute the coverage of all the -graphical elements in the SVG. This is a moderately expensive operation, -and depends on the complexity of the document. +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 |