diff options
author | Federico Mena Quintero <federico@gnome.org> | 2022-01-06 17:22:02 -0600 |
---|---|---|
committer | Marge Bot <marge-bot@gnome.org> | 2022-01-07 01:50:34 +0000 |
commit | 94cbc240edbb42e50dedd6539320fbd8b840743b (patch) | |
tree | 5cada41bcc93eca264aadbbe480a60fff451f323 | |
parent | 2f67661c200de951046a8861a94e7c4da0fafac8 (diff) | |
download | librsvg-94cbc240edbb42e50dedd6539320fbd8b840743b.tar.gz |
Convert the individual chapters to Markdown
Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/650>
-rw-r--r-- | doc/migrating.md | 310 | ||||
-rw-r--r-- | doc/overview.md | 77 | ||||
-rw-r--r-- | doc/recommendations.md | 86 |
3 files changed, 473 insertions, 0 deletions
diff --git a/doc/migrating.md b/doc/migrating.md new file mode 100644 index 00000000..0aa02b81 --- /dev/null +++ b/doc/migrating.md @@ -0,0 +1,310 @@ +# Migrating from old APIs + +## Migrating from the deprecated API that does not use viewports {#migrating-from-non-viewport} + +First, some context. Until librsvg version 2.44, the only way to render +an RsvgHandle into a Cairo context was with the functions +`rsvg_handle_render_cairo(handle, cairo_t)` and +`rsvg_handle_render_cairo_sub(handle, cairo_t, id)` --- respectively, to +render the whole document, and to render a single \"layer\" from it. +Both functions assumed that the SVG document was to be rendered at its +\"natural size\", or to the size overriden with +`rsvg_handle_set_size_callback()`. Since the Cairo context can already +have an affine transform applied to it, that transform can further +change the size of the rendered image. + +Librsvg 2.46 introduced the following functions, designed to replace the +`render_cairo` ones: + +* rsvg_handle_render_document() - renders the whole document +* rsvg_handle_render_layer() - renders a single layer +* rsvg_handle_render_element() - renders a single element +* Plus corresponding functions to get the geometries of the document/layer/element. + +All of those functions take a viewport argument. Let\'s see what this +means. But first, some history. + +### Historical note: before librsvg supported viewports + +When librsvg was first written, its API basically consisted of only +functions to load an RsvgHandle, plus `rsvg_handle_get_pixbuf()` to +render it directly to a GdkPixbuf image. Internally the library used +libart (a pre-Cairo 2D rendering library), but did not expose it in the +public API. + +The only way to specify a size at which to render an RsvgHandle was with +`rsvg_handle_set_size_callback()`, and the callback would run at an +unspecified time during *loading*: when just enough of the SVG document +had been loaded to read in the `width/height` attributes of the toplevel +`<svg>` element, the callback would let the application override these +values with its own desired size. + +Some years later, Cairo was introduced, and it started to replace +libart. Unlike libart, which could only render to in-memory RGBA +buffers, Cairo had a notion of \"backends\": it could render to RGBA +buffers, or it could translate its drawing model commands into PDF or +PostScript. In Cairo\'s terms, one creates a `cairo_surface_t` of a +particular kind (in-memory image surface, PDF surface, EPS surface, +etc.), and then a `cairo_t` context for the surface. The context is what +makes the drawing commands available. + +Being able to render SVG documents directly to PDF or PostScript was +clearly attractive, so librsvg\'s API of `rsvg_handle_get_pixbuf()` +would clearly not be enough. It would be better to pass a `cairo_t` for +an already-created surface, and have librsvg issue its drawing commands +to it. Then the application would be in control of the surface type, or +in the case of GTK widgets, they would already get a `cairo_t` passed to +their drawing functions. Librsvg got modified to export a +`rsvg_handle_render_cairo(handle, cairo_t)`, and then it reimplemented the old +`rsvg_handle_get_pixbuf()` in terms of Cairo. + +At this point, librsvg still kept the notion of rendering SVG documents +at their \"natural size\": the `<svg>` element\'s `width` and `height` +attributes converted to pixels (e.g. converting from `width="5cm"` by +using the dots-per-inch value from the RsvgHandle), or if those +attributes don\'t exist, by using the `viewBox` as a pixel size. The +assumption was that if you needed a different size, you could always +start by setting the transformation matrix on your `cairo_t` and then +rendering to that. + +### The problem with not having viewports + +Most applications which use librsvg to render SVG assets for their user +interface generally work in the same way. For example, to take an SVG +icon and render it, they do something like this: + +1. Create an `RsvgHandle` by loading it from the SVG icon data. + +2. Ask the `RsvgHandle` for its dimensions. + +3. Divide the dimensions by the GUI\'s preferred size for icons. + +4. Scale a Cairo context by the result of the previous step. Translate + the Cairo context so the icon will appear at the desired location. + +5. Render the `RsvgHandle` in that Cairo context. + +This is\... too much work. The web world has moved on to using the CSS +box model practically everywhere. To embed an image you specify *where* +and at *what size* you want to place it, and it gets done automatically. +You actually have to do extra work if you want to do non-standard things +like scale an image non-proportionally. + +### The new rendering API that uses viewports + +These are the *deprecated* drawing functions that do not take viewports: + + gboolean rsvg_handle_render_cairo (RsvgHandle *handle, + cairo_t *cr); + + gboolean rsvg_handle_render_cairo_sub (RsvgHandle *handle, + cairo_t *cr, + const char *id); + + +Starting with librsvg 2.46, the following functions are available: + + typedef struct { + double x; + double y; + double width; + double height; + } RsvgRectangle; + + gboolean rsvg_handle_render_document (RsvgHandle *handle, + cairo_t *cr, + const RsvgRectangle *viewport, + GError **error); + + gboolean rsvg_handle_render_layer (RsvgHandle *handle, + cairo_t *cr, + const char *id, + const RsvgRectangle *viewport, + GError **error); + + gboolean rsvg_handle_render_element (RsvgHandle *handle, + cairo_t *cr, + const char *id, + const RsvgRectangle *element_viewport, + GError **error); + + +For brevity we\'ll omit the `rsvg_handle` namespace prefix, and just +talk about the actual function names. You can see that `render_document` +is basically the same as `render_cairo`, but it has an extra `viewport` +argument. The same occurs in `render_layer` versus `render_cairo_sub`. + +In both of those cases --- `render_document` and `render_layer` ---, the +`viewport` argument specifies a rectangle into which the SVG will be +positioned and scaled to fit. Consider something like this: + + RsvgRectangle viewport = { + .x = 10.0, + .y = 20.0, + .width = 640.0, + .height = 480.0, + }; + + rsvg_handle_render_document (handle, cr, &viewport, NULL); + + +This is equivalent to first figuring out the scaling factor to make the +SVG fit proportionally in 640x480 pixels, then translating the `cr` by +(10, 20) pixels, and then calling `rsvg_handle_render_cairo`. If the SVG +has different proportions than the width and height of the rectangle, it +will be rendered and centered to fit the rectangle. + +Even better: the old functions to get an SVG\'s natural dimensions, like +`rsvg_handle_get_dimensions`, returned integers instead of +floating-point numbers, so you could not always get an exact fit. Please +use the new functions that take viewports; they will give you easier and +better results. + +::: note +`rsvg_handle_render_element` is new in librsvg 2.46. It extracts a +single element from the SVG and renders it scaled to the viewport you +specify. It is different from `render_layer` (or the old-style +`render_cairo_sub`) in that those ones act as if they rendered the whole +document\'s area, but they only paint the element you specify. +::: + +### New API for obtaining an SVG\'s dimensions + +Per the previous section, you should seldom need to obtain the \"natural +size\" of an SVG document now that you can render it directly into a +viewport. But if you still need to know what the SVG document specifies +for its own size, you can use the following functions, depending on the +level of detail you require: + + gboolean rsvg_handle_get_intrinsic_size_in_pixels (RsvgHandle *handle, + gdouble *out_width, + gdouble *out_height); + + +This returns an exact width and height in floating-point pixels. *You +should round up to the next integer* if you need to allocate a pixel +buffer big enough, to avoid clipping the last column or row of pixels, +which may be only partially covered. + +`rsvg_handle_get_intrinsic_size_in_pixels` works by resolving the +`width/height` attributes of the toplevel `<svg>` element against the +handle\'s current DPI and the `font-size` that is defined for the +`<svg>` element. + +However, that is only possible if the `width/height` attributes actually +exist and are in physical units. The function will return FALSE if the +SVG has no resolvable units, for example if the `width/height` +attributes are specified in percentages (e.g. `width="50%"`), since the +function has no knowledge of the viewport where you will place the SVG, +or if those attributes are not specified. + +The other way of obtaining an SVG\'s dimensions is to actually query its +\"intrinsic dimensions\", i.e. what is actually specified in the SVG +document: + + 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); + + +This function will tell you precisely if the toplevel `<svg>` has +`width/height` attributes and their values, and also whether it has a +`viewBox` and its value. + +::: note +Remember that SVGs are *scalable*. They are not like raster images which +have an exact size in pixels, and which you must always take into +account to scale them to a convenient size. For SVGs, you can just +render them to a viewport, and avoid working directly with their size +--- which is kind of arbitrary, and all that matters is the document\'s +aspect ratio. +::: + +### SVGs with no intrinsic dimensions nor aspect ratio + +SVG documents that have none of the `width`, `height`, or `viewBox` +attributes are thankfully not very common, but they are hard to deal +with: the software cannot immediately know their natural size or aspect +ratio, so they cannot be easily scaled to fit within a viewport. Librsvg +has to actually measure the extents of every single element in the SVG +document in that case. If you need to do this by hand, use +`rsvg_handle_get_geometry_for_layer`. + +## Migrating to the geometry APIs {#migrating-to-geometry-apis} + +Until librsvg 2.44, the available APIs to query the geometry of a layer +or element were these: + + struct _RsvgPositionData { + int x; + int y; + }; + + gboolean rsvg_handle_get_position_sub (RsvgHandle *handle, + RsvgPositionData *position_data, + const char *id); + + struct _RsvgDimensionData { + int width; + int height; + gdouble em; + gdouble ex; + }; + + gboolean rsvg_handle_get_dimensions_sub (RsvgHandle *handle, + RsvgDimensionData *dimension_data, + const char *id); + + +These functions are inconvenient --- separate calls to get the position +and dimensions ---, and also inexact, since they only return integer +values, while SVG uses floating-point units. + +Since librsvg 2.46, you can use these functions instead: + + typedef struct { + double x; + double y; + double width; + double height; + } RsvgRectangle; + + gboolean rsvg_handle_get_geometry_for_layer (RsvgHandle *handle, + const char *id, + const RsvgRectangle *viewport, + RsvgRectangle *out_ink_rect, + RsvgRectangle *out_logical_rect, + GError **error); + + gboolean rsvg_handle_get_geometry_for_element (RsvgHandle *handle, + const char *id, + RsvgRectangle *out_ink_rect, + RsvgRectangle *out_logical_rect, + GError **error); + + +These functions return exact floating-point values. They also give you +the ink rectangle, or area covered by paint, as well as the logical +rectangle, which is the extents of unstroked paths (i.e. just the +outlines). diff --git a/doc/overview.md b/doc/overview.md new file mode 100644 index 00000000..393ea1ab --- /dev/null +++ b/doc/overview.md @@ -0,0 +1,77 @@ +# Overview of Librsvg's API + +Librsvg is a library for rendering Scalable Vector Graphics files (SVG). +Specifically, it can take non-animated, non-scripted SVG documents and +render them into a [Cairo](https://www.cairographics.org/) surface. +Normally this means an in-memory raster surface, but it could also be +any of the other surface types that Cairo supports. + +Librsvg supports many of the graphic features in the [SVG +1.1](https://www.w3.org/TR/SVG/) and [SVG2](https://www.w3.org/TR/SVG2/) +specifications. The main features of SVG that librsvg does not support +are the following: + +* Scripting or animation: Librsvg reads SVG data and renders it to a + static image. There is no provision to execute scripts that may + control animation parameters. + +* Access to the DOM: Librsvg creates an internal representation of + the SVG data, but it does not provide outside access to the + resulting Document Object Model (DOM). + +* SVG fonts: Instead, librsvg relies on the system's fonts, + particularly those that are available through Cairo/Pango. + +Librsvg's API is divided into two main parts: one for loading SVG data +and one for rendering it. In the *loading stage*, you create an +`RsvgHandle` object from SVG data, which can come from a file or from a +stream of bytes. In the *rendering stage*, you take an `RsvgHandle` and +ask it to render itself to a Cairo context. + +## Loading + +`RsvgHandle` is an object that represents SVG data in memory. Your program +creates an `RsvgHandle` from an SVG file, or from a memory buffer that +contains SVG data, or in the most general form, from a GIO stream that +will provide SVG data. At this stage you can get either I/O errors or +parsing errors. If loading completes successfully, the `RsvgHandle` will +be ready for rendering. + +Generally you should use `rsvg_handle_new_from_gfile_sync()` or +`rsvg_handle_new_from_stream_sync()` to load an SVG document into an +`RsvgHandle`. There are other convenience functions to load an SVG +document, but these two functions let one set the "base file" and the +`RsvgHandleFlags` in a single call. + +## Rendering + +Once you have an SVG image loaded into an `RsvgHandle`, you can render it +to a Cairo context any number of times, or to different Cairo contexts, +as needed. As a convenience, you can pick a single element in the SVG by +its `id` attribute and render only that element; this is so that +sub-elements can be extracted conveniently out of a larger SVG. + +Generally you should use `rsvg_handle_render_document()` to render the +whole SVG document at any size you choose into a Cairo context. + +## Example: simple loading and rendering + +The following program loads `hello.svg`, renders it scaled to fit within +640x480 pixels, and writes a `hello.png` file. + +Note the following: + +* `rsvg_handle_render_document()` will scale the document + proportionally to fit the viewport you specify, and it will center + the image within that viewport. + +* Librsvg does not paint a background color by default, so in the + following example all unfilled areas of the SVG will appear as fully + transparent. If you wish to have a specific background, fill the + viewport area yourself before rendering the SVG. + +``` +FIXME: include load-and-render.c here +``` + + diff --git a/doc/recommendations.md b/doc/recommendations.md new file mode 100644 index 00000000..89d68ae6 --- /dev/null +++ b/doc/recommendations.md @@ -0,0 +1,86 @@ +# Recommendations for Applications + +## How sizing works in SVG {#sizing} + +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 +100x50 pixels, but it could also be rendered scaled at 200x100 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. + +## Recommendations for applications with SVG assets {#recommendations-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 `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 +`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 +`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). |