diff --git a/rsvg-base-file-util.c b/rsvg-base-file-util.c
index bf89a14..efef2df 100644
--- a/rsvg-base-file-util.c
+++ b/rsvg-base-file-util.c
@@ -88,11 +88,22 @@ rsvg_handle_new_from_file (const gchar * file_name, GError ** error)
     char *data;
     gsize data_len;
     RsvgHandle *handle = NULL;
+    GFile *file;
 
     rsvg_return_val_if_fail (file_name != NULL, NULL, error);
 
-    base_uri = rsvg_get_base_uri_from_filename (file_name);
-    data = _rsvg_io_acquire_data (file_name, base_uri, NULL, &data_len, NULL, error);
+    file = g_file_new_for_path (file_name);
+    base_uri = g_file_get_uri (file);
+    if (!base_uri) {
+        g_set_error (error,
+                     G_IO_ERROR,
+                     G_IO_ERROR_FAILED,
+                     _("Cannot obtain URI from '%s'"), file_name);
+        g_object_unref (file);
+        return NULL;
+    }
+
+    data = _rsvg_io_acquire_data (base_uri, base_uri, NULL, &data_len, NULL, error);
 
     if (data) {
         handle = rsvg_handle_new ();
@@ -105,6 +116,7 @@ rsvg_handle_new_from_file (const gchar * file_name, GError ** error)
     }
 
     g_free (base_uri);
+    g_object_unref (file);
 
     return handle;
 }
diff --git a/rsvg-base.c b/rsvg-base.c
index b0b7feb..b34b143 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -187,7 +187,20 @@ rsvg_start_style (RsvgHandle * ctx, RsvgPropertyBag *atts)
     handler->ctx = ctx;
 
     handler->style = g_string_new (NULL);
-    handler->is_text_css = type && g_ascii_strcasecmp (type, "text/css") == 0;
+
+    /* FIXME: See these:
+     *
+     * https://www.w3.org/TR/SVG/styling.html#StyleElementTypeAttribute
+     * https://www.w3.org/TR/SVG/styling.html#ContentStyleTypeAttribute
+     *
+     * If the "type" attribute is not present, we should fallback to the
+     * "contentStyleType" attribute of the svg element, which in turn
+     * defaults to "text/css".
+     *
+     * See where is_text_css is used to see where we parse the contents
+     * of the style element.
+     */
+    handler->is_text_css = (type == NULL) || (g_ascii_strcasecmp (type, "text/css") == 0);
 
     handler->parent = (RsvgSaxHandlerDefs *) ctx->priv->handler;
     ctx->priv->handler = &handler->super;
@@ -309,9 +322,11 @@ rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag
     else if (!strcmp (name, "tref"))
         newnode = rsvg_new_tref ();
     else {
-		/* hack for bug 401115. whenever we encounter a node we don't understand, push it into a group. 
-		   this will allow us to handle things like conditionals properly. */
-		newnode = rsvg_new_group ();
+        /* Whenever we encounter a node we don't understand, represent it as a defs.
+         * This is like a group, but it doesn't do any rendering of children.  The
+         * effect is that we will ignore all children of unknown elements.
+         */
+		newnode = rsvg_new_defs ();
 	}
 
     if (newnode) {
@@ -617,7 +632,6 @@ rsvg_start_xinclude (RsvgHandle * ctx, RsvgPropertyBag * atts)
         /* xml */
         GInputStream *stream;
         GError *err = NULL;
-        xmlDocPtr xml_doc;
         xmlParserCtxtPtr xml_parser;
         xmlParserInputBufferPtr buffer;
         xmlParserInputPtr input;
@@ -637,16 +651,14 @@ rsvg_start_xinclude (RsvgHandle * ctx, RsvgPropertyBag * atts)
         if (xmlPushInput (xml_parser, input) < 0) {
             g_clear_error (&err);
             xmlFreeInputStream (input);
-            xmlFreeParserCtxt (xml_parser);
+
+            xml_parser = rsvg_free_xml_parser_and_doc (xml_parser);
             goto fallback;
         }
 
         (void) xmlParseDocument (xml_parser);
 
-        xml_doc = xml_parser->myDoc;
-        xmlFreeParserCtxt (xml_parser);
-        if (xml_doc)
-            xmlFreeDoc (xml_doc);
+        xml_parser = rsvg_free_xml_parser_and_doc (xml_parser);
 
         g_clear_error (&err);
     }
@@ -1023,14 +1035,13 @@ rsvg_path_is_uri (char const *path)
     return (p[0] == ':' && p[1] == '/' && p[2] == '/');
 }
 
-gchar *
+static gchar *
 rsvg_get_base_uri_from_filename (const gchar * filename)
 {
     gchar *current_dir;
     gchar *absolute_filename;
     gchar *base_uri;
 
-
     if (g_path_is_absolute (filename))
         return g_filename_to_uri (filename, NULL, NULL);
 
@@ -1202,29 +1213,21 @@ rsvg_handle_close_impl (RsvgHandle * handle, GError ** error)
 {
     GError *real_error = NULL;
 
-	handle->priv->is_closed = TRUE;
-
     handle->priv->error = &real_error;
 
     if (handle->priv->ctxt != NULL) {
-        xmlDocPtr xml_doc;
         int result;
 
-        xml_doc = handle->priv->ctxt->myDoc;
-
         result = xmlParseChunk (handle->priv->ctxt, "", 0, TRUE);
         if (result != 0) {
             rsvg_set_error (error, handle->priv->ctxt);
-            xmlFreeParserCtxt (handle->priv->ctxt);
-            xmlFreeDoc (xml_doc);
+            handle->priv->ctxt = rsvg_free_xml_parser_and_doc (handle->priv->ctxt);
             return FALSE;
         }
 
-        xmlFreeParserCtxt (handle->priv->ctxt);
-        xmlFreeDoc (xml_doc);
+        handle->priv->ctxt = rsvg_free_xml_parser_and_doc (handle->priv->ctxt);
     }
 
-    handle->priv->finished = TRUE;
     handle->priv->error = NULL;
 
     if (real_error != NULL) {
@@ -1722,6 +1725,34 @@ rsvg_handle_set_size_callback (RsvgHandle * handle,
     handle->priv->user_data_destroy = user_data_destroy;
 }
 
+#define GZ_MAGIC_0 ((guchar) 0x1f)
+#define GZ_MAGIC_1 ((guchar) 0x8b)
+
+/* Creates handle->priv->compressed_input_stream and adds the gzip header data
+ * to it.  We implicitly consume the header data from the caller in
+ * rsvg_handle_write(); that's why we add it back here.
+ */
+static void
+create_compressed_input_stream (RsvgHandle *handle)
+{
+    RsvgHandlePrivate *priv = handle->priv;
+
+    static const guchar gz_magic[2] = { GZ_MAGIC_0, GZ_MAGIC_1 };
+
+#if GLIB_CHECK_VERSION (2, 24, 0)
+    g_assert (priv->compressed_input_stream == NULL);
+
+    priv->compressed_input_stream = g_memory_input_stream_new ();
+    g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (priv->compressed_input_stream),
+                                    gz_magic, 2, NULL);
+#else
+    g_assert (priv->gzipped_data == NULL);
+
+    priv->gzipped_data = GSF_OUTPUT (gsf_output_memory_new ());
+    gsf_output_write (handle->priv->gzipped_data, 2, gz_magic);
+#endif
+}
+
 /**
  * rsvg_handle_write:
  * @handle: an #RsvgHandle
@@ -1745,34 +1776,59 @@ rsvg_handle_write (RsvgHandle * handle, const guchar * buf, gsize count, GError
     rsvg_return_val_if_fail (handle, FALSE, error);
     priv = handle->priv;
 
-    rsvg_return_val_if_fail (!priv->is_closed, FALSE, error);
+    rsvg_return_val_if_fail (priv->state == RSVG_HANDLE_STATE_START
+                             || priv->state == RSVG_HANDLE_STATE_EXPECTING_GZ_1
+                             || priv->state == RSVG_HANDLE_STATE_READING_COMPRESSED
+                             || priv->state == RSVG_HANDLE_STATE_READING,
+                             FALSE,
+                             error);
+
+    while (count > 0) {
+        switch (priv->state) {
+        case RSVG_HANDLE_STATE_START:
+            if (buf[0] == GZ_MAGIC_0) {
+                priv->state = RSVG_HANDLE_STATE_EXPECTING_GZ_1;
+                buf++;
+                count--;
+            } else {
+                priv->state = RSVG_HANDLE_STATE_READING;
+                return rsvg_handle_write_impl (handle, buf, count, error);
+            }
 
-    if (priv->first_write) {
-        priv->first_write = FALSE;
+            break;
+
+        case RSVG_HANDLE_STATE_EXPECTING_GZ_1:
+            if (buf[0] == GZ_MAGIC_1) {
+                priv->state = RSVG_HANDLE_STATE_READING_COMPRESSED;
+                create_compressed_input_stream (handle);
+                buf++;
+                count--;
+            } else {
+                priv->state = RSVG_HANDLE_STATE_READING;
+                return rsvg_handle_write_impl (handle, buf, count, error);
+            }
 
-        /* test for GZ marker. todo: store the first 2 bytes in the odd circumstance that someone calls
-         * write() in 1 byte increments */
-        if ((count >= 2) && (buf[0] == (guchar) 0x1f) && (buf[1] == (guchar) 0x8b)) {
+            break;
+
+        case RSVG_HANDLE_STATE_READING_COMPRESSED:
 #if GLIB_CHECK_VERSION (2, 24, 0)
-            priv->data_input_stream = g_memory_input_stream_new ();
+            g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (priv->compressed_input_stream),
+                                            g_memdup (buf, count), count, (GDestroyNotify) g_free);
 #else
-            priv->gzipped_data = GSF_OUTPUT (gsf_output_memory_new ());
+            gsf_output_write (handle->priv->gzipped_data, count, buf);
 #endif
-        }
-    }
+            return TRUE;
+            
 
