[librsvg: 4/5] (#852): Rewrite the Recommendations chapter to explain what to do in all cases




commit afe37fcdfe4b9a1f079318b6d686f44e31de7322
Author: Federico Mena Quintero <federico gnome org>
Date:   Tue Mar 15 15:51:34 2022 -0600

    (#852): Rewrite the Recommendations chapter to explain what to do in all cases
    
    Fixes https://gitlab.gnome.org/GNOME/librsvg/-/issues/852
    
    Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/681>

 doc/recommendations.md | 274 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 215 insertions(+), 59 deletions(-)
---
diff --git a/doc/recommendations.md b/doc/recommendations.md
index 247bc7185..b1f976343 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
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]