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 ``` 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 ``` 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 ``` 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 ``. 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 ``` 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 ``` 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).