-#if GLIB_CHECK_VERSION (2, 24, 0)
-    if (priv->data_input_stream) {
-        g_memory_input_stream_add_data ((GMemoryInputStream *) priv->data_input_stream,
-                                        g_memdup (buf, count), count, (GDestroyNotify) g_free);
-        return TRUE;
+        case RSVG_HANDLE_STATE_READING:
+            return rsvg_handle_write_impl (handle, buf, count, error);
+
+        default:
+            g_assert_not_reached ();
+        }
     }
-#else
-    if (priv->gzipped_data)
-        return gsf_output_write (handle->priv->gzipped_data, count, buf);
-#endif
 
-    return rsvg_handle_write_impl (handle, buf, count, error);
+    return TRUE;
 }
 
 /**
@@ -1790,25 +1846,33 @@ gboolean
 rsvg_handle_close (RsvgHandle * handle, GError ** error)
 {
     RsvgHandlePrivate *priv;
+    gboolean result;
 
     rsvg_return_val_if_fail (handle, FALSE, error);
     priv = handle->priv;
 
-    if (priv->is_closed)
-          return TRUE;
+    if (priv->state == RSVG_HANDLE_STATE_CLOSED_OK
+        || priv->state == RSVG_HANDLE_STATE_CLOSED_ERROR) {
+        /* closing is idempotent */
+        return TRUE;
+    }
 
+    if (priv->state == RSVG_HANDLE_STATE_READING_COMPRESSED) {
 #if GLIB_CHECK_VERSION (2, 24, 0)
-    if (priv->data_input_stream) {
         gboolean ret;
 
-        ret = rsvg_handle_read_stream_sync (handle, priv->data_input_stream, NULL, error);
-        g_object_unref (priv->data_input_stream);
-        priv->data_input_stream = NULL;
+        /* FIXME: when using rsvg_handle_write()/rsvg_handle_close(), as opposed to using the
+         * stream functions, for compressed SVGs we buffer the whole compressed file in memory
+         * and *then* uncompress/parse it here.
+         *
+         * We should make it so that the incoming data is decompressed and parsed on the fly.
+         */
+        ret = rsvg_handle_read_stream_sync (handle, priv->compressed_input_stream, NULL, error);
+        g_object_unref (priv->compressed_input_stream);
+        priv->compressed_input_stream = NULL;
 
         return ret;
-    }
 #else
-    if (priv->gzipped_data) {
         GsfInput *gzip;
         const guchar *bytes;
         gsize size;
@@ -1848,10 +1912,18 @@ rsvg_handle_close (RsvgHandle * handle, GError ** error)
         gsf_output_close (priv->gzipped_data);
         g_object_unref (priv->gzipped_data);
         priv->gzipped_data = NULL;
-    }
 #endif /* GIO >= 2.24.0 */
+    }
+
+    result = rsvg_handle_close_impl (handle, error);
 
-    return rsvg_handle_close_impl (handle, error);
+    if (result) {
+        priv->state = RSVG_HANDLE_STATE_CLOSED_OK;
+    } else {
+        priv->state = RSVG_HANDLE_STATE_CLOSED_ERROR;
+    }
+
+    return result;
 }
 
 /**
@@ -1883,10 +1955,10 @@ rsvg_handle_read_stream_sync (RsvgHandle   *handle,
     xmlParserInputBufferPtr buffer;
     xmlParserInputPtr input;
     int result;
-    xmlDocPtr doc;
     GError *err = NULL;
     gboolean res = FALSE;
     const guchar *buf;
+    gssize num_read;
 
     g_return_val_if_fail (RSVG_IS_HANDLE (handle), FALSE);
     g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
@@ -1897,12 +1969,21 @@ rsvg_handle_read_stream_sync (RsvgHandle   *handle,
 
     /* detect zipped streams */
     stream = g_buffered_input_stream_new (stream);
-    if (g_buffered_input_stream_fill (G_BUFFERED_INPUT_STREAM (stream), 2, cancellable, error) != 2) {
+    num_read = g_buffered_input_stream_fill (G_BUFFERED_INPUT_STREAM (stream), 2, cancellable, error);
+    if (num_read < 2) {
         g_object_unref (stream);
+        priv->state = RSVG_HANDLE_STATE_CLOSED_ERROR;
+        if (num_read < 0) {
+            g_assert (error == NULL || *error != NULL);
+        } else {
+            g_set_error (error, rsvg_error_quark (), RSVG_ERROR_FAILED,
+                         _("Input file is too short"));
+        }
+
         return FALSE;
     }
     buf = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (stream), NULL);
