<?xml version="1.0" encoding="UTF-8" ?>
<chapter>
  <title>Migrating from old APIs</title>

  <section id="migrating-from-non-viewport">
    <title>Migrating from the deprecated API that does not use viewports</title>

    <para>
      First, some context.  Until librsvg version 2.44, the only way
      to render an RsvgHandle into a Cairo context was with the
      functions <function>rsvg_handle_render_cairo(handle, cairo_t)</function> and
      <function>rsvg_handle_render_cairo_sub(handle, cairo_t, id)</function> —
      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
      <function>rsvg_handle_set_size_callback()</function>.  Since the
      Cairo context can already have an affine transform applied to
      it, that transform can further change the size of the rendered
      image.
    </para>

    <para>
      Librsvg 2.46 introduced the following functions, designed to replace the <function>render_cairo</function> ones:
    </para>

    <itemizedlist>
      <listitem><function>rsvg_handle_render_document()</function> - renders the whole document</listitem>
      <listitem><function>rsvg_handle_render_layer()</function> - renders a single layer</listitem>
      <listitem><function>rsvg_handle_render_element()</function> - renders a single element</listitem>
      <listitem>Plus corresponding functions to get the geometries of the document/layer/element.</listitem>
    </itemizedlist>

    <para>
      All of those functions take a <firstterm>viewport</firstterm>
      argument.  Let's see what this means.  But first, some history.
    </para>

    <section>
      <title>Historical note: before librsvg supported viewports</title>

      <para>
        When librsvg was first written, its API basically consisted of
        only functions to load an RsvgHandle, plus
        <function>rsvg_handle_get_pixbuf()</function> 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.
      </para>

      <para>
        The only way to specify a size at which to render an
        RsvgHandle was with
        <function>rsvg_handle_set_size_callback()</function>, and the
        callback would run at an unspecified time during
        <emphasis>loading</emphasis>: when just enough of the SVG
        document had been loaded to read in the
        <literal>width/height</literal> attributes of the toplevel
        <literal>&lt;svg&gt;</literal> element, the callback would let
        the application override these values with its own desired
        size.
      </para>

      <para>
        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 <type>cairo_surface_t</type> of a
        particular kind (in-memory image surface, PDF surface, EPS
        surface, etc.), and then a <type>cairo_t</type> context for
        the surface.  The context is what makes the drawing commands
        available.
      </para>

      <para>
        Being able to render SVG documents directly to PDF or
        PostScript was clearly attractive, so librsvg's API of
        <function>rsvg_handle_get_pixbuf()</function> would clearly
        not be enough.  It would be better to pass a
        <type>cairo_t</type> 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
        <type>cairo_t</type> passed to their drawing functions.
        Librsvg got modified to export a
        <function>rsvg_handle_render_cairo(handle,
        cairo_t)</function>, and then it reimplemented the old
        <function>rsvg_handle_get_pixbuf()</function> in terms of
        Cairo.
      </para>

      <para>
        At this point, librsvg still kept the notion of rendering SVG
        documents at their "natural size": the
        <literal>&lt;svg&gt;</literal> element's
        <literal>width</literal> and <literal>height</literal>
        attributes converted to pixels (e.g. converting from
        <literal>width="5cm"</literal> by using the dots-per-inch
        value from the RsvgHandle), or if those attributes don't
        exist, by using the <literal>viewBox</literal> 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 <type>cairo_t</type> and then rendering to that.
      </para>
    </section>

    <section>
      <title>The problem with not having viewports</title>

      <para>
        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:
      </para>

      <orderedlist>
        <listitem>
          <para>
            Create an <type>RsvgHandle</type> by loading it from the SVG icon data.
          </para>
        </listitem>

        <listitem>
          <para>
            Ask the <type>RsvgHandle</type> for its dimensions.
          </para>
        </listitem>

        <listitem>
          <para>
            Divide the dimensions by the GUI's preferred size for icons.
          </para>
        </listitem>

        <listitem>
          <para>
            Scale a Cairo context by the result of the previous step.
            Translate the Cairo context so the icon will appear at the
            desired location.
          </para>
        </listitem>

        <listitem>
          <para>
            Render the <type>RsvgHandle</type> in that Cairo context.
          </para>
        </listitem>
      </orderedlist>

      <para>
        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 <emphasis>where</emphasis> and at <emphasis>what
        size</emphasis> 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.
      </para>
    </section>

    <section>
      <title>The new rendering API that uses viewports</title>

      <para>
        These are the <emphasis>deprecated</emphasis> drawing functions that do not take viewports:
      </para>

      <programlisting lang="c">
gboolean rsvg_handle_render_cairo     (RsvgHandle *handle,
                                       cairo_t    *cr);