-    if ((buf[0] == 0x1f) && (buf[1] == 0x8b)) {
+    if ((buf[0] == GZ_MAGIC_0) && (buf[1] == GZ_MAGIC_1)) {
 #if GLIB_CHECK_VERSION (2, 24, 0)
         GConverter *converter;
         GInputStream *conv_stream;
@@ -1953,14 +2034,6 @@ rsvg_handle_read_stream_sync (RsvgHandle   *handle,
         goto out;
     }
 
-    doc = priv->ctxt->myDoc;
-    xmlFreeParserCtxt (priv->ctxt);
-    priv->ctxt = NULL;
-
-    xmlFreeDoc (doc);
-
-    priv->finished = TRUE;
-
     res = TRUE;
 #else
     {
@@ -1994,6 +2067,8 @@ rsvg_handle_read_stream_sync (RsvgHandle   *handle,
 
   out:
 
+    priv->ctxt = rsvg_free_xml_parser_and_doc (priv->ctxt);
+
     g_object_unref (stream);
 
     priv->error = NULL;
@@ -2003,6 +2078,12 @@ rsvg_handle_read_stream_sync (RsvgHandle   *handle,
         priv->cancellable = NULL;
     }
 
+    if (res) {
+        priv->state = RSVG_HANDLE_STATE_CLOSED_OK;
+    } else {
+        priv->state = RSVG_HANDLE_STATE_CLOSED_ERROR;
+    }
+
     return res;
 }
 
@@ -2389,17 +2470,16 @@ rsvg_return_if_fail_warning (const char *pretty_function, const char *expression
     g_set_error (error, RSVG_ERROR, 0, _("%s: assertion `%s' failed"), pretty_function, expression);
 }
 
-static gboolean
-_rsvg_handle_allow_load (RsvgHandle *handle,
-                         const char *uri,
-                         GError **error)
+gboolean
+rsvg_allow_load (GFile       *base_gfile,
+                 const char  *uri,
+                 GError     **error)
 {
-    RsvgHandlePrivate *priv = handle->priv;
     GFile *base;
     char *path, *dir;
     char *scheme = NULL, *cpath = NULL, *cdir = NULL;
 
-    g_assert (handle->priv->load_policy == RSVG_LOAD_POLICY_STRICT);
+    g_assert (error == NULL || *error == NULL);
 
     scheme = g_uri_parse_scheme (uri);
 
@@ -2412,11 +2492,11 @@ _rsvg_handle_allow_load (RsvgHandle *handle,
         goto allow;
 
     /* No base to compare to? */
-    if (priv->base_gfile == NULL)
+    if (base_gfile == NULL)
         goto deny;
 
     /* Deny loads from differing URI schemes */
-    if (!g_file_has_uri_scheme (priv->base_gfile, scheme))
+    if (!g_file_has_uri_scheme (base_gfile, scheme))
         goto deny;
 
     /* resource: is allowed to load anything from other resources */
@@ -2427,7 +2507,7 @@ _rsvg_handle_allow_load (RsvgHandle *handle,
     if (!g_str_equal (scheme, "file"))
         goto deny;
 
-    base = g_file_get_parent (priv->base_gfile);
+    base = g_file_get_parent (base_gfile);
     if (base == NULL)
         goto deny;
 
@@ -2472,9 +2552,9 @@ _rsvg_handle_allow_load (RsvgHandle *handle,
     return FALSE;
 }
 
-static char *
-_rsvg_handle_resolve_uri (RsvgHandle *handle,
-                          const char *uri)
+char *
+rsvg_handle_resolve_uri (RsvgHandle *handle,
+                         const char *uri)
 {
     RsvgHandlePrivate *priv = handle->priv;
     char *scheme, *resolved_uri;
@@ -2508,16 +2588,17 @@ _rsvg_handle_acquire_data (RsvgHandle *handle,
                            gsize *len,
                            GError **error)
 {
+    RsvgHandlePrivate *priv = handle->priv;
     char *uri;
     char *data;
 
-    uri = _rsvg_handle_resolve_uri (handle, url);
+    uri = rsvg_handle_resolve_uri (handle, url);
 
-    if (_rsvg_handle_allow_load (handle, uri, error)) {
-        data = _rsvg_io_acquire_data (uri, 
-                                      rsvg_handle_get_base_uri (handle), 
-                                      content_type, 
-                                      len, 
+    if (rsvg_allow_load (priv->base_gfile, uri, error)) {
+        data = _rsvg_io_acquire_data (uri,
+                                      rsvg_handle_get_base_uri (handle),
+                                      content_type,
+                                      len,
                                       handle->priv->cancellable,
                                       error);
     } else {
@@ -2534,15 +2615,16 @@ _rsvg_handle_acquire_stream (RsvgHandle *handle,
                              char **content_type,
                              GError **error)
 {
+    RsvgHandlePrivate *priv = handle->priv;
     char *uri;
     GInputStream *stream;
 
-    uri = _rsvg_handle_resolve_uri (handle, url);
+    uri = rsvg_handle_resolve_uri (handle, url);
 
-    if (_rsvg_handle_allow_load (handle, uri, error)) {
-        stream = _rsvg_io_acquire_stream (uri, 
-                                          rsvg_handle_get_base_uri (handle), 
-                                          content_type, 
+    if (rsvg_allow_load (priv->base_gfile, uri, error)) {
+        stream = _rsvg_io_acquire_stream (uri,
+                                          rsvg_handle_get_base_uri (handle),
+                                          content_type,
                                           handle->priv->cancellable,
                                           error);
     } else {
@@ -2552,3 +2634,23 @@ _rsvg_handle_acquire_stream (RsvgHandle *handle,
     g_free (uri);
     return stream;
 }
+
+/* Frees the ctxt and its ctxt->myDoc - libxml2 doesn't free them together
+ * http://xmlsoft.org/html/libxml-parser.html#xmlFreeParserCtxt
+ *
+ * Returns NULL.
+ */
+xmlParserCtxtPtr
+rsvg_free_xml_parser_and_doc (xmlParserCtxtPtr ctxt)
+{
+    if (ctxt) {
+        if (ctxt->myDoc) {
+            xmlFreeDoc (ctxt->myDoc);
+            ctxt->myDoc = NULL;
+        }
+
+        xmlFreeParserCtxt (ctxt);
+    }
+
+    return NULL;
+}
diff --git a/rsvg-cairo-clip.c b/rsvg-cairo-clip.c
index f2af3e5..909a89f 100644
--- a/rsvg-cairo-clip.c
+++ b/rsvg-cairo-clip.c
@@ -35,6 +35,7 @@
 
 #include <math.h>
 #include <string.h>
+#include <pango/pangocairo.h>
 
 typedef struct RsvgCairoClipRender RsvgCairoClipRender;
 
@@ -60,6 +61,31 @@ rsvg_cairo_clip_apply_affine (RsvgCairoClipRender *render, cairo_matrix_t *affin
     cairo_set_matrix (cairo_render->cr, &matrix);
 }
 
+static void
+rsvg_cairo_clip_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, double x, double y)
+{
+    RsvgCairoClipRender *render = RSVG_CAIRO_CLIP_RENDER (ctx->render);
+    RsvgCairoRender *cairo_render = &render->super;
+    cairo_matrix_t affine;
+    PangoGravity gravity = pango_context_get_gravity (pango_layout_get_context (layout));
+    double rotation;
+
+    affine = ctx->state->affine;
+    rsvg_cairo_clip_apply_affine (render, &affine);
+
+    rotation = pango_gravity_to_rotation (gravity);
+
+    cairo_save (cairo_render->cr);
+    cairo_move_to (cairo_render->cr, x, y);
+    if (rotation != 0.)
+        cairo_rotate (cairo_render->cr, -rotation);
+
+    pango_cairo_update_layout (cairo_render->cr, layout);
+    pango_cairo_layout_path (cairo_render->cr, layout);
+
+    cairo_restore (cairo_render->cr);
+}
+
 static void
 rsvg_cairo_clip_render_path (RsvgDrawingCtx * ctx, const cairo_path_t *path)
 {
@@ -112,7 +138,7 @@ rsvg_cairo_clip_add_clipping_rect (RsvgDrawingCtx * ctx, double x, double y, dou
 }
 
 static RsvgRender *
-rsvg_cairo_clip_render_new (cairo_t * cr, RsvgCairoRender *parent)
+rsvg_cairo_clip_render_new (cairo_t *cr, RsvgCairoRender *parent)
 {
     RsvgCairoClipRender *clip_render = g_new0 (RsvgCairoClipRender, 1);
     RsvgCairoRender *cairo_render = &clip_render->super;
@@ -123,15 +149,24 @@ rsvg_cairo_clip_render_new (cairo_t * cr, RsvgCairoRender *parent)
     render->type = RSVG_RENDER_TYPE_CAIRO_CLIP;
     render->free = rsvg_cairo_clip_render_free;
     render->create_pango_context = rsvg_cairo_create_pango_context;
-    render->render_pango_layout = rsvg_cairo_render_pango_layout;
-    render->render_surface = rsvg_cairo_clip_render_surface;
+    render->render_pango_layout = rsvg_cairo_clip_render_pango_layout;
     render->render_path = rsvg_cairo_clip_render_path;
+    render->render_surface = rsvg_cairo_clip_render_surface;
     render->pop_discrete_layer = rsvg_cairo_clip_pop_discrete_layer;
     render->push_discrete_layer = rsvg_cairo_clip_push_discrete_layer;
     render->add_clipping_rect = rsvg_cairo_clip_add_clipping_rect;
     render->get_surface_of_node = NULL;
+
     cairo_render->initial_cr = parent->cr;
-    cairo_render->cr = cr;
+    cairo_render->cr         = cr;
+    cairo_render->width      = parent->width;
+    cairo_render->height     = parent->height;
+    cairo_render->offset_x   = parent->offset_x;
+    cairo_render->offset_y   = parent->offset_y;
+    cairo_render->cr_stack   = NULL;
+    cairo_render->bbox       = parent->bbox;
+    cairo_render->bb_stack   = NULL;
+
     clip_render->parent = parent;
 
     return render;
@@ -140,10 +175,14 @@ rsvg_cairo_clip_render_new (cairo_t * cr, RsvgCairoRender *parent)
 void
 rsvg_cairo_clip (RsvgDrawingCtx * ctx, RsvgClipPath * clip, RsvgBbox * bbox)
 {
+    RsvgCairoClipRender *clip_render;
     RsvgCairoRender *save = RSVG_CAIRO_RENDER (ctx->render);
+    cairo_t *cr;
     cairo_matrix_t affinesave;
 
-    ctx->render = rsvg_cairo_clip_render_new (save->cr, save);
+    cr = save->cr;
+    clip_render = RSVG_CAIRO_CLIP_RENDER (rsvg_cairo_clip_render_new (cr, save));
+    ctx->render = &clip_render->super.super;
 
     /* Horribly dirty hack to have the bbox premultiplied to everything */
     if (clip->units == objectBoundingBox) {
@@ -166,7 +205,11 @@ rsvg_cairo_clip (RsvgDrawingCtx * ctx, RsvgClipPath * clip, RsvgBbox * bbox)
     if (clip->units == objectBoundingBox)
         clip->super.state->affine = affinesave;
 
+    g_assert (clip_render->super.cr_stack == NULL);
+    g_assert (clip_render->super.bb_stack == NULL);
+    g_assert (clip_render->super.surfaces_stack == NULL);
+
     g_free (ctx->render);
-    cairo_clip (save->cr);
+    cairo_clip (cr);
     ctx->render = &save->super;
 }
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index f100d9f..4ec42c6 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -189,6 +189,7 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
     cairo_matrix_t affine, caffine, taffine;
     double bbwscale, bbhscale, scwscale, schscale;
     double patternw, patternh, patternx, patterny;
+    double scaled_width, scaled_height;
     int pw, ph;
 
     rsvg_pattern = &local_pattern;
@@ -206,10 +207,8 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
     if (rsvg_pattern->obj_bbox)
         _rsvg_pop_view_box (ctx);
 
-
     /* Work out the size of the rectangle so it takes into account the object bounding box */
 
-
     if (rsvg_pattern->obj_bbox) {
         bbwscale = bbox.rect.width;
         bbhscale = bbox.rect.height;
@@ -226,8 +225,14 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
     pw = patternw * bbwscale * scwscale;
     ph = patternh * bbhscale * schscale;
 
-    scwscale = (double) pw / (double) (patternw * bbwscale);
-    schscale = (double) ph / (double) (patternh * bbhscale);
+    scaled_width = patternw * bbwscale;
+    scaled_height = patternh * bbhscale;
+
+    if (fabs (scaled_width) < DBL_EPSILON || fabs (scaled_height) < DBL_EPSILON)
+        return;
+
+    scwscale = pw / scaled_width;
+    schscale = ph / scaled_height;
 
     surface = cairo_surface_create_similar (cairo_get_target (cr_render),
                                             CAIRO_CONTENT_COLOR_ALPHA, pw, ph);
@@ -390,12 +395,16 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
     PangoGravity gravity = pango_context_get_gravity (pango_layout_get_context (layout));
     double rotation;
 
+    pango_layout_get_extents (layout, &ink, NULL);
+
+    if (ink.width == 0 || ink.height == 0) {
+        return;
+    }
+
     cairo_set_antialias (render->cr, state->text_rendering_type);
 
     _set_rsvg_affine (render, &state->affine);
 
-    pango_layout_get_extents (layout, &ink, NULL);
-
     rsvg_bbox_init (&bbox, &state->affine);
     if (PANGO_GRAVITY_IS_VERTICAL (gravity)) {
         bbox.rect.x = x + (ink.x - ink.height) / (double)PANGO_SCALE;
@@ -422,6 +431,8 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
                                        bbox, rsvg_current_state (ctx)->current_color);
         if (rotation != 0.)
             cairo_rotate (render->cr, -rotation);
+
+        pango_cairo_update_layout (render->cr, layout);
         pango_cairo_show_layout (render->cr, layout);
         cairo_restore (render->cr);
     }
@@ -439,6 +450,8 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
 
         if (rotation != 0.)
             cairo_rotate (render->cr, -rotation);
+
+        pango_cairo_update_layout (render->cr, layout);
         pango_cairo_layout_path (render->cr, layout);
 
         cairo_set_line_width (render->cr, _rsvg_css_normalize_length (&state->stroke_width, ctx, 'h'));
@@ -486,6 +499,19 @@ rsvg_cairo_render_path (RsvgDrawingCtx * ctx, const cairo_path_t *path)
        _rendering_ time speedups, are these rather expensive operations
        really needed here? */
 
+    /* FIXME: See https://www.w3.org/TR/SVG/coords.html#ObjectBoundingBox for
+     * discussion on how to compute bounding boxes to be used for viewports and
+     * clipping.  It looks like we should be using cairo_path_extents() for
+     * that, not cairo_fill_extents().
+     *
+     * We may need to maintain *two* sets of bounding boxes - one for
+     * viewports/clipping, and one for user applications like a
+     * rsvg_compute_ink_rect() function in the future.
+     *
+     * See https://bugzilla.gnome.org/show_bug.cgi?id=760112 for discussion of a
+     * public API to get the ink rectangle.
+     */
+
     /* Bounding box for fill
      *
      * Unlike the case for stroke, for fills we always compute the bounding box.
@@ -693,9 +719,28 @@ rsvg_cairo_generate_mask (cairo_t * cr, RsvgMask * self, RsvgDrawingCtx * ctx, R
         guint8 *row_data = (pixels + (row * rowstride));
         for (i = 0; i < width; i++) {
             guint32 *pixel = (guint32 *) row_data + i;
-            *pixel = ((((*pixel & 0x00ff0000) >> 16) * 13817 +
-                       ((*pixel & 0x0000ff00) >> 8) * 46518 +
-                       ((*pixel & 0x000000ff)) * 4688) * state->opacity);
+            /*
+             *  Assuming, the pixel is linear RGB (not sRGB)
+             *  y = luminance
+             *  Y = 0.2126 R + 0.7152 G + 0.0722 B
+             *  1.0 opacity = 255
+             *
+             *  When Y = 1.0, pixel for mask should be 0xFFFFFFFF
+             *  	(you get 1.0 luminance from 255 from R, G and B)
+             *
+             *	r_mult = 0xFFFFFFFF / (255.0 * 255.0) * .2126 = 14042.45  ~= 14042
+             *	g_mult = 0xFFFFFFFF / (255.0 * 255.0) * .7152 = 47239.69  ~= 47240
+             *	b_mult = 0xFFFFFFFF / (255.0 * 255.0) * .0722 =  4768.88  ~= 4769
+             *
+             * 	This allows for the following expected behaviour:
+             *  (we only care about the most sig byte)
+             *	if pixel = 0x00FFFFFF, pixel' = 0xFF......
+             *	if pixel = 0x00020202, pixel' = 0x02......
+             *	if pixel = 0x00000000, pixel' = 0x00......
+             */
+            *pixel = ((((*pixel & 0x00ff0000) >> 16) * 14042 +
+                       ((*pixel & 0x0000ff00) >>  8) * 47240 +
+                       ((*pixel & 0x000000ff)      ) * 4769    ) * state->opacity);
         }
     }
 
diff --git a/rsvg-cairo-render.c b/rsvg-cairo-render.c
index 4285fe8..ca93338 100644
--- a/rsvg-cairo-render.c
+++ b/rsvg-cairo-render.c
@@ -53,6 +53,7 @@ RsvgCairoRender *
 rsvg_cairo_render_new (cairo_t * cr, double width, double height)
 {
     RsvgCairoRender *cairo_render = g_new0 (RsvgCairoRender, 1);
+    cairo_matrix_t matrix;
 
     cairo_render->super.type = RSVG_RENDER_TYPE_CAIRO;
     cairo_render->super.free = rsvg_cairo_render_free;
@@ -74,6 +75,9 @@ rsvg_cairo_render_new (cairo_t * cr, double width, double height)
     cairo_render->bb_stack = NULL;
     cairo_render->surfaces_stack = NULL;
 
+    cairo_matrix_init_identity (&matrix);
+    rsvg_bbox_init (&cairo_render->bbox, &matrix);
+
     return cairo_render;
 }
 
@@ -198,7 +202,7 @@ rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id)
 
     g_return_val_if_fail (handle != NULL, FALSE);
 
-    if (!handle->priv->finished)
+    if (handle->priv->state != RSVG_HANDLE_STATE_CLOSED_OK)
         return FALSE;
 
     if (id && *id)
diff --git a/rsvg-cairo-render.h b/rsvg-cairo-render.h
index d9bfd57..75e8489 100644
--- a/rsvg-cairo-render.h
+++ b/rsvg-cairo-render.h
@@ -54,8 +54,7 @@ struct _RsvgCairoRender {
 
 G_GNUC_INTERNAL
 RsvgCairoRender *rsvg_cairo_render_new		(cairo_t * cr, double width, double height);
-G_GNUC_INTERNAL
-void		rsvg_cairo_render_rsvg_handle	(cairo_t * cr, RsvgHandle * handle);
+
 G_GNUC_INTERNAL
 RsvgDrawingCtx *rsvg_cairo_new_drawing_ctx	(cairo_t * cr, RsvgHandle * handle);
 
diff --git a/rsvg-cond.c b/rsvg-cond.c
index b5c02a4..35a73b3 100644
--- a/rsvg-cond.c
+++ b/rsvg-cond.c
@@ -34,12 +34,13 @@
 #include <stdlib.h>
 #include <locale.h>
 
+/* Keep these sorted alphabetically!  These are used with bsearch() */
 static const char *implemented_features[] = {
-    "http://www.w3.org/TR/SVG11/feature#BasicText",
     "http://www.w3.org/TR/SVG11/feature#BasicFilter",
     "http://www.w3.org/TR/SVG11/feature#BasicGraphicsAttribute",
     "http://www.w3.org/TR/SVG11/feature#BasicPaintAttribute",
     "http://www.w3.org/TR/SVG11/feature#BasicStructure",
+    "http://www.w3.org/TR/SVG11/feature#BasicText",
     "http://www.w3.org/TR/SVG11/feature#ConditionalProcessing",
     "http://www.w3.org/TR/SVG11/feature#ContainerAttribute",
     "http://www.w3.org/TR/SVG11/feature#Filter",
@@ -49,11 +50,11 @@ static const char *implemented_features[] = {
     "http://www.w3.org/TR/SVG11/feature#Mask",
     "http://www.w3.org/TR/SVG11/feature#OpacityAttribute",
     "http://www.w3.org/TR/SVG11/feature#Pattern",
+    "http://www.w3.org/TR/SVG11/feature#SVG",
+    "http://www.w3.org/TR/SVG11/feature#SVG-static",
     "http://www.w3.org/TR/SVG11/feature#Shape",
     "http://www.w3.org/TR/SVG11/feature#Structure",
     "http://www.w3.org/TR/SVG11/feature#Style",
-    "http://www.w3.org/TR/SVG11/feature#SVG",
-    "http://www.w3.org/TR/SVG11/feature#SVG-static",
     "http://www.w3.org/TR/SVG11/feature#View",
     "org.w3c.svg.static"        /* deprecated SVG 1.0 feature string */
 };
diff --git a/rsvg-defs.c b/rsvg-defs.c
index d2c6a06..9a3f877 100644
--- a/rsvg-defs.c
+++ b/rsvg-defs.c
@@ -52,52 +52,50 @@ rsvg_defs_new (RsvgHandle *handle)
     return result;
 }
 
-static int
-rsvg_defs_load_extern (const RsvgDefs * defs, const char *name)
+static RsvgHandle *
+rsvg_defs_load_extern (const RsvgDefs * defs, const char *uri)
 {
-    RsvgHandle *handle;
-    gchar *filename, *base_uri;
+    RsvgHandle *handle = NULL;
     char *data;
     gsize data_len;
-    gboolean rv;
-
-    filename = _rsvg_io_get_file_path (name, rsvg_handle_get_base_uri (defs->ctx));
 
-    data = _rsvg_handle_acquire_data (defs->ctx, name, NULL, &data_len, NULL);
+    data = _rsvg_handle_acquire_data (defs->ctx, uri, NULL, &data_len, NULL);
 
     if (data) {
         handle = rsvg_handle_new ();
-
-        base_uri = rsvg_get_base_uri_from_filename (filename);
-        rsvg_handle_set_base_uri (handle, base_uri);
-        g_free (base_uri);
-
-        rv = rsvg_handle_write (handle, (guchar *) data, data_len, NULL);
-        rv = rsvg_handle_close (handle, NULL) && rv;
-        if (rv) {
-            g_hash_table_insert (defs->externs, g_strdup (name), handle);
+        rsvg_handle_set_base_uri (handle, uri);
+
+        if (rsvg_handle_write (handle, (guchar *) data, data_len, NULL)
+            && rsvg_handle_close (handle, NULL)) {
+            g_hash_table_insert (defs->externs, g_strdup (uri), handle);
+        } else {
+            g_object_unref (handle);
+            handle = NULL;
         }
 
         g_free (data);
     }
 
-    g_free (filename);
-    return 0;
+    return handle;
 }
 
 static RsvgNode *
-rsvg_defs_extern_lookup (const RsvgDefs * defs, const char *filename, const char *name)
+rsvg_defs_extern_lookup (const RsvgDefs * defs, const char *possibly_relative_uri, const char *name)
 {
-    RsvgHandle *file;
-    file = (RsvgHandle *) g_hash_table_lookup (defs->externs, filename);
-    if (file == NULL) {
-        if (rsvg_defs_load_extern (defs, filename))
-            return NULL;
-        file = (RsvgHandle *) g_hash_table_lookup (defs->externs, filename);
+    RsvgHandle *handle;
+    char *uri;
+
+    uri = rsvg_handle_resolve_uri (defs->ctx, possibly_relative_uri);
+    if (!uri)
+        return NULL;
+
+    handle = (RsvgHandle *) g_hash_table_lookup (defs->externs, uri);
+    if (handle == NULL) {
+        handle = rsvg_defs_load_extern (defs, uri);
     }
 
-    if (file != NULL)
-        return g_hash_table_lookup (file->priv->defs->hash, name);
+    if (handle != NULL)
+        return g_hash_table_lookup (handle->priv->defs->hash, name);
     else
         return NULL;
 }
diff --git a/rsvg-filter.c b/rsvg-filter.c
index 81cce75..7148360 100644
--- a/rsvg-filter.c
+++ b/rsvg-filter.c
@@ -676,7 +676,6 @@ rsvg_filter_get_bg (RsvgFilterContext * ctx)
     return ctx->bg_surface;
 }
 
-/* FIXMEchpe: proper return value and out param! */
 /**
  * rsvg_filter_get_result:
  * @name: The name of the surface
@@ -722,24 +721,21 @@ rsvg_filter_get_result (GString * name, RsvgFilterContext * ctx)
         return output;
     }
 
-    /* g_warning (_("%s not found\n"), name->str); */
-
-    output = ctx->lastresult;
-    cairo_surface_reference (output.surface);
+    output.surface = NULL;
     return output;
 }
 
-/**
- * rsvg_filter_get_in:
- * @name:
- * @ctx:
- * 
- * Returns: (transfer full) (nullable): a new #cairo_surface_t, or %NULL
- */
 static cairo_surface_t *
 rsvg_filter_get_in (GString * name, RsvgFilterContext * ctx)
 {
-    return rsvg_filter_get_result (name, ctx).surface;
+    cairo_surface_t *surface;
+
+    surface = rsvg_filter_get_result (name, ctx).surface;
+    if (surface == NULL || cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        return NULL;
+    }
+
+    return surface;
 }
 
 static void
@@ -1331,7 +1327,9 @@ box_blur_line (gint box_width, gint even_offset,
                       kernel; it's the pixel to remove from the accumulator. */
     gint  *ac;     /* Accumulator for each channel                           */
 
-    ac = g_new (gint, bpp);
+    g_assert (box_width > 0);
+
+    ac = g_new0 (gint, bpp);
 
     /* The algorithm differs for even and odd-sized kernels.
      * With the output at the center,
@@ -1688,7 +1686,6 @@ gaussian_blur_surface (cairo_surface_t *in,
                        gdouble sx,
                        gdouble sy)
 {
-    gboolean use_box_blur;
     gint width, height;
     cairo_format_t in_format, out_format;
     gint in_stride;
@@ -1732,14 +1729,6 @@ gaussian_blur_surface (cairo_surface_t *in,
     if (sy < 0.0)
         sy = 0.0;
 
-    /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with
-     * clever offsets.
-     */
-    if (sx < 10.0 && sy < 10.0)
-        use_box_blur = FALSE;
-    else
-        use_box_blur = TRUE;
-
     /* Bail out by just copying? */
     if ((sx == 0.0 && sy == 0.0)
         || sx > 1000 || sy > 1000) {
@@ -1759,6 +1748,15 @@ gaussian_blur_surface (cairo_surface_t *in,
         int y;
         guchar *row_buffer = NULL;
         guchar *row1, *row2;
+        gboolean use_box_blur;
+
+        /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with
+         * clever offsets.
+         */
+        if (sx < 10.0)
+            use_box_blur = FALSE;
+        else
+            use_box_blur = TRUE;
 
         if (use_box_blur) {
             box_width = compute_box_blur_width (sx);
@@ -1814,6 +1812,15 @@ gaussian_blur_surface (cairo_surface_t *in,
         guchar *col_buffer;
         guchar *col1, *col2;
         int x;
+        gboolean use_box_blur;
+
+        /* For small radiuses, use a true gaussian kernel; otherwise use three box blurs with
+         * clever offsets.
+         */
+        if (sy < 10.0)
+            use_box_blur = FALSE;
+        else
+            use_box_blur = TRUE;
 
         /* twice the size so we can have the source pixels and the blurred pixels */
         col_buffer = g_new (guchar, height * bpp * 2);
@@ -1863,15 +1870,17 @@ rsvg_filter_primitive_gaussian_blur_render (RsvgFilterPrimitive * self, RsvgFilt
     int width, height;
     cairo_surface_t *output, *in;
     RsvgIRect boundarys;
-    gfloat sdx, sdy;
+    gdouble sdx, sdy;
     RsvgFilterPrimitiveOutput op;
     cairo_t *cr;
 
     upself = (RsvgFilterPrimitiveGaussianBlur *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
-    op = rsvg_filter_get_result (self->in, ctx);
-    in = op.surface;
+    in = rsvg_filter_get_in (self->in, ctx);
+    if (in == NULL) {
+        return;
+    }
 
     width = cairo_image_surface_get_width (in);
     height = cairo_image_surface_get_height (in);
@@ -1884,8 +1893,8 @@ rsvg_filter_primitive_gaussian_blur_render (RsvgFilterPrimitive * self, RsvgFilt
     }
 
     /* scale the SD values */
-    sdx = upself->sdx * ctx->paffine.xx;
-    sdy = upself->sdy * ctx->paffine.yy;
+    sdx = fabs (upself->sdx * ctx->paffine.xx);
+    sdy = fabs (upself->sdy * ctx->paffine.yy);
 
     gaussian_blur_surface (in, output, sdx, sdy);
 
@@ -4853,8 +4862,17 @@ rsvg_filter_primitive_tile_render (RsvgFilterPrimitive * self, RsvgFilterContext
 
     input = rsvg_filter_get_result (self->in, ctx);
     in = input.surface;
+    if (in == NULL) {
+        return;
+    }
+
     boundarys = input.bounds;
 
+    if ((boundarys.x0 >= boundarys.x1) || (boundarys.y0 >= boundarys.y1)) {
+        cairo_surface_destroy (in);
+        return;
+    }
+
     cairo_surface_flush (in);
 
     in_pixels = cairo_image_surface_get_data (in);
diff --git a/rsvg-gobject.c b/rsvg-gobject.c
index 5639e77..5748deb 100644
--- a/rsvg-gobject.c
+++ b/rsvg-gobject.c
@@ -68,6 +68,7 @@ rsvg_handle_init (RsvgHandle * self)
     self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, RSVG_TYPE_HANDLE, RsvgHandlePrivate);
 
     self->priv->flags = RSVG_HANDLE_FLAGS_NONE;
+    self->priv->state = RSVG_HANDLE_STATE_START;
     self->priv->load_policy = RSVG_LOAD_POLICY_DEFAULT;
     self->priv->defs = rsvg_defs_new (self);
     self->priv->handler_nest = 0;
@@ -87,13 +88,11 @@ rsvg_handle_init (RsvgHandle * self)
     self->priv->currentnode = NULL;
     self->priv->treebase = NULL;
 
-    self->priv->finished = 0;
 #if GLIB_CHECK_VERSION (2, 24, 0)
-    self->priv->data_input_stream = NULL;
+    self->priv->compressed_input_stream = NULL;
 #else
     self->priv->gzipped_data = NULL;
 #endif
-    self->priv->first_write = TRUE;
     self->priv->cancellable = NULL;
 
     self->priv->is_disposed = FALSE;
@@ -110,6 +109,8 @@ rsvg_handle_dispose (GObject *instance)
 
     self->priv->is_disposed = TRUE;
 
+    self->priv->ctxt = rsvg_free_xml_parser_and_doc (self->priv->ctxt);
+
     g_hash_table_destroy (self->priv->entities);
     rsvg_defs_free (self->priv->defs);
     g_hash_table_destroy (self->priv->css_props);
@@ -131,9 +132,9 @@ rsvg_handle_dispose (GObject *instance)
         g_object_unref (self->priv->base_gfile);
         self->priv->base_gfile = NULL;
     }
-    if (self->priv->data_input_stream) {
-        g_object_unref (self->priv->data_input_stream);
-        self->priv->data_input_stream = NULL;
+    if (self->priv->compressed_input_stream) {
+        g_object_unref (self->priv->compressed_input_stream);
+        self->priv->compressed_input_stream = NULL;
     }
 #else
     if (self->priv->gzipped_data) {
diff --git a/rsvg-io.c b/rsvg-io.c
index 7452b73..79c7863 100644
--- a/rsvg-io.c
+++ b/rsvg-io.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /*
    Copyright (C) 2000 Eazel, Inc.
    Copyright (C) 2002, 2003, 2004, 2005 Dom Lachowicz <cinamod@hotmail.com>
@@ -66,11 +67,10 @@ uri_decoded_copy (const char *part,
 #define BASE64_INDICATOR_LEN (sizeof (";base64") - 1)
 
 static char *
-rsvg_acquire_data_data (const char *uri,
-                        const char *base_uri, 
-                        char **out_mime_type,
-                        gsize *out_len,
-                        GError **error)
+rsvg_decode_data_uri (const char *uri,
+                      char **out_mime_type,
+                      gsize *out_len,
+                      GError **error)
 {
     const char *comma, *start, *end;
     char *mime_type;
@@ -124,56 +124,32 @@ rsvg_acquire_data_data (const char *uri,
     return data;
 }
 
-gchar *
-_rsvg_io_get_file_path (const gchar * filename,
-                        const gchar * base_uri)
-{
-    gchar *absolute_filename;
-
-    if (g_file_test (filename, G_FILE_TEST_EXISTS) || g_path_is_absolute (filename)) {
-        absolute_filename = g_strdup (filename);
-    } else {
-        gchar *tmpcdir;
-        gchar *base_filename;
-
-        if (base_uri) {
-            base_filename = g_filename_from_uri (base_uri, NULL, NULL);
-            if (base_filename != NULL) {
-                tmpcdir = g_path_get_dirname (base_filename);
-                g_free (base_filename);
-            } else 
-                return NULL;
-        } else
-            tmpcdir = g_get_current_dir ();
-
-        absolute_filename = g_build_filename (tmpcdir, filename, NULL);
-        g_free (tmpcdir);
-    }
-
-    return absolute_filename;
-}
-
 static char *
-rsvg_acquire_file_data (const char *filename,
-                        const char *base_uri,
+rsvg_acquire_file_data (const char *uri,
                         char **out_mime_type,
                         gsize *out_len,
                         GCancellable *cancellable,
                         GError **error)
 {
+    GFile *file;
     gchar *path, *data;
     gsize len;
     char *content_type;
 
-    rsvg_return_val_if_fail (filename != NULL, NULL, error);
+    rsvg_return_val_if_fail (uri != NULL, NULL, error);
     g_assert (out_len != NULL);
 
-    path = _rsvg_io_get_file_path (filename, base_uri);
-    if (path == NULL)
+    file = g_file_new_for_uri (uri);
+    path = g_file_get_path (file);
+
+    if (path == NULL) {
+        g_object_unref (file);
         return NULL;
+    }
 
     if (!g_file_get_contents (path, &data, &len, error)) {
         g_free (path);
+        g_object_unref (file);
         return NULL;
     }
 
@@ -184,6 +160,7 @@ rsvg_acquire_file_data (const char *filename,
     }
 
     g_free (path);
+    g_object_unref (file);
 
     *out_len = len;
     return data;
@@ -313,9 +290,9 @@ _rsvg_io_acquire_data (const char *href,
         len = &llen;
 
     if (strncmp (href, "data:", 5) == 0)
-      return rsvg_acquire_data_data (href, NULL, mime_type, len, error);
+      return rsvg_decode_data_uri (href, mime_type, len, error);
 
-    if ((data = rsvg_acquire_file_data (href, base_uri, mime_type, len, cancellable, NULL)))
+    if ((data = rsvg_acquire_file_data (href, mime_type, len, cancellable, NULL)))
       return data;
 
     if ((data = rsvg_acquire_gvfs_data (href, base_uri, mime_type, len, cancellable, error)))
@@ -342,13 +319,13 @@ _rsvg_io_acquire_stream (const char *href,
     }
 
     if (strncmp (href, "data:", 5) == 0) {
-        if (!(data = rsvg_acquire_data_data (href, NULL, mime_type, &len, error)))
+        if (!(data = rsvg_decode_data_uri (href, mime_type, &len, error)))
             return NULL;
 
         return g_memory_input_stream_new_from_data (data, len, (GDestroyNotify) g_free);
     }
 
-    if ((data = rsvg_acquire_file_data (href, base_uri, mime_type, &len, cancellable, NULL)))
+    if ((data = rsvg_acquire_file_data (href, mime_type, &len, cancellable, NULL)))
       return g_memory_input_stream_new_from_data (data, len, (GDestroyNotify) g_free);
 
     if ((stream = rsvg_acquire_gvfs_stream (href, base_uri, mime_type, cancellable, error)))
diff --git a/rsvg-io.h b/rsvg-io.h
index d710ccf..50e0002 100644
--- a/rsvg-io.h
+++ b/rsvg-io.h
@@ -26,10 +26,6 @@
 #include <glib.h>
 #include <gio/gio.h>
 
-G_GNUC_INTERNAL
-gchar *_rsvg_io_get_file_path (const gchar *filename, 
-                               const gchar *basedir);
-
 char *_rsvg_io_acquire_data (const char *uri,
 			     const char *base_uri,
 			     char **mime_type,
diff --git a/rsvg-private.h b/rsvg-private.h
index 35b4607..03d02ad 100644
--- a/rsvg-private.h
+++ b/rsvg-private.h
@@ -132,13 +132,24 @@ typedef enum {
 
 #define RSVG_LOAD_POLICY_DEFAULT (RSVG_LOAD_POLICY_STRICT)
 
+/* Reading state for an RsvgHandle */
+typedef enum {
+    RSVG_HANDLE_STATE_START,
+    RSVG_HANDLE_STATE_EXPECTING_GZ_1,
+    RSVG_HANDLE_STATE_READING_COMPRESSED,
+    RSVG_HANDLE_STATE_READING,
+    RSVG_HANDLE_STATE_CLOSED_OK,
+    RSVG_HANDLE_STATE_CLOSED_ERROR
+} RsvgHandleState;
+
 struct RsvgHandlePrivate {
     RsvgHandleFlags flags;
 
+    RsvgHandleState state;
+
     RsvgLoadPolicy load_policy;
 
     gboolean is_disposed;
-    gboolean is_closed;
 
     RsvgSizeFunc size_func;
     gpointer user_data;
@@ -177,13 +188,10 @@ struct RsvgHandlePrivate {
     gchar *base_uri;
     GFile *base_gfile;
 
-    gboolean finished;
-
     gboolean in_loop;		/* see get_dimension() */
 
-    gboolean first_write;
 #if GLIB_CHECK_VERSION (2, 24, 0)
-    GInputStream *data_input_stream; /* for rsvg_handle_write of svgz data */
+    GInputStream *compressed_input_stream; /* for rsvg_handle_write of svgz data */
 #else
     GsfOutput *gzipped_data;
 #endif
@@ -360,8 +368,7 @@ GdkPixbuf *rsvg_pixbuf_from_data_with_size_data (const guchar * buff,
                                                  const char *base_uri, GError ** error);
 G_GNUC_INTERNAL
 gboolean     rsvg_eval_switch_attributes	(RsvgPropertyBag * atts, gboolean * p_has_cond);
-G_GNUC_INTERNAL
-gchar       *rsvg_get_base_uri_from_filename    (const gchar * file_name);
+
 G_GNUC_INTERNAL
 void rsvg_pop_discrete_layer    (RsvgDrawingCtx * ctx);
 G_GNUC_INTERNAL
@@ -417,6 +424,15 @@ G_GNUC_INTERNAL
 void rsvg_return_if_fail_warning (const char *pretty_function,
                                   const char *expression, GError ** error);
 
+G_GNUC_INTERNAL
+char *rsvg_handle_resolve_uri (RsvgHandle *handle,
+                               const char *uri);
+
+G_GNUC_INTERNAL
+gboolean rsvg_allow_load (GFile       *base_gfile,
+                          const char  *uri,
+                          GError     **error);
+
 G_GNUC_INTERNAL
 char *_rsvg_handle_acquire_data (RsvgHandle *handle,
                                  const char *uri,
@@ -429,6 +445,9 @@ GInputStream *_rsvg_handle_acquire_stream (RsvgHandle *handle,
                                            char **content_type,
                                            GError **error);
 
+G_GNUC_INTERNAL
+xmlParserCtxtPtr rsvg_free_xml_parser_and_doc (xmlParserCtxtPtr ctxt) G_GNUC_WARN_UNUSED_RESULT;
+
 
 #define rsvg_return_if_fail(expr, error)    G_STMT_START{			\
      if G_LIKELY(expr) { } else                                     \
diff --git a/rsvg-structure.c b/rsvg-structure.c
index 4ab4473..3c972e5 100644
--- a/rsvg-structure.c
+++ b/rsvg-structure.c
@@ -210,6 +210,8 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
         rsvg_state_push (ctx);
         rsvg_node_draw (child, ctx, 1);
         rsvg_state_pop (ctx);
+
+        rsvg_release_node (ctx, child);
         rsvg_pop_discrete_layer (ctx);
     } else {
         RsvgNodeSymbol *symbol = (RsvgNodeSymbol *) child;
@@ -246,9 +248,9 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
         rsvg_pop_discrete_layer (ctx);
         if (symbol->vbox.active)
             _rsvg_pop_view_box (ctx);
-    }
 
-    rsvg_release_node (ctx, child);
+        rsvg_release_node (ctx, child);
+    }
 }
 
 static void
diff --git a/rsvg-styles.c b/rsvg-styles.c
index 6836894..1069b1b 100644
--- a/rsvg-styles.c
+++ b/rsvg-styles.c
@@ -1219,6 +1219,7 @@ rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
     int n_args;
     guint key_len;
     cairo_matrix_t affine;
+    cairo_matrix_t inverse;
 
     cairo_matrix_init_identity (dst);
 
@@ -1242,7 +1243,8 @@ rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
                 break;
         }
         if (key_len >= sizeof (keyword))
-            return FALSE;
+            goto invalid;
+
         keyword[key_len] = '\0';
 
         /* skip whitespace */
@@ -1250,7 +1252,8 @@ rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
             idx++;
 
         if (src[idx] != '(')
-            return FALSE;
+            goto invalid;
+
         idx++;
 
         for (n_args = 0;; n_args++) {
@@ -1263,7 +1266,8 @@ rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
             c = src[idx];
             if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.') {
                 if (n_args == sizeof (args) / sizeof (args[0]))
-                    return FALSE;       /* too many args */
+                    goto invalid;
+
                 args[n_args] = g_ascii_strtod (src + idx, &end_ptr);
                 idx = end_ptr - src;
 
@@ -1276,7 +1280,7 @@ rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
             } else if (c == ')')
                 break;
             else
-                return FALSE;
+                goto invalid;
         }
         idx++;
 
@@ -1291,14 +1295,14 @@ rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
             if (n_args == 1)
                 args[1] = 0;
             else if (n_args != 2)
-                return FALSE;
+                goto invalid;
             cairo_matrix_init_translate (&affine, args[0], args[1]);
             cairo_matrix_multiply (dst, &affine, dst);
         } else if (!strcmp (keyword, "scale")) {
             if (n_args == 1)
                 args[1] = args[0];
             else if (n_args != 2)
-                return FALSE;
+                goto invalid;
             cairo_matrix_init_scale (&affine, args[0], args[1]);
             cairo_matrix_multiply (dst, &affine, dst);
         } else if (!strcmp (keyword, "rotate")) {
@@ -1316,24 +1320,34 @@ rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
                 cairo_matrix_init_translate (&affine, -args[1], -args[2]);
                 cairo_matrix_multiply (dst, &affine, dst);
             } else
-                return FALSE;
+                goto invalid;
         } else if (!strcmp (keyword, "skewX")) {
             if (n_args != 1)
-                return FALSE;
+                goto invalid;
             _rsvg_cairo_matrix_init_shear (&affine, args[0]);
             cairo_matrix_multiply (dst, &affine, dst);
         } else if (!strcmp (keyword, "skewY")) {
             if (n_args != 1)
-                return FALSE;
+                goto invalid;
             _rsvg_cairo_matrix_init_shear (&affine, args[0]);
             /* transpose the affine, given that we know [1] is zero */
             affine.yx = affine.xy;
             affine.xy = 0.;
             cairo_matrix_multiply (dst, &affine, dst);
         } else
-            return FALSE;       /* unknown keyword */
+            goto invalid;       /* unknown keyword */
     }
+
+    inverse = *dst;
+    if (cairo_matrix_invert (&inverse) != CAIRO_STATUS_SUCCESS)
+        goto invalid;
+
     return TRUE;
+
+invalid:
+
+    cairo_matrix_init_identity (dst);
+    return FALSE;
 }
 
 /**
diff --git a/rsvg.c b/rsvg.c
index 31c4baf..e1d85c7 100644
--- a/rsvg.c
+++ b/rsvg.c
@@ -71,7 +71,7 @@ rsvg_handle_get_pixbuf_sub (RsvgHandle * handle, const char *id)
 
     g_return_val_if_fail (handle != NULL, NULL);
 
-    if (!handle->priv->finished)
+    if (handle->priv->state != RSVG_HANDLE_STATE_CLOSED_OK)
         return NULL;
 
     rsvg_handle_get_dimensions (handle, &dimensions);