gboolean rsvg_handle_render_cairo_sub (RsvgHandle *handle,
                                       cairo_t    *cr,
                                       const char *id);
      </programlisting>

      <para>
        Starting with librsvg 2.46, the following functions are available:
      </para>

      <programlisting lang="c">
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);
      </programlisting>

      <para>
        For brevity we'll omit the <literal>rsvg_handle</literal>
        namespace prefix, and just talk about the actual function
        names.  You can see that <function>render_document</function>
        is basically the same as <function>render_cairo</function>,
        but it has an extra <parameter>viewport</parameter> argument.
        The same occurs in <function>render_layer</function> versus
        <function>render_cairo_sub</function>.
      </para>

      <para>
        In both of those cases — <function>render_document</function>
        and <function>render_layer</function> —, the
        <parameter>viewport</parameter> argument specifies a rectangle
        into which the SVG will be positioned and scaled to fit.
        Consider something like this:
      </para>

      <programlisting lang="c">
RsvgRectangle viewport = {
    .x = 10.0,
    .y = 20.0,
    .width = 640.0,
    .height = 480.0,
};

rsvg_handle_render_document (handle, cr, &amp;viewport, NULL);
      </programlisting>

      <para>
        This is equivalent to first figuring out the scaling factor to
        make the SVG fit proportionally in 640x480 pixels, then
        translating the <varname>cr</varname> by (10, 20) pixels, and
        then calling <function>rsvg_handle_render_cairo</function>.
        If the SVG has different proportions than the width and height
        of the rectangle, it will be rendered and centered to fit the
        rectangle.
      </para>

      <para>
        Even better: the old functions to get an SVG's natural
        dimensions, like
        <function>rsvg_handle_get_dimensions</function>, 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.
      </para>

      <note>
        <para>
          <function>rsvg_handle_render_element</function> 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 <function>render_layer</function> (or the
          old-style <function>render_cairo_sub</function>) in that
          those ones act as if they rendered the whole document's
          area, but they only paint the element you specify.
        </para>
      </note>
    </section>

    <section>
      <title>New API for obtaining an SVG's dimensions</title>

      <para>
        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:
      </para>

      <programlisting lang="c">
gboolean rsvg_handle_get_intrinsic_size_in_pixels (RsvgHandle *handle,
                                                   gdouble    *out_width,
                                                   gdouble    *out_height);
      </programlisting>

      <para>
        This returns an exact width and height in floating-point
        pixels.  <emphasis>You should round up to the next integer</emphasis> 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.
      </para>

      <para>
        <function>rsvg_handle_get_intrinsic_size_in_pixels</function>
        works by resolving the <literal>width/height</literal>
        attributes of the toplevel <literal>&lt;svg&gt;</literal>
        element against the handle's current DPI and the
        <literal>font-size</literal> that is defined for the
        <literal>&lt;svg&gt;</literal> element.
      </para>

      <para>
        However, that is only possible if the
        <literal>width/height</literal> attributes actually exist and
        are in physical units.  The function will return FALSE if the
        SVG has no resolvable units, for example if the
        <literal>width/height</literal> attributes are specified in
        percentages (e.g. <literal>width="50%"</literal>), since the
        function has no knowledge of the viewport where you will place
        the SVG, or if those attributes are not specified.
      </para>

      <para>
        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:
      </para>
      
      <programlisting lang="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);
      </programlisting>

      <para>
        This function will tell you precisely if the toplevel
        <literal>&lt;svg&gt;</literal> has
        <literal>width/height</literal> attributes and their values,
        and also whether it has a <literal>viewBox</literal> and its
        value.
      </para>

      <note>
        <para>
          Remember that SVGs are <emphasis>scalable</emphasis>.  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.
        </para>
      </note>
    </section>

    <section>
      <title>SVGs with no intrinsic dimensions nor aspect ratio</title>

      <para>
        SVG documents that have none of the <literal>width</literal>,
        <literal>height</literal>, or <literal>viewBox</literal>
        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
        <function>rsvg_handle_get_geometry_for_layer</function>.
      </para>
    </section>
  </section>

  <section id="migrating-to-geometry-apis">
    <title>Migrating to the geometry APIs</title>

    <para>
      Until librsvg 2.44, the available APIs to query the geometry of a layer or element were these:
    </para>

    <programlisting lang="c">
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);
    </programlisting>

    <para>
      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.
    </para>

    <para>
      Since librsvg 2.46, you can use these functions instead:
    </para>

    <programlisting lang="c">
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);
    </programlisting>

    <para>
      These functions return exact floating-point values.  They also
      give you the <firstterm>ink rectangle</firstterm>, or area
      covered by paint, as well as the <firstterm>logical
      rectangle</firstterm>, which is the extents of unstroked paths
      (i.e. just the outlines).
    </para>
  </section>
</chapter>
