diff --git a/INSTALL b/INSTALL
index 54caf7c..2099840 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,13 +1,25 @@
-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
-Foundation, Inc.
+Installation Instructions
+*************************
 
-   This file is free documentation; the Free Software Foundation gives
-unlimited permission to copy, distribute and modify it.
+Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
+Inc.
+
+   Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without warranty of any kind.
 
 Basic Installation
 ==================
 
-   These are generic installation instructions.
+   Briefly, the shell command `./configure && make && make install'
+should configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.  Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below.  The lack of an optional feature in a given package is not
+necessarily a bug.  More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
 
    The `configure' shell script attempts to guess correct values for
 various system-dependent variables used during compilation.  It uses
@@ -20,9 +32,9 @@ debugging `configure').
 
    It can also use an optional file (typically called `config.cache'
 and enabled with `--cache-file=config.cache' or simply `-C') that saves
-the results of its tests to speed up reconfiguring.  (Caching is
+the results of its tests to speed up reconfiguring.  Caching is
 disabled by default to prevent problems with accidental use of stale
-cache files.)
+cache files.
 
    If you need to do unusual things to compile the package, please try
 to figure out how `configure' could check whether to do them, and mail
@@ -32,30 +44,37 @@ some point `config.cache' contains results you don't want to keep, you
 may remove or edit it.
 
    The file `configure.ac' (or `configure.in') is used to create
-`configure' by a program called `autoconf'.  You only need
-`configure.ac' if you want to change it or regenerate `configure' using
-a newer version of `autoconf'.
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
 
-The simplest way to compile this package is:
+   The simplest way to compile this package is:
 
   1. `cd' to the directory containing the package's source code and type
-     `./configure' to configure the package for your system.  If you're
-     using `csh' on an old version of System V, you might need to type
-     `sh ./configure' instead to prevent `csh' from trying to execute
-     `configure' itself.
+     `./configure' to configure the package for your system.
 
-     Running `configure' takes awhile.  While running, it prints some
-     messages telling which features it is checking for.
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
 
   2. Type `make' to compile the package.
 
   3. Optionally, type `make check' to run any self-tests that come with
-     the package.
+     the package, generally using the just-built uninstalled binaries.
 
   4. Type `make install' to install the programs and any data files and
-     documentation.
-
-  5. You can remove the program binaries and object files from the
+     documentation.  When installing into a prefix owned by root, it is
+     recommended that the package be configured and built as a regular
+     user, and only the `make install' phase executed with root
+     privileges.
+
+  5. Optionally, type `make installcheck' to repeat any self-tests, but
+     this time using the binaries in their final installed location.
+     This target does not install anything.  Running this target as a
+     regular user, particularly if the prior `make install' required
+     root privileges, verifies that the installation completed
+     correctly.
+
+  6. You can remove the program binaries and object files from the
      source code directory by typing `make clean'.  To also remove the
      files that `configure' created (so you can compile the package for
      a different kind of computer), type `make distclean'.  There is
@@ -64,6 +83,16 @@ The simplest way to compile this package is:
      all sorts of other programs in order to regenerate files that came
      with the distribution.
 
+  7. Often, you can also type `make uninstall' to remove the installed
+     files again.  In practice, not all packages have tested that
+     uninstallation works correctly, even though it is required by the
+     GNU Coding Standards.
+
+  8. Some packages, particularly those that use Automake, provide `make
+     distcheck', which can by used by developers to test that all other
+     targets like `make install' and `make uninstall' work correctly.
+     This target is generally not run by end users.
+
 Compilers and Options
 =====================
 
@@ -75,7 +104,7 @@ for details on some of the pertinent environment variables.
 by setting variables in the command line or in the environment.  Here
 is an example:
 
-     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
 
    *Note Defining Variables::, for more details.
 
@@ -84,44 +113,89 @@ Compiling For Multiple Architectures
 
    You can compile the package for more than one kind of computer at the
 same time, by placing the object files for each architecture in their
-own directory.  To do this, you must use a version of `make' that
-supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+own directory.  To do this, you can use GNU `make'.  `cd' to the
 directory where you want the object files and executables to go and run
 the `configure' script.  `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
+source code in the directory that `configure' is in and in `..'.  This
+is known as a "VPATH" build.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
 
-   If you have to use a `make' that does not support the `VPATH'
-variable, you have to compile the package for one architecture at a
-time in the source code directory.  After you have installed the
-package for one architecture, use `make distclean' before reconfiguring
-for another architecture.
+   On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor.  Like
+this:
+
+     ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+                 CPP="gcc -E" CXXCPP="g++ -E"
+
+   This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
 
 Installation Names
 ==================
 
-   By default, `make install' will install the package's files in
-`/usr/local/bin', `/usr/local/man', etc.  You can specify an
-installation prefix other than `/usr/local' by giving `configure' the
-option `--prefix=PATH'.
+   By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
 
    You can specify separate installation prefixes for
 architecture-specific files and architecture-independent files.  If you
-give `configure' the option `--exec-prefix=PATH', the package will use
-PATH as the prefix for installing programs and libraries.
-Documentation and other data files will still use the regular prefix.
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
 
    In addition, if you use an unusual directory layout you can give
-options like `--bindir=PATH' to specify different values for particular
+options like `--bindir=DIR' to specify different values for particular
 kinds of files.  Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
+you can set and what kinds of files go in them.  In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+   The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+   The first method involves providing an override variable for each
+affected directory.  For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'.  Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated.  The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+   The second method involves providing the `DESTDIR' variable.  For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names.  The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters.  On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
 
    If the package supports it, you can cause programs to be installed
 with an extra prefix or suffix on their names by giving `configure' the
 option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
 
-Optional Features
-=================
-
    Some packages pay attention to `--enable-FEATURE' options to
 `configure', where FEATURE indicates an optional part of the package.
 They may also pay attention to `--with-PACKAGE' options, where PACKAGE
@@ -134,6 +208,50 @@ find the X include and library files automatically, but if it doesn't,
 you can use the `configure' options `--x-includes=DIR' and
 `--x-libraries=DIR' to specify their locations.
 
+   Some packages offer the ability to configure how verbose the
+execution of `make' will be.  For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+   On HP-UX, the default C compiler is not ANSI C compatible.  If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+   HP-UX `make' updates targets which have the same time stamps as
+their prerequisites, which makes it generally unusable when shipped
+generated files such as `configure' are involved.  Use GNU `make'
+instead.
+
+   On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `<wchar.h>' header file.  The option `-nodtk' can be used as
+a workaround.  If GNU CC is not installed, it is therefore recommended
+to try
+
+     ./configure CC="cc"
+
+and if that doesn't work, try
+
+     ./configure CC="cc -nodtk"
+
+   On Solaris, don't put `/usr/ucb' early in your `PATH'.  This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'.  So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+   On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'.  It is recommended to use the following options:
+
+     ./configure --prefix=/boot/common
+
 Specifying the System Type
 ==========================
 
@@ -149,14 +267,15 @@ type, such as `sun4', or a canonical name which has the form:
 
 where SYSTEM can have one of these forms:
 
-     OS KERNEL-OS
+     OS
+     KERNEL-OS
 
    See the file `config.sub' for the possible values of each field.  If
 `config.sub' isn't included in this package, then this package doesn't
 need to know the machine type.
 
    If you are _building_ compiler tools for cross-compiling, you should
-use the `--target=TYPE' option to select the type of system they will
+use the option `--target=TYPE' to select the type of system they will
 produce code for.
 
    If you want to _use_ a cross compiler, that generates code for a
@@ -186,9 +305,15 @@ them in the `configure' command line, using `VAR=value'.  For example:
 
      ./configure CC=/usr/local2/bin/gcc
 
-will cause the specified gcc to be used as the C compiler (unless it is
+causes the specified `gcc' to be used as the C compiler (unless it is
 overridden in the site shell script).
 
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf limitation.  Until the limitation is lifted, you can use
+this workaround:
+
+     CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
+
 `configure' Invocation
 ======================
 
@@ -197,7 +322,14 @@ operates.
 
 `--help'
 `-h'
-     Print a summary of the options to `configure', and exit.
+     Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+     Print a summary of the options unique to this package's
+     `configure', and exit.  The `short' variant lists options used
+     only in the top level, while the `recursive' variant lists options
+     also present in any nested packages.
 
 `--version'
 `-V'
@@ -224,6 +356,15 @@ operates.
      Look for the package's source code in directory DIR.  Usually
      `configure' can determine that directory automatically.
 
+`--prefix=DIR'
+     Use DIR as the installation prefix.  *note Installation Names::
+     for more details, including other options available for fine-tuning
+     the installation locations.
+
+`--no-create'
+`-n'
+     Run the configure checks, but stop before creating any output
+     files.
+
 `configure' also accepts some other, not widely useful, options.  Run
 `configure --help' for more details.
-
diff --git a/Makefile.am b/Makefile.am
index 82dd543..205f406 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,20 +1,14 @@
-if HAVE_GTK
-target_rsvg_view = rsvg-view
-else
-target_rsvg_view =
-endif
+SUBDIRS = . gdk-pixbuf-loader tests doc
 
-SUBDIRS = . moz-plugin gdk-pixbuf-loader gtk-engine data tests doc
+NULL =
+BUILT_SOURCES =
 
 lib_LTLIBRARIES = librsvg-2.la
-bin_PROGRAMS = rsvg-convert $(target_rsvg_view)
-noinst_PROGRAMS = test-performance rsvg-dimensions
 
-man_MANS = rsvg.1
+bin_PROGRAMS = rsvg-convert
 
-if OS_WIN32
-else
-libm = -lm
+if HAVE_GTK
+bin_PROGRAMS += rsvg-view
 endif
 
 headers = \
@@ -24,18 +18,19 @@ headers = \
 enum_sources = \
 	librsvg-enum-types.h	\
 	librsvg-enum-types.c
+BUILT_SOURCES += $(enum_sources)
 
-librsvg_2_la_SOURCES = 		\
-	rsvg-affine.c		\
+librsvg_2_la_SOURCES = \
 	librsvg-features.c 	\
-	rsvg-bpath-util.c 	\
-	rsvg-bpath-util.h 	\
 	rsvg-css.c 		\
 	rsvg-css.h 		\
+	rsvg-compat.h		\
 	rsvg-defs.c 		\
 	rsvg-defs.h 		\
 	rsvg-image.c		\
 	rsvg-image.h		\
+	rsvg-io.c		\
+	rsvg-io.h		\
 	rsvg-paint-server.c 	\
 	rsvg-paint-server.h 	\
 	rsvg-path.c 		\
@@ -67,10 +62,41 @@ librsvg_2_la_SOURCES = 		\
 	rsvg-cairo-clip.c	\
 	rsvg.c			\
 	rsvg-gobject.c		\
-	rsvg-file-util.c
-
-librsvg_2_la_LDFLAGS = -version-info @VERSION_INFO@ -export-dynamic -no-undefined -export-symbols $(srcdir)/librsvg.def
-librsvg_2_la_LIBADD = $(LIBGSF_LIBS) $(LIBCROCO_LIBS) $(LIBRSVG_LIBS)
+	rsvg-file-util.c	\
+	rsvg-size-callback.c	\
+	rsvg-size-callback.h	\
+	rsvg-xml.c		\
+	rsvg-xml.h		\
+	rsvg.h			\
+	rsvg-cairo.h		\
+	librsvg-features.h 	\
+	librsvg-enum-types.h	\
+	$(NULL)
+
+librsvg_2_la_CPPFLAGS = \
+	-I$(top_srcdir) 			\
+	-I$(top_builddir) 			\
+	-DG_LOG_DOMAIN=\"librsvg\" 		\
+	-DLIBRSVG_DATADIR="\"$(datadir)\""	\
+	-DRSVG_DISABLE_DEPRECATION_WARNINGS	\
+	$(AM_CPPFLAGS)
+
+librsvg_2_la_CFLAGS = \
+	-DRSVG_COMPILATION	\
+	$(LIBRSVG_CFLAGS) 	\
+	$(AM_CFLAGS)
+
+librsvg_2_la_LDFLAGS = \
+	$(BSYMBOLIC_LDFLAG) \
+	-version-info @VERSION_INFO@ \
+	-export-dynamic \
+	-no-undefined \
+	-export-symbols-regex "^rsvg_.*" \
+	$(AM_LDFLAGS)
+
+librsvg_2_la_LIBADD = \
+	$(LIBRSVG_LIBS) 	\
+	$(LIBM)
 
 librsvgincdir = $(includedir)/librsvg-2/librsvg
 librsvginc_HEADERS = 	\
@@ -78,63 +104,76 @@ librsvginc_HEADERS = 	\
 	librsvg-features.h \
 	librsvg-enum-types.h
 
-INCLUDES =				\
-	-I$(top_srcdir) 		\
-	-I$(top_builddir) 		\
-	$(LIBGSF_CFLAGS)		\
-	$(LIBCROCO_CFLAGS)		\
-	$(LIBRSVG_CFLAGS) 		\
-	$(GTK_CFLAGS)			\
-	$(GDK_X11_CFLAGS)		\
-	-DG_LOG_DOMAIN=\"librsvg\" 	\
-	-DLIBRSVG_DATADIR="\"$(datadir)\""
-
-DEPS = $(top_builddir)/librsvg-2.la
-LDADDS =				\
-	$(LIBGSF_LIBS)			\
-	$(GLIB_LIBS)			\
-	$(LIBCROCO_LIBS)		\
-	$(LIBRSVG_LIBS) 		\
-	$(top_builddir)/librsvg-2.la
-
 bin_SCRIPTS=rsvg
 
-rsvg_convert_SOURCES=rsvg-convert.c
-rsvg_convert_LDFLAGS =
-rsvg_convert_DEPENDENCIES = $(DEPS)
-rsvg_convert_LDADD = $(LDADDS) $(libm)
+dist_man_MANS = rsvg-convert.1
+
+rsvg_convert_SOURCES = \
+	rsvg-convert.c \
+	rsvg-size-callback.c \
+	rsvg-size-callback.h \
+	$(NULL)
+
+rsvg_convert_CPPFLAGS = \
+	-I$(top_srcdir) 			\
+	-I$(top_builddir) 			\
+	-DLIBRSVG_DATADIR="\"$(datadir)\""	\
+	$(AM_CPPFLAGS)
+
+rsvg_convert_CFLAGS =\
+	$(RSVG_CONVERT_CFLAGS) \
+	$(AM_CFLAGS)
+
+rsvg_convert_LDFLAGS = $(AM_LDFLAGS)
 
-test_performance_SOURCES=test-performance.c
-test_performance_LDFLAGS =
-test_performance_DEPENDENCIES = $(DEPS)
-test_performance_LDADD = $(LDADDS) $(libm)
+rsvg_convert_LDADD = \
+	$(top_builddir)/librsvg-2.la	\
+	$(RSVG_CONVERT_LIBS) \
+	$(LIBM)
 
-rsvg_dimensions_SOURCES=rsvg-dimensions.c
-rsvg_dimensions_LDFLAGS =
-rsvg_dimensions_DEPENDENCIES = $(DEPS)
-rsvg_dimensions_LDADD = $(LDADDS) $(libm)
+rsvg_view_SOURCES = \
+	test-display.c \
+	rsvg-size-callback.h
+
+rsvg_view_CPPFLAGS = \
+	-I$(top_srcdir) 			\
+	-I$(top_builddir) 			\
+	-DLIBRSVG_DATADIR="\"$(datadir)\""	\
+	$(AM_CPPFLAGS)
+
+rsvg_view_CFLAGS =\
+	$(GTK_CFLAGS)		\
+	$(LIBRSVG_CFLAGS) 	\
+	$(AM_CFLAGS)
 
-rsvg_view_SOURCES = 		\
-	test-display.c
 rsvg_view_LDFLAGS =
-rsvg_view_DEPENDENCIES = $(DEPS)
-rsvg_view_LDADD = $(GTK_LIBS) $(GDK_X11_LIBS) $(libm) $(LDADDS)
+
+rsvg_view_LDADD = \
+	$(top_builddir)/librsvg-2.la \
+	$(GTK_LIBS)		\
+	$(LIBRSVG_LIBS)		\
+	$(GTHREAD_LIBS)		\
+	$(LIBM)
+
+if OS_WIN32
+rsvg_view_LDFLAGS += -mwindows
+endif # OS_WIN32
 
 EXTRA_DIST =				\
-	MAINTAINERS			\
+	librsvg.doap			\
 	AUTHORS				\
 	NEWS				\
 	TODO				\
 	COPYING.LIB 			\
-	librsvg.spec.in 		\
-	librsvg-2.0.pc.in 		\
-	librsvg-2.0-uninstalled.pc.in 	\
 	librsvg-zip.in 			\
-	rsvg.in				\
-	librsvg.def			\
-	$(man_MANS)			\
-	$(rsvg_view_SOURCES)		\
-	gtk-doc.make
+	gtk-doc.make			\
+	Rsvg-2.0.metadata		\
+	config.h.win32.in		\
+	config.h.win32			\
+	rsvg.symbols			\
+	glib-tap.mk			\
+	tap-driver.sh			\
+	tap-test
 
 CLEANFILES = \
 	$(enum_sources) \
@@ -146,15 +185,12 @@ CLEANFILES = \
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = librsvg-2.0.pc
 
-dist-hook: librsvg.spec
-	cp librsvg.spec $(distdir)
-
 librsvg-enum-types.h: s-enum-types-h
 	@true
 
 s-enum-types-h: $(headers) Makefile
-	( cd $(srcdir) && glib-mkenums \
-			--fhead "#ifndef __LIBRSVG_ENUM_TYPES_H__\n#define __LIBRSVG_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+	$(AM_V_GEN) ( cd $(srcdir) && $(GLIB_MKENUMS) \
+			--fhead "#if !defined (__RSVG_RSVG_H_INSIDE__) && !defined (RSVG_COMPILATION)\n#warning \"Including <librsvg/librsvg-enum-types.h> directly is deprecated.\"\n#endif\n\n#ifndef __LIBRSVG_ENUM_TYPES_H__\n#define __LIBRSVG_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
 			--fprod "/* enumerations from \"@filename@\" */\n" \
 			--vhead "GType @enum_name@_get_type (void);\n#define RSVG_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" 	\
 			--ftail "G_END_DECLS\n\n#endif /* __LIBRSVG_ENUM_TYPES_H__ */" \
@@ -167,8 +203,8 @@ librsvg-enum-types.c: s-enum-types-c librsvg-enum-types.h
 	@true
 
 s-enum-types-c: $(headers) Makefile
-	( cd $(srcdir) && glib-mkenums \
-			--fhead "#include \"librsvg-enum-types.h\"\n#include \"rsvg.h\"" \
+	$(AM_V_GEN) ( cd $(srcdir) && $(GLIB_MKENUMS) \
+			--fhead "#include \"rsvg.h\"" \
 			--fprod "\n/* enumerations from \"@filename@\" */" \
 			--vhead "GType\n@enum_name@_get_type (void)\n{\n  static GType etype = 0;\n  if (etype == 0) {\n    static const G@Type@Value values[] = {" 	\
 			--vprod "      { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
@@ -179,3 +215,14 @@ s-enum-types-c: $(headers) Makefile
 	&& echo timestamp > $(@F)
 
 DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
+
+# ChangeLog generation
+
+ChangeLog:
+	$(AM_V_GEN) if test -d $(top_srcdir)/.git; then \
+		GIT_DIR="$(top_srcdir)/.git" git log --stat > $@; \
+	fi
+
+dist: ChangeLog
+
+.PHONY: ChangeLog
diff --git a/build/win32/vs9/math.h b/build/win32/vs9/math.h
new file mode 100644
index 0000000..f4f0c89
--- /dev/null
+++ b/build/win32/vs9/math.h
@@ -0,0 +1,39 @@
+/* librsvg - SVG rendering library
+ * Copyright (C) 2015 Chun-wei Fan <fanc999@yahoo.com.tw>
+ *
+ * Author: Chun-wei Fan <fanc999@yahoo.com.tw>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <float.h>
+
+/* include the system's math.h */
+#include <../include/math.h>
+#include <glib.h>
+
+#if (_MSC_VER < 1800)
+/* it seems of the supported compilers only
+ * MSVC does not have isnan(), but it does
+ * have _isnan() which does the same as isnan()
+ */
+#ifndef __MSVC_ISNAN_FALLBACK__
+#define __MSVC_ISNAN_FALLBACK__
+static inline gboolean
+isnan (double x)
+{
+  return _isnan (x);
+}
+#endif /* __MSVC_ISNAN_FALLBACK__ */
+#endif /* _MSC_VER < 1800 */
diff --git a/configure.in b/configure.in
index 2311e9e..ed07451 100644
--- a/configure.in
+++ b/configure.in
@@ -10,6 +10,8 @@ LIBXML_REQUIRED=2.4.7
 CAIRO_REQUIRED=1.2.0
 PANGOFT2_REQUIRED=1.2.0
 PANGOCAIRO_REQUIRED=1.10.0
+CROCO_REQUIRED=0.6.1
+GSF_REQUIRED=1.6.0
 
 AC_SUBST(GDK_PIXBUF_REQUIRED)
 AC_SUBST(GLIB_REQUIRED)
@@ -86,6 +88,9 @@ PKG_CHECK_MODULES(GLIB, \
 AC_SUBST(GLIB_LIBS)
 AC_SUBST(GLIB_CFLAGS)	
 
+GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
+AC_SUBST(GLIB_MKENUMS)
+
 PKG_CHECK_MODULES(LIBRSVG, \
 	gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED \
 	glib-2.0 >= $GLIB_REQUIRED \
@@ -94,7 +99,9 @@ PKG_CHECK_MODULES(LIBRSVG, \
 	pangocairo >= $PANGOCAIRO_REQUIRED \
 	cairo >= $CAIRO_REQUIRED \
 	cairo-png >= $CAIRO_REQUIRED \
-	fontconfig >= $FONTCONFIG_REQUIRED
+	libcroco-0.6 >= $CROCO_REQUIRED \
+	libgsf-1 >= $GSF_REQUIRED \
+	fontconfig >= $FONTCONFIG_REQUIRED \
 	freetype2)
 
 test_gio=true
@@ -107,6 +114,8 @@ PKG_CHECK_EXISTS(gio-2.0 >= $GIO_REQUIRED, [
 	test_gio=false
 ])
 
+PKG_CHECK_MODULES([RSVG_CONVERT],[gio-2.0 gio-unix-2.0 gdk-pixbuf-2.0 cairo pangocairo])
+
 AC_SUBST(LIBRSVG_LIBS)
 AC_SUBST(LIBRSVG_CFLAGS)
 
@@ -148,40 +157,6 @@ AC_SUBST([LIBRSVG_HAVE_SVGZ],[$svgz_define])
 
 dnl ===========================================================================
 
-LIBCROCO_CFLAGS=""
-LIBCROCO_LIBS=""
-LIBCROCOPKG=""
-
-test_croco=true
-AC_ARG_WITH(croco,[  --with-croco  Use libcroco for CSS parsing],[
-	if test "x$withval" = "xno"; then
-		test_croco=false
-	fi
-])
-if test "x$test_croco" = "xtrue"; then
-	PKG_CHECK_MODULES(LIBCROCO,[libcroco-0.6 >= 0.6.1], test_croco=true, test_croco=false)
-fi
-
-if test "x$test_croco" = "xtrue"; then
-        croco_define=1
-	LIBCROCO_CFLAGS="$LIBCROCO_CFLAGS -DHAVE_LIBCROCO=1"
-	LIBCROCOPKG="libcroco-0.6"
-else
-        croco_define=0
-	AC_MSG_WARN([LibCroco support disabled, as requested (Use --with-croco to enable)])
-        croco_warning="
-You are building without libcroco support. LibRSVG will not be able to handle embedded CSS, as is required per the SVG specification. If you are a library vendor or distributor, you are doing the world a disservice and should strongly consider shipping libcroco. Even KSVG needs libcroco now..."
-fi
-
-AC_SUBST(LIBCROCOPKG)
-AC_SUBST(LIBCROCO_CFLAGS)
-AC_SUBST(LIBCROCO_LIBS)
-
-AC_SUBST([CSS_SUPPORTED],[$test_croco])
-AC_SUBST([LIBRSVG_HAVE_CSS],[$croco_define])
-
-dnl ===========================================================================
-
 AC_CHECK_FUNCS(strtok_r basename)
 
 dnl ===========================================================================
@@ -243,18 +218,6 @@ fi
 AC_SUBST(GDK_X11_CFLAGS)
 AC_SUBST(GDK_X11_LIBS)
 
-AC_ARG_ENABLE(gtk-theme,
-    [  --enable-gtk-theme  Enable a RSVG based GTK+ theme engine [default=auto]],,
-    enable_gtk_theme=yes)
-
-if test "x$enable_gtk_theme" = "xyes"; then
-   if test "x$have_gtk" = "xno"; then
-      enable_gtk_theme=no;
-   fi
-fi
-
-AM_CONDITIONAL(ENABLE_GTK_ENGINE, test "x$enable_gtk_theme" = "xyes")
-
 AC_PATH_PROG(QUERYLOADERS, gdk-pixbuf-query-loaders, true)
 
 AC_ARG_ENABLE(pixbuf-loader,
@@ -272,36 +235,6 @@ fi
 AM_CONDITIONAL(ENABLE_PIXBUF_LOADER, test x$enable_pixbuf_loader = xyes)
 
 ##################################################
-##################################################
-
-MOZILLA_CFLAGS=
-build_mozilla_plugin=no
-
-test_mozilla_plugin=true
-AC_ARG_ENABLE(mozilla-plugin, [  --enable-mozilla-plugin  Build a Mozilla/Firefox SVG viewing plugin [default = auto]],
-			 test_mozilla_plugin="$enableval")
-
-if test "x$test_mozilla_plugin" != "xno"; then
-   if test -z "$MOZILLA_CONFIG"; then
-      AC_PATH_PROGS(MOZILLA_CONFIG, mozilla-config firefox-config xulrunner-config, no)
-   fi
-
-   if test "x$MOZILLA_CONFIG" != "xno"; then
-      MOZILLA_CFLAGS=`$MOZILLA_CONFIG --cflags plugin java`
-   else
-	PKG_CHECK_MODULES(MOZILLA, mozilla-plugin, build_mozilla_plugin=yes, 
-			[AC_MSG_WARN([mozilla-config not found. Mozilla/Netscape plugin will not be built])])
-   fi
-
-   if test "x$MOZILLA_CFLAGS" != "x"; then
-      build_mozilla_plugin=yes
-   fi
-fi
-
-AM_CONDITIONAL(WITH_MOZILLA,[test "x$build_mozilla_plugin" = "xyes"])
-AC_SUBST(MOZILLA_CFLAGS)
-
-##################################################
 # Checks for gtk-doc and docbook-tools
 ##################################################
 gtk_doc_installed=true
@@ -339,12 +272,7 @@ librsvg-2.0-uninstalled.pc
 librsvg-zip
 rsvg
 data/Makefile
-gtk-engine/Makefile
-gtk-engine/examples/Makefile
-gtk-engine/examples/bubble/Makefile
-gtk-engine/examples/bubble/gtk-2.0/Makefile
 gdk-pixbuf-loader/Makefile
-moz-plugin/Makefile
 tests/Makefile
 tests/pdiff/Makefile
 doc/Makefile
@@ -360,17 +288,10 @@ librsvg-$LIBRSVG_VERSION
 	compiler:                       ${CC}
 	
 	Build GdkPixbuf loader:         ${enable_pixbuf_loader}
-	Build theme engine:             ${enable_gtk_theme}
-	Build Netscape plugin:          ${build_mozilla_plugin}
 	Handle svgz files:              ${test_gsf}
 	Use GIO:                        ${test_gio}
-	Use libcroco for css parsing:   ${test_croco}
 "
 
-if test "x$croco_warning" != "x"; then
-   AC_MSG_RESULT([$croco_warning]);
-fi
-
 if test "x$gsf_warning" != "x"; then
    AC_MSG_RESULT([$gsf_warning]);
 fi
diff --git a/gdk-pixbuf-loader/io-svg.c b/gdk-pixbuf-loader/io-svg.c
index 5d24c78..91bbff9 100644
--- a/gdk-pixbuf-loader/io-svg.c
+++ b/gdk-pixbuf-loader/io-svg.c
@@ -233,17 +233,13 @@ fill_info (GdkPixbufFormat *info)
                 "image/svg-xml",
                 "image/vnd.adobe.svg+xml",
                 "text/xml-svg",
-#ifdef HAVE_SVGZ
                 "image/svg+xml-compressed",
-#endif
                 NULL
         };
         static gchar *extensions[] = {
                 "svg",
-#ifdef HAVE_SVGZ
                 "svgz",
                 "svg.gz",
-#endif
                 NULL
         };
 
diff --git a/gtk-engine/Makefile.am b/gtk-engine/Makefile.am
deleted file mode 100644
index 3209476..0000000
--- a/gtk-engine/Makefile.am
+++ /dev/null
@@ -1,34 +0,0 @@
-DIST_SUBDIRS= examples
-
-if PLATFORM_WIN32
-no_undefined = -no-undefined
-endif
-
-INCLUDES = -I$(top_srcdir) $(GTK_CFLAGS) $(LIBGSF_CFLAGS) $(LIBCROCO_CFLAGS) $(LIBRSVG_CFLAGS)
-
-enginedir = $(libdir)/gtk-2.0/$(GTK_VERSION)/engines
-themedir = $(datadir)/themes
-
-if ENABLE_GTK_ENGINE
-target_rsvg_engine = libsvg.la
-SUBDIRS =
-else
-target_rsvg_engine =
-SUBDIRS =
-endif
-
-engine_LTLIBRARIES = $(target_rsvg_engine)
-
-libsvg_la_SOURCES = 		\
-	svg-draw.c		\
-	svg-main.c		\
-	svg-render.c		\
-	svg-rc-style.c		\
-	svg-rc-style.h		\
-	svg-style.h		\
-	svg.h
-
-libsvg_la_LDFLAGS =  -avoid-version -module $(no_undefined)
-libsvg_la_LIBADD = $(top_builddir)/librsvg-2.la $(GTK_LIBS) $(LIBRSVG_LIBS) $(LIBCROCO_LIBS)
-
-EXTRA_DIST = $(libsvg_la_SOURCES)
diff --git a/gtk-engine/README b/gtk-engine/README
deleted file mode 100644
index 9431f8a..0000000
--- a/gtk-engine/README
+++ /dev/null
@@ -1,33 +0,0 @@
-The code in this directory is a GTK+ theme engine based on librsvg and 
-the gdk-pixbuf theme engine. 
-
-Over time, this engine will look less like the gdk-pixbuf engine and 
-really show off the power of librsvg and the concept of using SVG as a
-toolkit theming library.
-
-The config files are meant to be compatible with the gdk-pixbuf theme
-engine, with the notable exception that the images must be SVG graphics
-and not raster graphics (i.e. PNG, JPEG, ...)
-
-Dom Lachowicz <cinamod@hotmail.com>
-September 24, 2002
-
---------------------------------------------------------------------------
-
-The code in this directory is a GTK+ theme engine based on the earlier
-pixmap theme engine.
-
-The config files are meant to be compatible, but instead of rendering
-using Imlib, it renders using GdkPixbuf.  This makes the memory
-management much more understandable, and also allows us to use
-GdkPixbuf's high quality scaling.
-
-Most of the code was reworked/rewritten in the process to make it more
-understandable and maintainable.
-
-There are lots of bugs here, a considersable number of bugs. But it's
-cleaned up a great deal from the older pixmap engine. Please don't
-make it uglier again.
-
-Owen Taylor <otaylor@redhat.com>
-6 February 2000
diff --git a/gtk-engine/examples/Makefile.am b/gtk-engine/examples/Makefile.am
deleted file mode 100644
index 0630c4c..0000000
--- a/gtk-engine/examples/Makefile.am
+++ /dev/null
@@ -1 +0,0 @@
-SUBDIRS=bubble
diff --git a/gtk-engine/examples/bubble/Makefile.am b/gtk-engine/examples/bubble/Makefile.am
deleted file mode 100644
index fbb67c8..0000000
--- a/gtk-engine/examples/bubble/Makefile.am
+++ /dev/null
@@ -1,4 +0,0 @@
-SUBDIRS=gtk-2.0
-
-themedir = $(datadir)/themes/bubble
-theme_DATA = README
diff --git a/gtk-engine/examples/bubble/README b/gtk-engine/examples/bubble/README
deleted file mode 100644
index 46cade4..0000000
--- a/gtk-engine/examples/bubble/README
+++ /dev/null
@@ -1,14 +0,0 @@
-gtk-2.0/triangle-background.png is copyright Owen Taylor, 1997
-and may be used without restriction as long as this attribution
-is reproduced.
-
-images/bc.pnm images/bc-dark.pnm images/bc-light.png are
-from the BrushedMetalClean theme; I believe Tuomas Kuosmanen
-and Carsten Haitzler had something to do with the original
-BrushedMetal artwork.
-
-This theme is truly hideous for a reason ... to demonstrate
-that alpha-compositing is going on.
-
-Owen Taylor
-19 Jan 2002x
\ No newline at end of file
diff --git a/gtk-engine/examples/bubble/gtk-2.0/Makefile.am b/gtk-engine/examples/bubble/gtk-2.0/Makefile.am
deleted file mode 100644
index 45fdbba..0000000
--- a/gtk-engine/examples/bubble/gtk-2.0/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-themedir = $(datadir)/themes/bubble/gtk-2.0
-theme_DATA = \
-	gtkrc		\
-	blue.svg	\
-	green.svg	\
-	orange.svg	\
-	purple.svg	\
-	red.svg
-
-EXTRA_DIST = $(theme_DATA)
diff --git a/gtk-engine/examples/bubble/gtk-2.0/blue.svg b/gtk-engine/examples/bubble/gtk-2.0/blue.svg
deleted file mode 100644
index 64faa7d..0000000
--- a/gtk-engine/examples/bubble/gtk-2.0/blue.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN" 
-  "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
-<svg width="266" height="244">
-  <desc>Gradients apply to leaf nodes
-  </desc>
-  <g>
-    <defs>
-      <linearGradient id="MyGradient" gradientUnits="objectBoundingBox">
-        <stop offset="0%" style="stop-color:#00F"/>
-        <stop offset="100%" style="stop-color:#6FF"/>
-      </linearGradient>
-    </defs>
-    <g style="fill:url(#MyGradient)">
-      <rect x="0" y="0" width="266" height="244"/>
-   </g>
-  </g>
-</svg>
diff --git a/gtk-engine/examples/bubble/gtk-2.0/green.svg b/gtk-engine/examples/bubble/gtk-2.0/green.svg
deleted file mode 100644
index d14176d..0000000
--- a/gtk-engine/examples/bubble/gtk-2.0/green.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN" 
-  "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
-<svg width="266" height="244">
-  <desc>Gradients apply to leaf nodes
-  </desc>
-  <g>
-    <defs>
-      <linearGradient id="MyGradient" gradientUnits="objectBoundingBox">
-        <stop offset="0%" style="stop-color:#0F0"/>
-        <stop offset="100%" style="stop-color:#F47"/>
-      </linearGradient>
-    </defs>
-    <g style="fill:url(#MyGradient)">
-      <rect x="0" y="0" width="266" height="244"/>
-   </g>
-  </g>
-</svg>
diff --git a/gtk-engine/examples/bubble/gtk-2.0/gtkrc b/gtk-engine/examples/bubble/gtk-2.0/gtkrc
deleted file mode 100644
index 4d3c46e..0000000
--- a/gtk-engine/examples/bubble/gtk-2.0/gtkrc
+++ /dev/null
@@ -1,77 +0,0 @@
-style "default"
-{
-  fg[NORMAL] = "#ffffff"
-  fg[PRELIGHT] = "#ffffff"
-
-  engine "svg" 
-    {
-    image 
-      {
-        function        = BOX
-	file            = "blue.svg"
-	border          = { 8, 8, 8, 8 }
-	stretch         = TRUE
-      }
-    }
-
-  bg_pixmap[NORMAL]      = "purple.svg"
-  bg_pixmap[PRELIGHT]    = "purple.svg"
-  bg_pixmap[ACTIVE]      = "red.svg"  
-  bg_pixmap[INSENSITIVE] = "green.svg"
-}
-
-class "GtkWidget" style "default"
-
-style "bubble-button"
-{
-  engine "svg" 
-    {
-    image 
-      {
-        function        = BOX
-	file            = "purple.svg"
-	border          = { 8, 8, 8, 8 }
-	stretch         = TRUE
-      }
-    }
-}
-
-# common default
-class "GtkButton" style "bubble-button"
-
-style "bubble-range"
-{
-  GtkRange::slider_width = 16
-  GtkRange::stepper_size = 16
-
-  engine "svg" 
-    {
-    image 
-      {
-        function        = BOX
-	file            = "red.svg"
-	border          = { 8, 8, 8, 8 }
-	stretch         = TRUE
-      }
-    }
-}
-
-# common default
-class "GtkRange" style "bubble-range"
-
-style "bubble-menuitem"
-{
-  engine "svg" 
-    {
-    image 
-      {
-        function        = BOX
-	file            = "orange.svg"
-	border          = { 8, 8, 8, 8 }
-	stretch         = TRUE
-      }
-    }
-}
-
-# common default
-class "GtkMenuItem" style "bubble-menuitem"
diff --git a/gtk-engine/examples/bubble/gtk-2.0/orange.svg b/gtk-engine/examples/bubble/gtk-2.0/orange.svg
deleted file mode 100644
index e81d96f..0000000
--- a/gtk-engine/examples/bubble/gtk-2.0/orange.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN" 
-  "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
-<svg width="266" height="244">
-  <desc>Gradients apply to leaf nodes
-  </desc>
-  <g>
-    <defs>
-      <linearGradient id="MyGradient" gradientUnits="objectBoundingBox">
-        <stop offset="0%" style="stop-color:#F60"/>
-        <stop offset="100%" style="stop-color:#FF6"/>
-      </linearGradient>
-    </defs>
-    <g style="fill:url(#MyGradient)">
-      <rect x="0" y="0" width="266" height="244"/>
-   </g>
-  </g>
-</svg>
diff --git a/gtk-engine/examples/bubble/gtk-2.0/purple.svg b/gtk-engine/examples/bubble/gtk-2.0/purple.svg
deleted file mode 100644
index a0787e7..0000000
--- a/gtk-engine/examples/bubble/gtk-2.0/purple.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN" 
-  "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
-<svg width="266" height="244">
-  <desc>Gradients apply to leaf nodes
-  </desc>
-  <g>
-    <defs>
-      <linearGradient id="MyGradient" gradientUnits="objectBoundingBox">
-        <stop offset="0%" style="stop-color:#60F"/>
-        <stop offset="100%" style="stop-color:#F6F"/>
-      </linearGradient>
-    </defs>
-    <g style="fill:url(#MyGradient)">
-      <rect x="0" y="0" width="266" height="244"/>
-   </g>
-  </g>
-</svg>
diff --git a/gtk-engine/examples/bubble/gtk-2.0/red.svg b/gtk-engine/examples/bubble/gtk-2.0/red.svg
deleted file mode 100644
index 49e3f04..0000000
--- a/gtk-engine/examples/bubble/gtk-2.0/red.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20001102//EN" 
-  "http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd">
-<svg width="266" height="244">
-  <desc>Gradients apply to leaf nodes
-  </desc>
-  <g>
-    <defs>
-      <linearGradient id="MyGradient" gradientUnits="objectBoundingBox">
-        <stop offset="0%" style="stop-color:#F00"/>
-        <stop offset="100%" style="stop-color:#F0F"/>
-      </linearGradient>
-    </defs>
-    <g style="fill:url(#MyGradient)">
-      <rect x="0" y="0" width="266" height="244"/>
-   </g>
-  </g>
-</svg>
diff --git a/gtk-engine/svg-draw.c b/gtk-engine/svg-draw.c
deleted file mode 100644
index 8c01866..0000000
--- a/gtk-engine/svg-draw.c
+++ /dev/null
@@ -1,1035 +0,0 @@
-/* GTK+ Rsvg Engine
- * Copyright (C) 1998-2000 Red Hat, Inc.
- * Copyright (C) 2002 Dom Lachowicz
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Written by Owen Taylor <otaylor@redhat.com>, based on code by
- * Carsten Haitzler <raster@rasterman.com>
- */
-
-#include <math.h>
-#include <string.h>
-
-#include "svg.h"
-#include "svg-rc-style.h"
-#include "svg-style.h"
-
-static void rsvg_style_init       (RsvgStyle      *style);
-static void rsvg_style_class_init (RsvgStyleClass *klass);
-
-static GtkStyleClass *parent_class = NULL;
-
-static ThemeImage *
-match_theme_image (GtkStyle       *style,
-		   ThemeMatchData *match_data)
-{
-  GList *tmp_list;
-
-  tmp_list = RSVG_RC_STYLE (style->rc_style)->img_list;
-  
-  while (tmp_list)
-    {
-      guint flags;
-      ThemeImage *image = tmp_list->data;
-      tmp_list = tmp_list->next;
-
-      if (match_data->function != image->match_data.function)
-	continue;
-
-      flags = match_data->flags & image->match_data.flags;
-      
-      if (flags != image->match_data.flags) /* Required components not present */
-	continue;
-
-      if ((flags & THEME_MATCH_STATE) &&
-	  match_data->state != image->match_data.state)
-	continue;
-
-      if ((flags & THEME_MATCH_SHADOW) &&
-	  match_data->shadow != image->match_data.shadow)
-	continue;
-      
-      if ((flags & THEME_MATCH_ARROW_DIRECTION) &&
-	  match_data->arrow_direction != image->match_data.arrow_direction)
-	continue;
-
-      if ((flags & THEME_MATCH_ORIENTATION) &&
-	  match_data->orientation != image->match_data.orientation)
-	continue;
-
-      if ((flags & THEME_MATCH_GAP_SIDE) &&
-	  match_data->gap_side != image->match_data.gap_side)
-	continue;
-
-      if (image->match_data.detail &&
-	  (!image->match_data.detail ||
-	   strcmp (match_data->detail, image->match_data.detail) != 0))
-      continue;
-
-      return image;
-    }
-  
-  return NULL;
-}
-
-static gboolean
-draw_simple_image(GtkStyle       *style,
-		  GdkWindow      *window,
-		  GdkRectangle   *area,
-		  GtkWidget      *widget,
-		  ThemeMatchData *match_data,
-		  gboolean        draw_center,
-		  gboolean        allow_setbg,
-		  gint            x,
-		  gint            y,
-		  gint            width,
-		  gint            height)
-{
-  ThemeImage *image;
-  gboolean setbg = FALSE;
-  
-  if ((width == -1) && (height == -1))
-    {
-      gdk_window_get_size(window, &width, &height);
-      if (allow_setbg)
-      	setbg = TRUE;
-    }
-  else if (width == -1)
-    gdk_window_get_size(window, &width, NULL);
-  else if (height == -1)
-    gdk_window_get_size(window, NULL, &height);
-
-  if (!(match_data->flags & THEME_MATCH_ORIENTATION))
-    {
-      match_data->flags |= THEME_MATCH_ORIENTATION;
-      
-      if (height > width)
-	match_data->orientation = GTK_ORIENTATION_VERTICAL;
-      else
-	match_data->orientation = GTK_ORIENTATION_HORIZONTAL;
-    }
-    
-  image = match_theme_image (style, match_data);
-  if (image)
-    {
-      if (image->background)
-	{
-	  theme_pixbuf_render (image->background,
-			       window, NULL, area,
-			       draw_center ? COMPONENT_ALL : COMPONENT_ALL | COMPONENT_CENTER,
-			       FALSE,
-			       x, y, width, height);
-	}
-      
-      if (image->overlay && draw_center)
-	theme_pixbuf_render (image->overlay,
-			     window, NULL, area, COMPONENT_ALL,
-			     TRUE, 
-			     x, y, width, height);
-
-      return TRUE;
-    }
-  else
-    return FALSE;
-}
-
-static gboolean
-draw_gap_image(GtkStyle       *style,
-	       GdkWindow      *window,
-	       GdkRectangle   *area,
-	       GtkWidget      *widget,
-	       ThemeMatchData *match_data,
-	       gboolean        draw_center,
-	       gint            x,
-	       gint            y,
-	       gint            width,
-	       gint            height,
-	       GtkPositionType gap_side,
-	       gint            gap_x,
-	       gint            gap_width)
-{
-  ThemeImage *image;
-  gboolean setbg = FALSE;
-  
-  if ((width == -1) && (height == -1))
-    {
-      gdk_window_get_size(window, &width, &height);
-      setbg = TRUE;
-    }
-  else if (width == -1)
-    gdk_window_get_size(window, &width, NULL);
-  else if (height == -1)
-    gdk_window_get_size(window, NULL, &height);
-
-  if (!(match_data->flags & THEME_MATCH_ORIENTATION))
-    {
-      match_data->flags |= THEME_MATCH_ORIENTATION;
-      
-      if (height > width)
-	match_data->orientation = GTK_ORIENTATION_VERTICAL;
-      else
-	match_data->orientation = GTK_ORIENTATION_HORIZONTAL;
-    }
-
-  match_data->flags |= THEME_MATCH_GAP_SIDE;
-  match_data->gap_side = gap_side;
-    
-  image = match_theme_image (style, match_data);
-  if (image)
-    {
-      gint thickness;
-      GdkRectangle r1 = {0,0,0,0}, r2 = {0,0,0,0}, r3 = {0,0,0,0};
-      GdkPixbuf *pixbuf = NULL;
-      guint components = COMPONENT_ALL;
-
-      if (!draw_center)
-	components |= COMPONENT_CENTER;
-
-      if (image->gap_start)
-	pixbuf = theme_pixbuf_get_pixbuf (image->gap_start, -1, -1);
-
-      switch (gap_side)
-	{
-	case GTK_POS_TOP:
-	  if (pixbuf)
-	    thickness = gdk_pixbuf_get_height (pixbuf);
-	  else
-	    thickness = style->ythickness;
-	  
-	  if (!draw_center)
-	    components |= COMPONENT_NORTH_WEST | COMPONENT_NORTH | COMPONENT_NORTH_EAST;
-
-	  r1.x      = x;
-	  r1.y      = y;
-	  r1.width  = gap_x;
-	  r1.height = thickness;
-	  r2.x      = x + gap_x;
-	  r2.y      = y;
-	  r2.width  = gap_width;
-	  r2.height = thickness;
-	  r3.x      = x + gap_x + gap_width;
-	  r3.y      = y;
-	  r3.width  = width - (gap_x + gap_width);
-	  r3.height = thickness;
-	  break;
-	  
-	case GTK_POS_BOTTOM:
-	  if (pixbuf)
-	    thickness = gdk_pixbuf_get_height (pixbuf);
-	  else
-	    thickness = style->ythickness;
-
-	  if (!draw_center)
-	    components |= COMPONENT_SOUTH_WEST | COMPONENT_SOUTH | COMPONENT_SOUTH_EAST;
-
-	  r1.x      = x;
-	  r1.y      = y + height - thickness;
-	  r1.width  = gap_x;
-	  r1.height = thickness;
-	  r2.x      = x + gap_x;
-	  r2.y      = y + height - thickness;
-	  r2.width  = gap_width;
-	  r2.height = thickness;
-	  r3.x      = x + gap_x + gap_width;
-	  r3.y      = y + height - thickness;
-	  r3.width  = width - (gap_x + gap_width);
-	  r3.height = thickness;
-	  break;
-	  
-	case GTK_POS_LEFT:
-	  if (pixbuf)
-	    thickness = gdk_pixbuf_get_width (pixbuf);
-	  else
-	    thickness = style->xthickness;
-
-	  if (!draw_center)
-	    components |= COMPONENT_NORTH_WEST | COMPONENT_WEST | COMPONENT_SOUTH_WEST;
-
-	  r1.x      = x;
-	  r1.y      = y;
-	  r1.width  = thickness;
-	  r1.height = gap_x;
-	  r2.x      = x;
-	  r2.y      = y + gap_x;
-	  r2.width  = thickness;
-	  r2.height = gap_width;
-	  r3.x      = x;
-	  r3.y      = y + gap_x + gap_width;
-	  r3.width  = thickness;
-	  r3.height = height - (gap_x + gap_width);
-	  break;
-	  
-	case GTK_POS_RIGHT:
-	  if (pixbuf)
-	    thickness = gdk_pixbuf_get_width (pixbuf);
-	  else
-	    thickness = style->xthickness;
-
-	  if (!draw_center)
-	    components |= COMPONENT_NORTH_EAST | COMPONENT_EAST | COMPONENT_SOUTH_EAST;
-
-	  r1.x      = x + width - thickness;
-	  r1.y      = y;
-	  r1.width  = thickness;
-	  r1.height = gap_x;
-	  r2.x      = x + width - thickness;
-	  r2.y      = y + gap_x;
-	  r2.width  = thickness;
-	  r2.height = gap_width;
-	  r3.x      = x + width - thickness;
-	  r3.y      = y + gap_x + gap_width;
-	  r3.width  = thickness;
-	  r3.height = height - (gap_x + gap_width);
-	  break;
-	}
-
-      if (image->background)
-	theme_pixbuf_render (image->background,
-			     window, NULL, area, components, FALSE,
-			     x, y, width, height);
-      if (image->gap_start)
-	theme_pixbuf_render (image->gap_start,
-			     window, NULL, area, COMPONENT_ALL, FALSE,
-			     r1.x, r1.y, r1.width, r1.height);
-      if (image->gap)
-	theme_pixbuf_render (image->gap,
-			     window, NULL, area, COMPONENT_ALL, FALSE,
-			     r2.x, r2.y, r2.width, r2.height);
-      if (image->gap_end)
-	theme_pixbuf_render (image->gap_end,
-			     window, NULL, area, COMPONENT_ALL, FALSE,
-			     r3.x, r3.y, r3.width, r3.height);
-
-      return TRUE;
-    }
-  else
-    return FALSE;
-}
-
-static void
-draw_hline (GtkStyle     *style,
-	    GdkWindow    *window,
-	    GtkStateType  state,
-	    GdkRectangle *area,
-	    GtkWidget    *widget,
-	    const gchar  *detail,
-	    gint          x1,
-	    gint          x2,
-	    gint          y)
-{
-  ThemeImage *image;
-  ThemeMatchData   match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_HLINE;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_ORIENTATION | THEME_MATCH_STATE;
-  match_data.state = state;
-  match_data.orientation = GTK_ORIENTATION_HORIZONTAL;
-  
-  image = match_theme_image (style, &match_data);
-  if (image)
-    {
-      if (image->background)
-	theme_pixbuf_render (image->background,
-			     window, NULL, area, COMPONENT_ALL, FALSE,
-			     x1, y, (x2 - x1) + 1, 2);
-    }
-  else
-    parent_class->draw_hline (style, window, state, area, widget, detail,
-			      x1, x2, y);
-}
-
-static void
-draw_vline (GtkStyle     *style,
-	    GdkWindow    *window,
-	    GtkStateType  state,
-	    GdkRectangle *area,
-	    GtkWidget    *widget,
-	    const gchar  *detail,
-	    gint          y1,
-	    gint          y2,
-	    gint          x)
-{
-  ThemeImage    *image;
-  ThemeMatchData match_data;
-  
-  g_return_if_fail (style != NULL);
-  g_return_if_fail (window != NULL);
-
-  match_data.function = TOKEN_D_VLINE;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_ORIENTATION | THEME_MATCH_STATE;
-  match_data.state = state;
-  match_data.orientation = GTK_ORIENTATION_VERTICAL;
-  
-  image = match_theme_image (style, &match_data);
-  if (image)
-    {
-      if (image->background)
-	theme_pixbuf_render (image->background,
-			     window, NULL, area, COMPONENT_ALL, FALSE,
-			     x, y1, 2, (y2 - y1) + 1);
-    }
-  else
-    parent_class->draw_vline (style, window, state, area, widget, detail,
-			      y1, y2, x);
-}
-
-static void
-draw_shadow(GtkStyle     *style,
-	    GdkWindow    *window,
-	    GtkStateType  state,
-	    GtkShadowType shadow,
-	    GdkRectangle *area,
-	    GtkWidget    *widget,
-	    const gchar  *detail,
-	    gint          x,
-	    gint          y,
-	    gint          width,
-	    gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_SHADOW;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-
-  if (!draw_simple_image (style, window, area, widget, &match_data, FALSE, FALSE,
-			  x, y, width, height))
-    parent_class->draw_shadow (style, window, state, shadow, area, widget, detail,
-			       x, y, width, height);
-}
-
-/* This function makes up for some brokeness in gtkrange.c
- * where we never get the full arrow of the stepper button
- * and the type of button in a single drawing function.
- *
- * It doesn't work correctly when the scrollbar is squished
- * to the point we don't have room for full-sized steppers.
- */
-static void
-reverse_engineer_stepper_box (GtkWidget    *range,
-			      GtkArrowType  arrow_type,
-			      gint         *x,
-			      gint         *y,
-			      gint         *width,
-			      gint         *height)
-{
-  gint slider_width = 14, stepper_size = 14;
-  gint box_width;
-  gint box_height;
-  
-  if (range)
-    {
-      gtk_widget_style_get (range,
-			    "slider_width", &slider_width,
-			    "stepper_size", &stepper_size,
-			    NULL);
-    }
-	
-  if (arrow_type == GTK_ARROW_UP || arrow_type == GTK_ARROW_DOWN)
-    {
-      box_width = slider_width;
-      box_height = stepper_size;
-    }
-  else
-    {
-      box_width = stepper_size;
-      box_height = slider_width;
-    }
-
-  *x = *x - (box_width - *width) / 2;
-  *y = *y - (box_height - *height) / 2;
-  *width = box_width;
-  *height = box_height;
-}
-
-static void
-draw_arrow (GtkStyle     *style,
-	    GdkWindow    *window,
-	    GtkStateType  state,
-	    GtkShadowType shadow,
-	    GdkRectangle *area,
-	    GtkWidget    *widget,
-	    const gchar  *detail,
-	    GtkArrowType  arrow_direction,
-	    gint          fill,
-	    gint          x,
-	    gint          y,
-	    gint          width,
-	    gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  if (detail &&
-      (strcmp (detail, "hscrollbar") == 0 || strcmp (detail, "vscrollbar") == 0))
-    {
-      /* This is a hack to work around the fact that scrollbar steppers are drawn
-       * as a box + arrow, so we never have
-       *
-       *   The full bounding box of the scrollbar 
-       *   The arrow direction
-       *
-       * At the same time. We simulate an extra paint function, "STEPPER", by doing
-       * nothing for the box, and then here, reverse engineering the box that
-       * was passed to draw box and using that
-       */
-      gint box_x = x;
-      gint box_y = y;
-      gint box_width = width;
-      gint box_height = height;
-
-      reverse_engineer_stepper_box (widget, arrow_direction,
-				    &box_x, &box_y, &box_width, &box_height);
-
-      match_data.function = TOKEN_D_STEPPER;
-      match_data.detail = (gchar *)detail;
-      match_data.flags = (THEME_MATCH_SHADOW | 
-			  THEME_MATCH_STATE | 
-			  THEME_MATCH_ARROW_DIRECTION);
-      match_data.shadow = shadow;
-      match_data.state = state;
-      match_data.arrow_direction = arrow_direction;
-      
-      if (draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			     box_x, box_y, box_width, box_height))
-	{
-	  /* The theme included stepper images, we're done */
-	  return;
-	}
-
-      /* Otherwise, draw the full box, and fall through to draw the arrow
-       */
-      match_data.function = TOKEN_D_BOX;
-      match_data.detail = (gchar *)detail;
-      match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-      match_data.shadow = shadow;
-      match_data.state = state;
-      
-      if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			      box_x, box_y, box_width, box_height))
-	parent_class->draw_box (style, window, state, shadow, area, widget, detail,
-				box_x, box_y, box_width, box_height);
-    }
-
-
-  match_data.function = TOKEN_D_ARROW;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = (THEME_MATCH_SHADOW | 
-		      THEME_MATCH_STATE | 
-		      THEME_MATCH_ARROW_DIRECTION);
-  match_data.shadow = shadow;
-  match_data.state = state;
-  match_data.arrow_direction = arrow_direction;
-  
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_arrow (style, window, state, shadow, area, widget, detail,
-			      arrow_direction, fill, x, y, width, height);
-}
-
-static void
-draw_diamond (GtkStyle     *style,
-	      GdkWindow    *window,
-	      GtkStateType  state,
-	      GtkShadowType shadow,
-	      GdkRectangle *area,
-	      GtkWidget    *widget,
-	      const gchar  *detail,
-	      gint          x,
-	      gint          y,
-	      gint          width,
-	      gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_DIAMOND;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-  
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_diamond (style, window, state, shadow, area, widget, detail,
-				x, y, width, height);
-}
-
-static void
-draw_string (GtkStyle * style,
-	     GdkWindow * window,
-	     GtkStateType state,
-	     GdkRectangle * area,
-	     GtkWidget * widget,
-	     const gchar *detail,
-	     gint x,
-	     gint y,
-	     const gchar * string)
-{
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  if (state == GTK_STATE_INSENSITIVE)
-    {
-      if (area)
-	{
-	  gdk_gc_set_clip_rectangle(style->white_gc, area);
-	  gdk_gc_set_clip_rectangle(style->fg_gc[state], area);
-	}
-
-      gdk_draw_string(window, gtk_style_get_font (style), style->fg_gc[state], x, y, string);
-      
-      if (area)
-	{
-	  gdk_gc_set_clip_rectangle(style->white_gc, NULL);
-	  gdk_gc_set_clip_rectangle(style->fg_gc[state], NULL);
-	}
-    }
-  else
-    {
-      gdk_gc_set_clip_rectangle(style->fg_gc[state], area);
-      gdk_draw_string(window, gtk_style_get_font (style), style->fg_gc[state], x, y, string);
-      gdk_gc_set_clip_rectangle(style->fg_gc[state], NULL);
-    }
-}
-
-static void
-draw_box (GtkStyle     *style,
-	  GdkWindow    *window,
- 	  GtkStateType  state,
- 	  GtkShadowType shadow,
- 	  GdkRectangle *area,
- 	  GtkWidget    *widget,
-	  const gchar  *detail,
-	  gint          x,
-	  gint          y,
-	  gint          width,
-	  gint          height)
-{
-  ThemeMatchData match_data;
-
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  if (detail &&
-      (strcmp (detail, "hscrollbar") == 0 || strcmp (detail, "vscrollbar") == 0))
-    {
-      /* We handle this in draw_arrow */
-      return;
-    }
-
-  match_data.function = TOKEN_D_BOX;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height)) {
-    parent_class->draw_box (style, window, state, shadow, area, widget, detail,
-			    x, y, width, height);
-  }
-}
-
-static void
-draw_flat_box (GtkStyle     *style,
-	       GdkWindow    *window,
-	       GtkStateType  state,
-	       GtkShadowType shadow,
-	       GdkRectangle *area,
-	       GtkWidget    *widget,
-	       const gchar  *detail,
-	       gint          x,
-	       gint          y,
-	       gint          width,
-	       gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_FLAT_BOX;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_flat_box (style, window, state, shadow, area, widget, detail,
-				 x, y, width, height);
-}
-
-static void
-draw_check (GtkStyle     *style,
-	    GdkWindow    *window,
-	    GtkStateType  state,
-	    GtkShadowType shadow,
-	    GdkRectangle *area,
-	    GtkWidget    *widget,
-	    const gchar  *detail,
-	    gint          x,
-	    gint          y,
-	    gint          width,
-	    gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_CHECK;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-  
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_check (style, window, state, shadow, area, widget, detail,
-			      x, y, width, height);
-}
-
-static void
-draw_option (GtkStyle      *style,
-	     GdkWindow     *window,
-	     GtkStateType  state,
-	     GtkShadowType shadow,
-	     GdkRectangle *area,
-	     GtkWidget    *widget,
-	     const gchar  *detail,
-	     gint          x,
-	     gint          y,
-	     gint          width,
-	     gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_OPTION;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-  
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_option (style, window, state, shadow, area, widget, detail,
-			       x, y, width, height);
-}
-
-static void
-draw_tab (GtkStyle     *style,
-	  GdkWindow    *window,
-	  GtkStateType  state,
-	  GtkShadowType shadow,
-	  GdkRectangle *area,
-	  GtkWidget    *widget,
-	  const gchar  *detail,
-	  gint          x,
-	  gint          y,
-	  gint          width,
-	  gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_TAB;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-  
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_tab (style, window, state, shadow, area, widget, detail,
-			    x, y, width, height);
-}
-
-static void
-draw_shadow_gap (GtkStyle       *style,
-		 GdkWindow      *window,
-		 GtkStateType    state,
-		 GtkShadowType   shadow,
-		 GdkRectangle   *area,
-		 GtkWidget      *widget,
-		 const gchar    *detail,
-		 gint            x,
-		 gint            y,
-		 gint            width,
-		 gint            height,
-		 GtkPositionType gap_side,
-		 gint            gap_x,
-		 gint            gap_width)
-{
-  ThemeMatchData match_data;
-  
-  match_data.function = TOKEN_D_SHADOW_GAP;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.flags = (THEME_MATCH_SHADOW | 
-		      THEME_MATCH_STATE | 
-		      THEME_MATCH_ORIENTATION);
-  match_data.shadow = shadow;
-  match_data.state = state;
-  
-  if (!draw_gap_image (style, window, area, widget, &match_data, FALSE,
-		       x, y, width, height, gap_side, gap_x, gap_width))
-    parent_class->draw_shadow_gap (style, window, state, shadow, area, widget, detail,
-				   x, y, width, height, gap_side, gap_x, gap_width);
-}
-
-static void
-draw_box_gap (GtkStyle       *style,
-	      GdkWindow      *window,
-	      GtkStateType    state,
-	      GtkShadowType   shadow,
-	      GdkRectangle   *area,
-	      GtkWidget      *widget,
-	      const gchar    *detail,
-	      gint            x,
-	      gint            y,
-	      gint            width,
-	      gint            height,
-	      GtkPositionType gap_side,
-	      gint            gap_x,
-	      gint            gap_width)
-{
-  ThemeMatchData match_data;
-  
-  match_data.function = TOKEN_D_BOX_GAP;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE;
-  match_data.flags = (THEME_MATCH_SHADOW | 
-		      THEME_MATCH_STATE | 
-		      THEME_MATCH_ORIENTATION);
-  match_data.shadow = shadow;
-  match_data.state = state;
-  
-  if (!draw_gap_image (style, window, area, widget, &match_data, TRUE,
-		       x, y, width, height, gap_side, gap_x, gap_width))
-    parent_class->draw_box_gap (style, window, state, shadow, area, widget, detail,
-				x, y, width, height, gap_side, gap_x, gap_width);
-}
-
-static void
-draw_extension (GtkStyle       *style,
-		GdkWindow      *window,
-		GtkStateType    state,
-		GtkShadowType   shadow,
-		GdkRectangle   *area,
-		GtkWidget      *widget,
-		const gchar    *detail,
-		gint            x,
-		gint            y,
-		gint            width,
-		gint            height,
-		GtkPositionType gap_side)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  /* Why? */
-  if (width >=0)
-    width++;
-  if (height >=0)
-    height++;
-  
-  match_data.function = TOKEN_D_EXTENSION;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = THEME_MATCH_SHADOW | THEME_MATCH_STATE | THEME_MATCH_GAP_SIDE;
-  match_data.shadow = shadow;
-  match_data.state = state;
-  match_data.gap_side = gap_side;
-
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_extension (style, window, state, shadow, area, widget, detail,
-				  x, y, width, height, gap_side);
-}
-
-static void
-draw_focus (GtkStyle     *style,
-	    GdkWindow    *window,
-	    GtkStateType  state_type,
-	    GdkRectangle *area,
-	    GtkWidget    *widget,
-	    const gchar  *detail,
-	    gint          x,
-	    gint          y,
-	    gint          width,
-	    gint          height)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_FOCUS;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = 0;
-  
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, FALSE,
-			  x, y, width, height))
-    parent_class->draw_focus (style, window, state_type, area, widget, detail,
-			      x, y, width, height);
-}
-
-static void
-draw_slider (GtkStyle      *style,
-	     GdkWindow     *window,
-	     GtkStateType   state,
-	     GtkShadowType  shadow,
-	     GdkRectangle  *area,
-	     GtkWidget     *widget,
-	     const gchar   *detail,
-	     gint           x,
-	     gint           y,
-	     gint           width,
-	     gint           height,
-	     GtkOrientation orientation)
-{
-  ThemeMatchData           match_data;
-  
-  g_return_if_fail(style != NULL);
-  g_return_if_fail(window != NULL);
-
-  match_data.function = TOKEN_D_SLIDER;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = (THEME_MATCH_SHADOW | 
-		      THEME_MATCH_STATE | 
-		      THEME_MATCH_ORIENTATION);
-  match_data.shadow = shadow;
-  match_data.state = state;
-  match_data.orientation = orientation;
-
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_slider (style, window, state, shadow, area, widget, detail,
-			       x, y, width, height, orientation);
-}
-
-
-static void
-draw_handle (GtkStyle      *style,
-	     GdkWindow     *window,
-	     GtkStateType   state,
-	     GtkShadowType  shadow,
-	     GdkRectangle  *area,
-	     GtkWidget     *widget,
-	     const gchar   *detail,
-	     gint           x,
-	     gint           y,
-	     gint           width,
-	     gint           height,
-	     GtkOrientation orientation)
-{
-  ThemeMatchData match_data;
-  
-  g_return_if_fail (style != NULL);
-  g_return_if_fail (window != NULL);
-
-  match_data.function = TOKEN_D_HANDLE;
-  match_data.detail = (gchar *)detail;
-  match_data.flags = (THEME_MATCH_SHADOW | 
-		      THEME_MATCH_STATE | 
-		      THEME_MATCH_ORIENTATION);
-  match_data.shadow = shadow;
-  match_data.state = state;
-  match_data.orientation = orientation;
-
-  if (!draw_simple_image (style, window, area, widget, &match_data, TRUE, TRUE,
-			  x, y, width, height))
-    parent_class->draw_handle (style, window, state, shadow, area, widget, detail,
-			       x, y, width, height, orientation);
-}
-
-GType rsvg_type_style = 0;
-
-void
-rsvg_style_register_type (GTypeModule *module)
-{
-  const GTypeInfo object_info =
-  {
-    sizeof (RsvgStyleClass),
-    (GBaseInitFunc) NULL,
-    (GBaseFinalizeFunc) NULL,
-    (GClassInitFunc) rsvg_style_class_init,
-    NULL,           /* class_finalize */
-    NULL,           /* class_data */
-    sizeof (RsvgStyle),
-    0,              /* n_preallocs */
-    (GInstanceInitFunc) rsvg_style_init,
-  };
-  
-  rsvg_type_style = g_type_module_register_type (module,
-						   GTK_TYPE_STYLE,
-						   "RsvgStyle",
-						   &object_info, 0);
-}
-
-static void
-rsvg_style_init (RsvgStyle *style)
-{
-}
-
-static void
-rsvg_style_class_init (RsvgStyleClass *klass)
-{
-  GtkStyleClass *style_class = GTK_STYLE_CLASS (klass);
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  style_class->draw_hline = draw_hline;
-  style_class->draw_vline = draw_vline;
-  style_class->draw_shadow = draw_shadow;
-  style_class->draw_arrow = draw_arrow;
-  style_class->draw_diamond = draw_diamond;
-  style_class->draw_string = draw_string;
-  style_class->draw_box = draw_box;
-  style_class->draw_flat_box = draw_flat_box;
-  style_class->draw_check = draw_check;
-  style_class->draw_option = draw_option;
-  style_class->draw_tab = draw_tab;
-  style_class->draw_shadow_gap = draw_shadow_gap;
-  style_class->draw_box_gap = draw_box_gap;
-  style_class->draw_extension = draw_extension;
-  style_class->draw_focus = draw_focus;
-  style_class->draw_slider = draw_slider;
-  style_class->draw_handle = draw_handle;
-}
diff --git a/gtk-engine/svg-main.c b/gtk-engine/svg-main.c
deleted file mode 100644
index 7ca4ae3..0000000
--- a/gtk-engine/svg-main.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/* GTK+ Rsvg Engine
- * Copyright (C) 1998-2000 Red Hat, Inc.
- * Copyright (C) 2002 Dom Lachowicz
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Written by Owen Taylor <otaylor@redhat.com>, based on code by
- * Carsten Haitzler <raster@rasterman.com>
- */
-
-#include "svg.h"
-#include "svg-style.h"
-#include "svg-rc-style.h"
-#include "rsvg-private.h"
-#include <gmodule.h>
-
-G_MODULE_EXPORT const gchar* g_module_check_init (GModule *module);
-G_MODULE_EXPORT void theme_init (GTypeModule *module);
-G_MODULE_EXPORT void theme_exit (void);
-G_MODULE_EXPORT GtkRcStyle * theme_create_rc_style (void);
-
-void
-theme_init (GTypeModule *module)
-{
-  _rsvg_register_types (module); /* HACK to get around bugs 357406 and 362217 */
-  rsvg_rc_style_register_type (module);
-  rsvg_style_register_type (module);
-}
-
-void
-theme_exit (void)
-{
-}
-
-GtkRcStyle *
-theme_create_rc_style (void)
-{
-  return GTK_RC_STYLE (g_object_new (RSVG_TYPE_RC_STYLE, NULL));  
-}
-
-/* The following function will be called by GTK+ when the module
- * is loaded and checks to see if we are compatible with the
- * version of GTK+ that loads us.
- */
-const gchar*
-g_module_check_init (GModule *module)
-{
-  return gtk_check_version (GTK_MAJOR_VERSION,
-			    GTK_MINOR_VERSION,
-			    GTK_MICRO_VERSION - GTK_INTERFACE_AGE);
-}
diff --git a/gtk-engine/svg-rc-style.c b/gtk-engine/svg-rc-style.c
deleted file mode 100644
index f8441fb..0000000
--- a/gtk-engine/svg-rc-style.c
+++ /dev/null
@@ -1,814 +0,0 @@
-/* GTK+ Rsvg Engine
- * Copyright (C) 1998-2000 Red Hat, Inc.
- * Copyright (C) 2002 Dom Lachowicz
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Written by Owen Taylor <otaylor@redhat.com>, based on code by
- * Carsten Haitzler <raster@rasterman.com>
- */
-
-#include "svg.h"
-#include "svg-style.h"
-#include "svg-rc-style.h"
-
-static void      rsvg_rc_style_init         (RsvgRcStyle      *style);
-static void      rsvg_rc_style_class_init   (RsvgRcStyleClass *klass);
-static void      rsvg_rc_style_finalize     (GObject            *object);
-static guint     rsvg_rc_style_parse        (GtkRcStyle         *rc_style,
-					       GtkSettings  *settings,
-					       GScanner           *scanner);
-static void      rsvg_rc_style_merge        (GtkRcStyle         *dest,
-					       GtkRcStyle         *src);
-static GtkStyle *rsvg_rc_style_create_style (GtkRcStyle         *rc_style);
-
-static void theme_image_unref (ThemeImage *data);
-
-static const struct
-  {
-    gchar              *name;
-    guint               token;
-  }
-theme_symbols[] =
-{
-  { "image", 		TOKEN_IMAGE  },
-  { "function", 	TOKEN_FUNCTION },
-  { "file", 		TOKEN_FILE },
-  { "stretch", 		TOKEN_STRETCH },
-  { "recolorable", 	TOKEN_RECOLORABLE },
-  { "border", 		TOKEN_BORDER },
-  { "detail", 		TOKEN_DETAIL },
-  { "state", 		TOKEN_STATE },
-  { "shadow", 		TOKEN_SHADOW },
-  { "gap_side", 	TOKEN_GAP_SIDE },
-  { "gap_file", 	TOKEN_GAP_FILE },
-  { "gap_border", 	TOKEN_GAP_BORDER },
-  { "gap_start_file", 	TOKEN_GAP_START_FILE },
-  { "gap_start_border", TOKEN_GAP_START_BORDER },
-  { "gap_end_file", 	TOKEN_GAP_END_FILE },
-  { "gap_end_border", 	TOKEN_GAP_END_BORDER },
-  { "overlay_file", 	TOKEN_OVERLAY_FILE },
-  { "overlay_border", 	TOKEN_OVERLAY_BORDER },
-  { "overlay_stretch", 	TOKEN_OVERLAY_STRETCH },
-  { "arrow_direction", 	TOKEN_ARROW_DIRECTION },
-  { "orientation", 	TOKEN_ORIENTATION },
-
-  { "HLINE", 		TOKEN_D_HLINE },
-  { "VLINE",		TOKEN_D_VLINE },
-  { "SHADOW",		TOKEN_D_SHADOW },
-  { "POLYGON",		TOKEN_D_POLYGON },
-  { "ARROW",		TOKEN_D_ARROW },
-  { "DIAMOND",		TOKEN_D_DIAMOND },
-  { "OVAL",		TOKEN_D_OVAL },
-  { "STRING",		TOKEN_D_STRING },
-  { "BOX",		TOKEN_D_BOX },
-  { "FLAT_BOX",		TOKEN_D_FLAT_BOX },
-  { "CHECK",		TOKEN_D_CHECK },
-  { "OPTION",		TOKEN_D_OPTION },
-  { "CROSS",		TOKEN_D_CROSS },
-  { "RAMP",		TOKEN_D_RAMP },
-  { "TAB",		TOKEN_D_TAB },
-  { "SHADOW_GAP",	TOKEN_D_SHADOW_GAP },
-  { "BOX_GAP",		TOKEN_D_BOX_GAP },
-  { "EXTENSION",	TOKEN_D_EXTENSION },
-  { "FOCUS",		TOKEN_D_FOCUS },
-  { "SLIDER",		TOKEN_D_SLIDER },
-  { "ENTRY",		TOKEN_D_ENTRY },
-  { "HANDLE",		TOKEN_D_HANDLE },
-  { "STEPPER",		TOKEN_D_STEPPER },
-
-  { "TRUE",		TOKEN_TRUE },
-  { "FALSE",		TOKEN_FALSE },
-
-  { "TOP",		TOKEN_TOP },
-  { "UP",		TOKEN_UP },
-  { "BOTTOM",		TOKEN_BOTTOM },
-  { "DOWN",		TOKEN_DOWN },
-  { "LEFT",		TOKEN_LEFT },
-  { "RIGHT",		TOKEN_RIGHT },
-
-  { "NORMAL",		TOKEN_NORMAL },
-  { "ACTIVE",		TOKEN_ACTIVE },
-  { "PRELIGHT",		TOKEN_PRELIGHT },
-  { "SELECTED",		TOKEN_SELECTED },
-  { "INSENSITIVE",	TOKEN_INSENSITIVE },
-
-  { "NONE",		TOKEN_NONE },
-  { "IN",		TOKEN_IN },
-  { "OUT",		TOKEN_OUT },
-  { "ETCHED_IN",	TOKEN_ETCHED_IN },
-  { "ETCHED_OUT",	TOKEN_ETCHED_OUT },
-  { "HORIZONTAL",	TOKEN_HORIZONTAL },
-  { "VERTICAL",		TOKEN_VERTICAL },
-};
-
-static GtkRcStyleClass *parent_class;
-
-GType rsvg_type_rc_style = 0;
-
-void
-rsvg_rc_style_register_type (GTypeModule *module)
-{
-  const GTypeInfo object_info =
-  {
-    sizeof (RsvgRcStyleClass),
-    (GBaseInitFunc) NULL,
-    (GBaseFinalizeFunc) NULL,
-    (GClassInitFunc) rsvg_rc_style_class_init,
-    NULL,           /* class_finalize */
-    NULL,           /* class_data */
-    sizeof (RsvgRcStyle),
-    0,              /* n_preallocs */
-    (GInstanceInitFunc) rsvg_rc_style_init,
-  };
-  
-  rsvg_type_rc_style = g_type_module_register_type (module,
-						      GTK_TYPE_RC_STYLE,
-						      "RsvgRcStyle",
-						      &object_info, 0);
-}
-
-static void
-rsvg_rc_style_init (RsvgRcStyle *style)
-{
-}
-
-static void
-rsvg_rc_style_class_init (RsvgRcStyleClass *klass)
-{
-  GtkRcStyleClass *rc_style_class = GTK_RC_STYLE_CLASS (klass);
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  parent_class = g_type_class_peek_parent (klass);
-
-  rc_style_class->parse = rsvg_rc_style_parse;
-  rc_style_class->merge = rsvg_rc_style_merge;
-  rc_style_class->create_style = rsvg_rc_style_create_style;
-  
-  object_class->finalize = rsvg_rc_style_finalize;
-}
-
-static void
-rsvg_rc_style_finalize (GObject *object)
-{
-  RsvgRcStyle *rc_style = RSVG_RC_STYLE (object);
-  
-  g_list_foreach (rc_style->img_list, (GFunc) theme_image_unref, NULL);
-  g_list_free (rc_style->img_list);
-
-  G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static guint
-theme_parse_file(GtkSettings  *settings,
-		 GScanner     *scanner,
-		 ThemePixbuf **theme_pb)
-{
-  guint token;
-  gchar *pixmap;
-
-  /* Skip 'blah_file' */
-  token = g_scanner_get_next_token(scanner);
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_STRING)
-    return G_TOKEN_STRING;
-
-  if (!*theme_pb)
-    *theme_pb = theme_pixbuf_new ();
-
-  pixmap = gtk_rc_find_pixmap_in_path(settings, scanner, scanner->value.v_string);
-  if (pixmap)
-    {
-      theme_pixbuf_set_filename (*theme_pb, pixmap);
-      g_free (pixmap);
-    }
-
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_border (GScanner     *scanner,
-		    ThemePixbuf **theme_pb)
-{
-  guint               token;
-  gint left, right, top, bottom;
-
-  /* Skip 'blah_border' */
-  token = g_scanner_get_next_token(scanner);
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_LEFT_CURLY)
-    return G_TOKEN_LEFT_CURLY;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_INT)
-    return G_TOKEN_INT;
-  left = scanner->value.v_int;
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_COMMA)
-    return G_TOKEN_COMMA;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_INT)
-    return G_TOKEN_INT;
-  right = scanner->value.v_int;
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_COMMA)
-    return G_TOKEN_COMMA;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_INT)
-    return G_TOKEN_INT;
-  top = scanner->value.v_int;
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_COMMA)
-    return G_TOKEN_COMMA;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_INT)
-    return G_TOKEN_INT;
-  bottom = scanner->value.v_int;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_RIGHT_CURLY)
-    return G_TOKEN_RIGHT_CURLY;
-
-  if (!*theme_pb)
-    *theme_pb = theme_pixbuf_new ();
-  
-  theme_pixbuf_set_border (*theme_pb, left, right, top, bottom);
-  
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_stretch(GScanner     *scanner,
-		    ThemePixbuf **theme_pb)
-{
-  guint token;
-  gboolean stretch;
-
-  /* Skip 'blah_stretch' */
-  token = g_scanner_get_next_token(scanner);
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token == TOKEN_TRUE)
-    stretch = TRUE;
-  else if (token == TOKEN_FALSE)
-    stretch = FALSE;
-  else
-    return TOKEN_TRUE;
-
-  if (!*theme_pb)
-    *theme_pb = theme_pixbuf_new ();
-  
-  theme_pixbuf_set_stretch (*theme_pb, stretch);
-  
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_recolorable(GScanner * scanner,
-			ThemeImage * data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_RECOLORABLE)
-    return TOKEN_RECOLORABLE;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token == TOKEN_TRUE)
-    data->recolorable = 1;
-  else if (token == TOKEN_FALSE)
-    data->recolorable = 0;
-  else
-    return TOKEN_TRUE;
-
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_function(GScanner * scanner,
-		     ThemeImage *data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_FUNCTION)
-    return TOKEN_FUNCTION;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if ((token >= TOKEN_D_HLINE) && (token <= TOKEN_D_STEPPER))
-    data->match_data.function = token;
-
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_detail(GScanner * scanner,
-		   ThemeImage * data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_DETAIL)
-    return TOKEN_DETAIL;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_STRING)
-    return G_TOKEN_STRING;
-
-  if (data->match_data.detail)
-    g_free (data->match_data.detail);
-  
-  data->match_data.detail = g_strdup(scanner->value.v_string);
-
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_state(GScanner * scanner,
-		  ThemeImage * data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_STATE)
-    return TOKEN_STATE;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token == TOKEN_NORMAL)
-    data->match_data.state = GTK_STATE_NORMAL;
-  else if (token == TOKEN_ACTIVE)
-    data->match_data.state = GTK_STATE_ACTIVE;
-  else if (token == TOKEN_PRELIGHT)
-    data->match_data.state = GTK_STATE_PRELIGHT;
-  else if (token == TOKEN_SELECTED)
-    data->match_data.state = GTK_STATE_SELECTED;
-  else if (token == TOKEN_INSENSITIVE)
-    data->match_data.state = GTK_STATE_INSENSITIVE;
-  else
-    return TOKEN_NORMAL;
-
-  data->match_data.flags |= THEME_MATCH_STATE;
-  
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_shadow(GScanner * scanner,
-		   ThemeImage * data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_SHADOW)
-    return TOKEN_SHADOW;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token == TOKEN_NONE)
-    data->match_data.shadow = GTK_SHADOW_NONE;
-  else if (token == TOKEN_IN)
-    data->match_data.shadow = GTK_SHADOW_IN;
-  else if (token == TOKEN_OUT)
-    data->match_data.shadow = GTK_SHADOW_OUT;
-  else if (token == TOKEN_ETCHED_IN)
-    data->match_data.shadow = GTK_SHADOW_ETCHED_IN;
-  else if (token == TOKEN_ETCHED_OUT)
-    data->match_data.shadow = GTK_SHADOW_ETCHED_OUT;
-  else
-    return TOKEN_NONE;
-
-  data->match_data.flags |= THEME_MATCH_SHADOW;
-  
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_arrow_direction(GScanner * scanner,
-			    ThemeImage * data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_ARROW_DIRECTION)
-    return TOKEN_ARROW_DIRECTION;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token == TOKEN_UP)
-    data->match_data.arrow_direction = GTK_ARROW_UP;
-  else if (token == TOKEN_DOWN)
-    data->match_data.arrow_direction = GTK_ARROW_DOWN;
-  else if (token == TOKEN_LEFT)
-    data->match_data.arrow_direction = GTK_ARROW_LEFT;
-  else if (token == TOKEN_RIGHT)
-    data->match_data.arrow_direction = GTK_ARROW_RIGHT;
-  else
-    return TOKEN_UP;
-
-  data->match_data.flags |= THEME_MATCH_ARROW_DIRECTION;
-  
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_gap_side(GScanner * scanner,
-		     ThemeImage * data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_GAP_SIDE)
-    return TOKEN_GAP_SIDE;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-
-  if (token == TOKEN_TOP)
-    data->match_data.gap_side = GTK_POS_TOP;
-  else if (token == TOKEN_BOTTOM)
-    data->match_data.gap_side = GTK_POS_BOTTOM;
-  else if (token == TOKEN_LEFT)
-    data->match_data.gap_side = GTK_POS_LEFT;
-  else if (token == TOKEN_RIGHT)
-    data->match_data.gap_side = GTK_POS_RIGHT;
-  else
-    return TOKEN_TOP;
-
-  data->match_data.flags |= THEME_MATCH_GAP_SIDE;
-  
-  return G_TOKEN_NONE;
-}
-
-static guint
-theme_parse_orientation(GScanner * scanner,
-			ThemeImage * data)
-{
-  guint               token;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_ORIENTATION)
-    return TOKEN_ORIENTATION;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_EQUAL_SIGN)
-    return G_TOKEN_EQUAL_SIGN;
-
-  token = g_scanner_get_next_token(scanner);
-
-  if (token == TOKEN_HORIZONTAL)
-    data->match_data.orientation = GTK_ORIENTATION_HORIZONTAL;
-  else if (token == TOKEN_VERTICAL)
-    data->match_data.orientation = GTK_ORIENTATION_VERTICAL;
-  else
-    return TOKEN_HORIZONTAL;
-
-  data->match_data.flags |= THEME_MATCH_ORIENTATION;
-  
-  return G_TOKEN_NONE;
-}
-
-static void
-theme_image_ref (ThemeImage *data)
-{
-  data->refcount++;
-}
-
-static void
-theme_image_unref (ThemeImage *data)
-{
-  data->refcount--;
-  if (data->refcount == 0)
-    {
-      if (data->match_data.detail)
-	g_free (data->match_data.detail);
-      if (data->background)
-	theme_pixbuf_destroy (data->background);
-      if (data->overlay)
-	theme_pixbuf_destroy (data->overlay);
-      if (data->gap_start)
-	theme_pixbuf_destroy (data->gap_start);
-      if (data->gap)
-	theme_pixbuf_destroy (data->gap);
-      if (data->gap_end)
-	theme_pixbuf_destroy (data->gap_end);
-      g_free (data);
-    }
-}
-
-static guint
-theme_parse_image(GtkSettings  *settings,
-		  GScanner      *scanner,
-		  RsvgRcStyle *rsvg_style,
-		  ThemeImage   **data_return)
-{
-  guint               token;
-  ThemeImage *data;
-
-  data = NULL;
-  token = g_scanner_get_next_token(scanner);
-  if (token != TOKEN_IMAGE)
-    return TOKEN_IMAGE;
-
-  token = g_scanner_get_next_token(scanner);
-  if (token != G_TOKEN_LEFT_CURLY)
-    return G_TOKEN_LEFT_CURLY;
-
-  data = g_malloc(sizeof(ThemeImage));
-
-  data->refcount = 1;
-
-  data->background = NULL;
-  data->overlay = NULL;
-  data->gap_start = NULL;
-  data->gap = NULL;
-  data->gap_end = NULL;
-
-  data->recolorable = FALSE;
-
-  data->match_data.function = 0;
-  data->match_data.detail = NULL;
-  data->match_data.flags = 0;
-
-  token = g_scanner_peek_next_token(scanner);
-  while (token != G_TOKEN_RIGHT_CURLY)
-    {
-      switch (token)
-	{
-	case TOKEN_FUNCTION:
-	  token = theme_parse_function(scanner, data);
-	  break;
-	case TOKEN_RECOLORABLE:
-	  token = theme_parse_recolorable(scanner, data);
-	  break;
-	case TOKEN_DETAIL:
-	  token = theme_parse_detail(scanner, data);
-	  break;
-	case TOKEN_STATE:
-	  token = theme_parse_state(scanner, data);
-	  break;
-	case TOKEN_SHADOW:
-	  token = theme_parse_shadow(scanner, data);
-	  break;
-	case TOKEN_GAP_SIDE:
-	  token = theme_parse_gap_side(scanner, data);
-	  break;
-	case TOKEN_ARROW_DIRECTION:
-	  token = theme_parse_arrow_direction(scanner, data);
-	  break;
-	case TOKEN_ORIENTATION:
-	  token = theme_parse_orientation(scanner, data);
-	  break;
-	case TOKEN_FILE:
-	  token = theme_parse_file(settings, scanner, &data->background);
-	  break;
-	case TOKEN_BORDER:
-	  token = theme_parse_border(scanner, &data->background);
-	  break;
-	case TOKEN_STRETCH:
-	  token = theme_parse_stretch(scanner, &data->background);
-	  break;
-	case TOKEN_GAP_FILE:
-	  token = theme_parse_file(settings, scanner, &data->gap);
-	  break;
-	case TOKEN_GAP_BORDER:
-	  token = theme_parse_border(scanner, &data->gap);
-	  break;
-	case TOKEN_GAP_START_FILE:
-	  token = theme_parse_file(settings, scanner, &data->gap_start);
-	  break;
-	case TOKEN_GAP_START_BORDER:
-	  token = theme_parse_border(scanner, &data->gap_start);
-	  break;
-	case TOKEN_GAP_END_FILE:
-	  token = theme_parse_file(settings, scanner, &data->gap_end);
-	  break;
-	case TOKEN_GAP_END_BORDER:
-	  token = theme_parse_border(scanner, &data->gap_end);
-	  break;
-	case TOKEN_OVERLAY_FILE:
-	  token = theme_parse_file(settings, scanner, &data->overlay);
-	  break;
-	case TOKEN_OVERLAY_BORDER:
-	  token = theme_parse_border(scanner, &data->overlay);
-	  break;
-	case TOKEN_OVERLAY_STRETCH:
-	  token = theme_parse_stretch(scanner, &data->overlay);
-	  break;
-	default:
-	  g_scanner_get_next_token(scanner);
-	  token = G_TOKEN_RIGHT_CURLY;
-	  break;
-	}
-      if (token != G_TOKEN_NONE)
-	{
-	  /* error - cleanup for exit */
-	  theme_image_unref (data);
-	  *data_return = NULL;
-	  return token;
-	}
-      token = g_scanner_peek_next_token(scanner);
-    }
-
-  token = g_scanner_get_next_token(scanner);
-
-  if (data->background && !data->background->filename)
-    {
-      g_scanner_warn (scanner, "Background image options specified without filename");
-      theme_pixbuf_destroy (data->background);
-      data->background = NULL;
-    }
-
-  if (data->overlay && !data->overlay->filename)
-    {
-      g_scanner_warn (scanner, "Overlay image options specified without filename");
-      theme_pixbuf_destroy (data->overlay);
-      data->overlay = NULL;
-    }
-
-  if (token != G_TOKEN_RIGHT_CURLY)
-    {
-      /* error - cleanup for exit */
-      theme_image_unref (data);
-      *data_return = NULL;
-      return G_TOKEN_RIGHT_CURLY;
-    }
-
-  /* everything is fine now - insert yer cruft */
-  *data_return = data;
-  return G_TOKEN_NONE;
-}
-
-static guint
-rsvg_rc_style_parse (GtkRcStyle *rc_style,
-		       GtkSettings  *settings,
-		       GScanner   *scanner)
-		     
-{
-  static GQuark scope_id = 0;
-  RsvgRcStyle *rsvg_style = RSVG_RC_STYLE (rc_style);
-
-  guint old_scope;
-  guint token;
-  guint i;
-  ThemeImage *img;
-  
-  /* Set up a new scope in this scanner. */
-
-  if (!scope_id)
-    scope_id = g_quark_from_string("rsvg_theme_engine");
-
-  /* If we bail out due to errors, we *don't* reset the scope, so the
-   * error messaging code can make sense of our tokens.
-   */
-  old_scope = g_scanner_set_scope(scanner, scope_id);
-
-  /* Now check if we already added our symbols to this scope
-   * (in some previous call to theme_parse_rc_style for the
-   * same scanner.
-   */
-
-  if (!g_scanner_lookup_symbol(scanner, theme_symbols[0].name))
-    {
-      for (i = 0; i < G_N_ELEMENTS (theme_symbols); i++)
-	g_scanner_scope_add_symbol(scanner, scope_id,
-				   theme_symbols[i].name,
-				   GINT_TO_POINTER(theme_symbols[i].token));
-    }
-
-  /* We're ready to go, now parse the top level */
-
-  token = g_scanner_peek_next_token(scanner);
-  while (token != G_TOKEN_RIGHT_CURLY)
-    {
-      switch (token)
-	{
-	case TOKEN_IMAGE:
-	  img = NULL;
-	  token = theme_parse_image(settings, scanner, rsvg_style, &img);
-	  break;
-	default:
-	  g_scanner_get_next_token(scanner);
-	  token = G_TOKEN_RIGHT_CURLY;
-	  break;
-	}
-
-      if (token != G_TOKEN_NONE)
-	return token;
-      else
-	rsvg_style->img_list = g_list_append(rsvg_style->img_list, img);
-
-      token = g_scanner_peek_next_token(scanner);
-    }
-
-  g_scanner_get_next_token(scanner);
-
-  g_scanner_set_scope(scanner, old_scope);
-
-  return G_TOKEN_NONE;
-}
-
-static void
-rsvg_rc_style_merge (GtkRcStyle *dest,
-		       GtkRcStyle *src)
-{
-  if (RSVG_IS_RC_STYLE (src))
-    {
-      RsvgRcStyle *rsvg_dest = RSVG_RC_STYLE (dest);
-      RsvgRcStyle *rsvg_src = RSVG_RC_STYLE (src);
-      GList *tmp_list1, *tmp_list2;
-      
-      if (rsvg_src->img_list)
-	{
-	  /* Copy src image list and append to dest image list */
-	  
-	  tmp_list2 = g_list_last (rsvg_dest->img_list);
-	  tmp_list1 = rsvg_src->img_list;
-	  
-	  while (tmp_list1)
-	    {
-	      if (tmp_list2)
-		{
-		  tmp_list2->next = g_list_alloc();
-		  tmp_list2->next->data = tmp_list1->data;
-		  tmp_list2->next->prev = tmp_list2;
-		  
-		  tmp_list2 = tmp_list2->next;
-		}
-	      else
-		{
-		  rsvg_dest->img_list = g_list_append (NULL, tmp_list1->data);
-		  tmp_list2 = rsvg_dest->img_list;
-		}
-	      
-	      theme_image_ref (tmp_list1->data);
-	      tmp_list1 = tmp_list1->next;
-	    }
-	}
-    }
-
-  parent_class->merge (dest, src);
-}
-
-/* Create an empty style suitable to this RC style
- */
-static GtkStyle *
-rsvg_rc_style_create_style (GtkRcStyle *rc_style)
-{
-  return GTK_STYLE (g_object_new (RSVG_TYPE_STYLE, NULL));
-}
-
diff --git a/gtk-engine/svg-rc-style.h b/gtk-engine/svg-rc-style.h
deleted file mode 100644
index ea4127d..0000000
--- a/gtk-engine/svg-rc-style.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* GTK+ Rsvg Engine
- * Copyright (C) 1998-2000 Red Hat, Inc.
- * Copyright (C) 2002 Dom Lachowicz
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Written by Owen Taylor <otaylor@redhat.com>, based on code by
- * Carsten Haitzler <raster@rasterman.com>
- */
-
-#include <gtk/gtk.h>
-
-typedef struct _RsvgRcStyle RsvgRcStyle;
-typedef struct _RsvgRcStyleClass RsvgRcStyleClass;
-
-extern GType rsvg_type_rc_style;
-
-#define RSVG_TYPE_RC_STYLE              rsvg_type_rc_style
-#define RSVG_RC_STYLE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), RSVG_TYPE_RC_STYLE, RsvgRcStyle))
-#define RSVG_RC_STYLE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), RSVG_TYPE_RC_STYLE, RsvgRcStyleClass))
-#define RSVG_IS_RC_STYLE(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), RSVG_TYPE_RC_STYLE))
-#define RSVG_IS_RC_STYLE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), RSVG_TYPE_RC_STYLE))
-#define RSVG_RC_STYLE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), RSVG_TYPE_RC_STYLE, RsvgRcStyleClass))
-
-struct _RsvgRcStyle
-{
-  GtkRcStyle parent_instance;
-  
-  GList *img_list;
-};
-
-struct _RsvgRcStyleClass
-{
-  GtkRcStyleClass parent_class;
-};
-
-void rsvg_rc_style_register_type (GTypeModule *module);
diff --git a/gtk-engine/svg-render.c b/gtk-engine/svg-render.c
deleted file mode 100644
index 014ca1a..0000000
--- a/gtk-engine/svg-render.c
+++ /dev/null
@@ -1,864 +0,0 @@
-/* GTK+ Rsvg Engine
- * Copyright (C) 1998-2000 Red Hat, Inc.
- * Copyright (C) 2002 Dom Lachowicz
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Written by Owen Taylor <otaylor@redhat.com>, based on code by
- * Carsten Haitzler <raster@rasterman.com>
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "svg.h"
-
-static GCache *pixbuf_cache = NULL;
-
-static GdkPixbuf *
-bilinear_gradient (GdkPixbuf    *src,
-		   gint          src_x,
-		   gint          src_y,
-		   gint          width,
-		   gint          height)
-{
-  guint n_channels = gdk_pixbuf_get_n_channels (src);
-  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
-  guchar *src_pixels = gdk_pixbuf_get_pixels (src);
-  guchar *p1, *p2, *p3, *p4;
-  guint dest_rowstride;
-  guchar *dest_pixels;
-  GdkPixbuf *result;
-  int i, j;
-  guint k;
-
-  p1 = src_pixels + (src_y - 1) * src_rowstride + (src_x - 1) * n_channels;
-  p2 = p1 + n_channels;
-  p3 = src_pixels + src_y * src_rowstride + (src_x - 1) * n_channels;
-  p4 = p3 + n_channels;
-
-  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
-			   width, height);
-  dest_rowstride = gdk_pixbuf_get_rowstride (result);
-  dest_pixels = gdk_pixbuf_get_pixels (result);
-
-  for (i = 0; i < height; i++)
-    {
-      guchar *p = dest_pixels + dest_rowstride *i;
-      guint v[4];
-      gint dv[4];
-
-      for (k = 0; k < n_channels; k++)
-	{
-	  guint start = ((height - i) * p1[k] + (1 + i) * p3[k]) / (height + 1);
-	  guint end = ((height -  i) * p2[k] + (1 + i) * p4[k]) / (height + 1);
-
-	  dv[k] = (((gint)end - (gint)start) << 16) / (width + 1);
-	  v[k] = (start << 16) + dv[k] + 0x8000;
-	}
-
-      for (j = width; j; j--)
-	{
-	  for (k = 0; k < n_channels; k++)
-	    {
-	      *(p++) = v[k] >> 16;
-	      v[k] += dv[k];
-	    }
-	}
-    }
-
-  return result;
-}
-
-static GdkPixbuf *
-horizontal_gradient (GdkPixbuf    *src,
-		     gint          src_x,
-		     gint          src_y,
-		     gint          width,
-		     gint          height)
-{
-  guint n_channels = gdk_pixbuf_get_n_channels (src);
-  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
-  guchar *src_pixels = gdk_pixbuf_get_pixels (src);
-  guint dest_rowstride;
-  guchar *dest_pixels;
-  GdkPixbuf *result;
-  int i, j;
-  guint k;
-
-  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
-			   width, height);
-  dest_rowstride = gdk_pixbuf_get_rowstride (result);
-  dest_pixels = gdk_pixbuf_get_pixels (result);
-
-  for (i = 0; i < height; i++)
-    {
-      guchar *p = dest_pixels + dest_rowstride *i;
-      guchar *p1 = src_pixels + (src_y + i) * src_rowstride + (src_x - 1) * n_channels;
-      guchar *p2 = p1 + n_channels;
-
-      guint v[4];
-      gint dv[4];
-
-      for (k = 0; k < n_channels; k++)
-	{
-	  dv[k] = (((gint)p2[k] - (gint)p1[k]) << 16) / (width + 1);
-	  v[k] = (p1[k] << 16) + dv[k] + 0x8000;
-	}
-      
-      for (j = width; j; j--)
-	{
-	  for (k = 0; k < n_channels; k++)
-	    {
-	      *(p++) = v[k] >> 16;
-	      v[k] += dv[k];
-	    }
-	}
-    }
-
-  return result;
-}
-
-static GdkPixbuf *
-vertical_gradient (GdkPixbuf    *src,
-		   gint          src_x,
-		   gint          src_y,
-		   gint          width,
-		   gint          height)
-{
-  guint n_channels = gdk_pixbuf_get_n_channels (src);
-  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
-  guchar *src_pixels = gdk_pixbuf_get_pixels (src);
-  guchar *top_pixels, *bottom_pixels;
-  guint dest_rowstride;
-  guchar *dest_pixels;
-  GdkPixbuf *result;
-  int i, j;
-
-  top_pixels = src_pixels + (src_y - 1) * src_rowstride + (src_x) * n_channels;
-  bottom_pixels = top_pixels + src_rowstride;
-
-  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
-			   width, height);
-  dest_rowstride = gdk_pixbuf_get_rowstride (result);
-  dest_pixels = gdk_pixbuf_get_pixels (result);
-
-  for (i = 0; i < height; i++)
-    {
-      guchar *p = dest_pixels + dest_rowstride *i;
-      guchar *p1 = top_pixels;
-      guchar *p2 = bottom_pixels;
-
-      for (j = width * n_channels; j; j--)
-	*(p++) = ((height - i) * *(p1++) + (1 + i) * *(p2++)) / (height + 1);
-    }
-
-  return result;
-}
-
-static GdkPixbuf *
-replicate_single (GdkPixbuf    *src,
-		  gint          src_x,
-		  gint          src_y,
-		  gint          width,
-		  gint          height)
-{
-  guint n_channels = gdk_pixbuf_get_n_channels (src);
-  guchar *pixels = (gdk_pixbuf_get_pixels (src) +
-		    src_y * gdk_pixbuf_get_rowstride (src) +
-		    src_x * n_channels);
-  guchar r = *(pixels++);
-  guchar g = *(pixels++);
-  guchar b = *(pixels++);
-  guint dest_rowstride;
-  guchar *dest_pixels;
-  guchar a = 0;
-  GdkPixbuf *result;
-  int i, j;
-
-  if (n_channels == 4)
-    a = *(pixels++);
-
-  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
-			   width, height);
-  dest_rowstride = gdk_pixbuf_get_rowstride (result);
-  dest_pixels = gdk_pixbuf_get_pixels (result);
-  
-  for (i = 0; i < height; i++)
-    {
-      guchar *p = dest_pixels + dest_rowstride *i;
-
-      for (j = 0; j < width; j++)
-	{
-	  *(p++) = r;
-	  *(p++) = g;
-	  *(p++) = b;
-
-	  if (n_channels == 4)
-	    *(p++) = a;
-	}
-    }
-
-  return result;
-}
-
-static GdkPixbuf *
-replicate_rows (GdkPixbuf    *src,
-		gint          src_x,
-		gint          src_y,
-		gint          width,
-		gint          height)
-{
-  guint n_channels = gdk_pixbuf_get_n_channels (src);
-  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
-  guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
-  guchar *dest_pixels;
-  GdkPixbuf *result;
-  guint dest_rowstride;
-  int i;
-
-  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
-			   width, height);
-  dest_rowstride = gdk_pixbuf_get_rowstride (result);
-  dest_pixels = gdk_pixbuf_get_pixels (result);
-
-  for (i = 0; i < height; i++)
-    memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
-
-  return result;
-}
-
-static GdkPixbuf *
-replicate_cols (GdkPixbuf    *src,
-		gint          src_x,
-		gint          src_y,
-		gint          width,
-		gint          height)
-{
-  guint n_channels = gdk_pixbuf_get_n_channels (src);
-  guint src_rowstride = gdk_pixbuf_get_rowstride (src);
-  guchar *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x * n_channels);
-  guchar *dest_pixels;
-  GdkPixbuf *result;
-  guint dest_rowstride;
-  int i, j;
-
-  result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
-			   width, height);
-  dest_rowstride = gdk_pixbuf_get_rowstride (result);
-  dest_pixels = gdk_pixbuf_get_pixels (result);
-
-  for (i = 0; i < height; i++)
-    {
-      guchar *p = dest_pixels + dest_rowstride * i;
-      guchar *q = pixels + src_rowstride * i;
-
-      guchar r = *(q++);
-      guchar g = *(q++);
-      guchar b = *(q++);
-      guchar a = 0;
-      
-      if (n_channels == 4)
-	a = *(q++);
-
-      for (j = 0; j < width; j++)
-	{
-	  *(p++) = r;
-	  *(p++) = g;
-	  *(p++) = b;
-
-	  if (n_channels == 4)
-	    *(p++) = a;
-	}
-    }
-
-  return result;
-}
-
-/* Scale the rectangle (src_x, src_y, src_width, src_height)
- * onto the rectangle (dest_x, dest_y, dest_width, dest_height)
- * of the destination, clip by clip_rect and render
- */
-static void
-pixbuf_render (GdkPixbuf    *src,
-	       guint         hints,
-	       GdkWindow    *window,
-	       GdkBitmap    *mask,
-	       GdkRectangle *clip_rect,
-	       gint          src_x,
-	       gint          src_y,
-	       gint          src_width,
-	       gint          src_height,
-	       gint          dest_x,
-	       gint          dest_y,
-	       gint          dest_width,
-	       gint          dest_height)
-{
-  GdkPixbuf *tmp_pixbuf;
-  GdkRectangle rect;
-  int x_offset, y_offset;
-  gboolean has_alpha = gdk_pixbuf_get_has_alpha (src);
-  gint src_rowstride = gdk_pixbuf_get_rowstride (src);
-  gint src_n_channels = gdk_pixbuf_get_n_channels (src);
-
-  if (dest_width <= 0 || dest_height <= 0)
-    return;
-
-  rect.x = dest_x;
-  rect.y = dest_y;
-  rect.width = dest_width;
-  rect.height = dest_height;
-
-  if (hints & THEME_MISSING)
-    return;
-
-  /* FIXME: Because we use the mask to shape windows, we don't use
-   * clip_rect to clip what we draw to the mask, only to clip
-   * what we actually draw. But this leads to the horrible ineffiency
-   * of scale the whole image to get a little bit of it.
-   */
-  if (!mask && clip_rect)
-    {
-      if (!gdk_rectangle_intersect (clip_rect, &rect, &rect))
-	return;
-    }
-
-  if (dest_width == src_width && dest_height == src_height)
-    {
-      tmp_pixbuf = g_object_ref (src);
-
-      x_offset = src_x + rect.x - dest_x;
-      y_offset = src_y + rect.y - dest_y;
-    }
-  else if (src_width == 0 && src_height == 0)
-    {
-      tmp_pixbuf = bilinear_gradient (src, src_x, src_y, dest_width, dest_height);      
-      
-      x_offset = rect.x - dest_x;
-      y_offset = rect.y - dest_y;
-    }
-  else if (src_width == 0 && dest_height == src_height)
-    {
-      tmp_pixbuf = horizontal_gradient (src, src_x, src_y, dest_width, dest_height);      
-      
-      x_offset = rect.x - dest_x;
-      y_offset = rect.y - dest_y;
-    }
-  else if (src_height == 0 && dest_width == src_width)
-    {
-      tmp_pixbuf = vertical_gradient (src, src_x, src_y, dest_width, dest_height);
-      
-      x_offset = rect.x - dest_x;
-      y_offset = rect.y - dest_y;
-    }
-  else if ((hints & THEME_CONSTANT_COLS) && (hints & THEME_CONSTANT_ROWS))
-    {
-      tmp_pixbuf = replicate_single (src, src_x, src_y, dest_width, dest_height);
-
-      x_offset = rect.x - dest_x;
-      y_offset = rect.y - dest_y;
-    }
-  else if (dest_width == src_width && (hints & THEME_CONSTANT_COLS))
-    {
-      tmp_pixbuf = replicate_rows (src, src_x, src_y, dest_width, dest_height);
-
-      x_offset = rect.x - dest_x;
-      y_offset = rect.y - dest_y;
-    }
-  else if (dest_height == src_height && (hints & THEME_CONSTANT_ROWS))
-    {
-      tmp_pixbuf = replicate_cols (src, src_x, src_y, dest_width, dest_height);
-
-      x_offset = rect.x - dest_x;
-      y_offset = rect.y - dest_y;
-    }
-  else 
-    {
-      double x_scale = (double)dest_width / src_width;
-      double y_scale = (double)dest_height / src_height;
-      guchar *pixels;
-      GdkPixbuf *partial_src;
-      
-      pixels = (gdk_pixbuf_get_pixels (src)
-		+ src_y * src_rowstride
-		+ src_x * src_n_channels);
-
-      partial_src = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB,
-					      has_alpha,
-					      8, src_width, src_height,
-					      src_rowstride,
-					      NULL, NULL);
-						  
-      tmp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
-				   has_alpha, 8,
-				   rect.width, rect.height);
-
-      gdk_pixbuf_scale (partial_src, tmp_pixbuf,
-			0, 0, rect.width, rect.height,
-			dest_x - rect.x, dest_y - rect.y, 
-			x_scale, y_scale,
-			GDK_INTERP_BILINEAR);
-
-      g_object_unref (partial_src);
-
-      x_offset = 0;
-      y_offset = 0;
-    }
-
-  if (mask)
-    {
-      gdk_pixbuf_render_threshold_alpha (tmp_pixbuf, mask,
-					 x_offset, y_offset,
-					 rect.x, rect.y,
-					 rect.width, rect.height,
-					 128);
-    }
-
-  gdk_pixbuf_render_to_drawable_alpha (tmp_pixbuf, window,
-				       x_offset, y_offset,
-				       rect.x, rect.y,
-				       rect.width, rect.height,
-				       GDK_PIXBUF_ALPHA_FULL, 128,
-				       GDK_RGB_DITHER_NORMAL,
-				       0, 0);
-  g_object_unref (tmp_pixbuf);
-}
-
-ThemePixbuf *
-theme_pixbuf_new (void)
-{
-  ThemePixbuf *result = g_new0 (ThemePixbuf, 1);
-  result->filename = NULL;
-  result->handle = NULL;
-
-  result->stretch = TRUE;
-  result->border_left = 0;
-  result->border_right = 0;
-  result->border_bottom = 0;
-  result->border_top = 0;
-
-  return result;
-}
-
-void
-theme_pixbuf_destroy (ThemePixbuf *theme_pb)
-{
-  theme_pixbuf_set_filename (theme_pb, NULL);
-  g_free (theme_pb);
-}
-
-void         
-theme_pixbuf_set_filename (ThemePixbuf *theme_pb,
-			   const char  *filename)
-{
-  if (theme_pb->handle)
-    {
-      g_cache_remove (pixbuf_cache, theme_pb->handle);
-      theme_pb->handle = NULL;
-    }
-
-  if (theme_pb->filename)
-    g_free (theme_pb->filename);
-
-  if (filename)
-    theme_pb->filename = g_strdup (filename);
-  else
-    theme_pb->filename = NULL;
-}
-
-static guint
-compute_hint (GdkPixbuf *pixbuf,
-	      gint       x0,
-	      gint       x1,
-	      gint       y0,
-	      gint       y1)
-{
-  int i, j;
-  int hints = THEME_CONSTANT_ROWS | THEME_CONSTANT_COLS | THEME_MISSING;
-  int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
-  
-  guchar *data = gdk_pixbuf_get_pixels (pixbuf);
-  int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-
-  if (x0 == x1 || y0 == y1)
-    return 0;
-
-  for (i = y0; i < y1; i++)
-    {
-      guchar *p = data + i * rowstride + x0 * n_channels;
-      guchar r = p[0];
-      guchar g = p[1];
-      guchar b = p[2];
-      guchar a = 0;
-      
-      if (n_channels == 4)
-	a = p[3];
-
-      for (j = x0; j < x1 ; j++)
-	{
-	  if (n_channels != 4 || p[3] != 0)
-	    {
-	      hints &= ~THEME_MISSING;
-	      if (!(hints & THEME_CONSTANT_ROWS))
-		goto cols;
-	    }
-	  
-	  if (r != *(p++) ||
-	      g != *(p++) ||
-	      b != *(p++) ||
-	      (n_channels != 4 && a != *(p++)))
-	    {
-	      hints &= ~THEME_CONSTANT_ROWS;
-	      if (!(hints & THEME_MISSING))
-		goto cols;
-	    }
-	}
-    }
-
- cols:
-  for (i = y0 + 1; i < y1; i++)
-    {
-      guchar *base = data + y0 * rowstride + x0 * n_channels;
-      guchar *p = data + i * rowstride + x0 * n_channels;
-
-      if (memcmp (p, base, n_channels * (x1 - x0)) != 0)
-	{
-	  hints &= ~THEME_CONSTANT_COLS;
-	  return hints;
-	}
-    }
-
-  return hints;
-}
-
-static void
-theme_pixbuf_compute_hints (ThemePixbuf *theme_pb, GdkPixbuf *pixbuf)
-{
-  int i, j;
-  gint width = gdk_pixbuf_get_width (pixbuf);
-  gint height = gdk_pixbuf_get_height (pixbuf);
-
-  if (theme_pb->border_left + theme_pb->border_right > width ||
-      theme_pb->border_top + theme_pb->border_bottom > height)
-    {
-      g_warning ("Invalid borders specified for theme pixmap:\n"
-		 "        %s,\n"
-		 "borders don't fit within the image", theme_pb->filename);
-      if (theme_pb->border_left + theme_pb->border_right > width)
-	{
-	  theme_pb->border_left = width / 2;
-	  theme_pb->border_right = (width + 1) / 2;
-	}
-      if (theme_pb->border_bottom + theme_pb->border_top > height)
-	{
-	  theme_pb->border_top = height / 2;
-	  theme_pb->border_bottom = (height + 1) / 2;
-	}
-    }
-  
-  for (i = 0; i < 3; i++)
-    {
-      gint y0, y1;
-
-      switch (i)
-	{
-	case 0:
-	  y0 = 0;
-	  y1 = theme_pb->border_top;
-	  break;
-	case 1:
-	  y0 = theme_pb->border_top;
-	  y1 = height - theme_pb->border_bottom;
-	  break;
-	default:
-	  y0 = height - theme_pb->border_bottom;
-	  y1 = height;
-	  break;
-	}
-      
-      for (j = 0; j < 3; j++)
-	{
-	  gint x0, x1;
-	  
-	  switch (j)
-	    {
-	    case 0:
-	      x0 = 0;
-	      x1 = theme_pb->border_left;
-	      break;
-	    case 1:
-	      x0 = theme_pb->border_left;
-	      x1 = width - theme_pb->border_right;
-	      break;
-	    default:
-	      x0 = width - theme_pb->border_right;
-	      x1 = width;
-	      break;
-	    }
-
-	  theme_pb->hints[i][j] = compute_hint (pixbuf, x0, x1, y0, y1);
-	}
-    }
-  
-}
-
-void
-theme_pixbuf_set_border (ThemePixbuf *theme_pb,
-			 gint         left,
-			 gint         right,
-			 gint         top,
-			 gint         bottom)
-{
-  theme_pb->border_left = left;
-  theme_pb->border_right = right;
-  theme_pb->border_top = top;
-  theme_pb->border_bottom = bottom;
-}
-
-void
-theme_pixbuf_set_stretch (ThemePixbuf *theme_pb,
-			  gboolean     stretch)
-{
-  theme_pb->stretch = stretch;
-}
-
-static void
-svg_cache_value_free(gpointer foo)
-{
-  RsvgHandle * handle;
-
-  handle = (RsvgHandle *)foo;
-  if(handle != NULL)
-    g_object_unref(G_OBJECT(handle));
-}
-
-#define SVG_BUFFER_SIZE (1024*8)
-
-static RsvgHandle *
-svg_cache_value_new (gchar *filename)
-{
-  RsvgHandle *result = NULL;
-  FILE *fp;
-
-  fp = fopen(filename, "rb");
-  if(fp)
-    {
-      size_t nread;
-      unsigned char buf[SVG_BUFFER_SIZE];
-
-      result = rsvg_handle_new();
-      while((nread = fread(buf, 1, sizeof(buf), fp)) > 0)
-	rsvg_handle_write(result, buf, nread, NULL);
-
-      fclose(fp);
-      rsvg_handle_close(result, NULL);
-    }
-  else
-    {
-      g_warning("Couldn't load theme part: %s\n", filename);
-    }
-
-  return result;
-}
-
-struct SizeInfo
-{
-  gint width, height;
-};
-
-static void set_size_fn(gint *width, gint *height, gpointer foo)
-{
-  struct SizeInfo * info = (struct SizeInfo *)foo;
-
-  *width = info->width;
-  *height = info->height;
-}
-
-static GdkPixbuf *
-get_pixbuf(RsvgHandle * handle, gint width, gint height)
-{
-  GdkPixbuf * result;
-
-  if(!handle)
-    return NULL;
-
-  if(width > 0 && height > 0)
-    {
-      struct SizeInfo info;
-
-      info.width = width;
-      info.height = height;
-      
-      rsvg_handle_set_size_callback(handle, set_size_fn, &info, NULL);
-    }
-
-  result = rsvg_handle_get_pixbuf(handle);
-
-  return result;
-}
-
-GdkPixbuf *
-theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb, gint width, gint height)
-{
-  GdkPixbuf *result = NULL;
-
-  if (!theme_pb->handle)
-    {
-      if (!pixbuf_cache)
-	pixbuf_cache = g_cache_new ((GCacheNewFunc)svg_cache_value_new,
-				    (GCacheDestroyFunc)svg_cache_value_free,
-				    (GCacheDupFunc)g_strdup,
-				    (GCacheDestroyFunc)g_free,
-				    g_str_hash, g_direct_hash, g_str_equal);
-      
-      theme_pb->handle = g_cache_insert (pixbuf_cache, theme_pb->filename);
-    }
-  
-  result = get_pixbuf(theme_pb->handle, width, height);
-  if(result)
-    theme_pixbuf_compute_hints(theme_pb, result);
-  
-  return result;
-}
-
-void
-theme_pixbuf_render (ThemePixbuf  *theme_pb,
-		     GdkWindow    *window,
-		     GdkBitmap    *mask,
-		     GdkRectangle *clip_rect,
-		     guint         component_mask,
-		     gboolean      center,
-		     gint          x,
-		     gint          y,
-		     gint          width,
-		     gint          height)
-{
-  GdkPixbuf *pixbuf = theme_pixbuf_get_pixbuf (theme_pb, width, height);
-  gint src_x[4], src_y[4], dest_x[4], dest_y[4];
-  gint pixbuf_width = gdk_pixbuf_get_width (pixbuf);
-  gint pixbuf_height = gdk_pixbuf_get_height (pixbuf);
-
-  if (!pixbuf)
-    return;
-
-  if (theme_pb->stretch)
-    {
-      src_x[0] = 0;
-      src_x[1] = theme_pb->border_left;
-      src_x[2] = pixbuf_width - theme_pb->border_right;
-      src_x[3] = pixbuf_width;
-      
-      src_y[0] = 0;
-      src_y[1] = theme_pb->border_top;
-      src_y[2] = pixbuf_height - theme_pb->border_bottom;
-      src_y[3] = pixbuf_height;
-      
-      dest_x[0] = x;
-      dest_x[1] = x + theme_pb->border_left;
-      dest_x[2] = x + width - theme_pb->border_right;
-      dest_x[3] = x + width;
-
-      dest_y[0] = y;
-      dest_y[1] = y + theme_pb->border_top;
-      dest_y[2] = y + height - theme_pb->border_bottom;
-      dest_y[3] = y + height;
-
-      if (component_mask & COMPONENT_ALL)
-	component_mask = (COMPONENT_ALL - 1) & ~component_mask;
-
-#define RENDER_COMPONENT(X1,X2,Y1,Y2)					         \
-        pixbuf_render (pixbuf, theme_pb->hints[Y1][X1], window, mask, clip_rect, \
-	 	       src_x[X1], src_y[Y1],				         \
-		       src_x[X2] - src_x[X1], src_y[Y2] - src_y[Y1],	         \
-		       dest_x[X1], dest_y[Y1],				         \
-		       dest_x[X2] - dest_x[X1], dest_y[Y2] - dest_y[Y1]);
-      
-      if (component_mask & COMPONENT_NORTH_WEST)
-	RENDER_COMPONENT (0, 1, 0, 1);
-
-      if (component_mask & COMPONENT_NORTH)
-	RENDER_COMPONENT (1, 2, 0, 1);
-
-      if (component_mask & COMPONENT_NORTH_EAST)
-	RENDER_COMPONENT (2, 3, 0, 1);
-
-      if (component_mask & COMPONENT_WEST)
-	RENDER_COMPONENT (0, 1, 1, 2);
-
-      if (component_mask & COMPONENT_CENTER)
-	RENDER_COMPONENT (1, 2, 1, 2);
-
-      if (component_mask & COMPONENT_EAST)
-	RENDER_COMPONENT (2, 3, 1, 2);
-
-      if (component_mask & COMPONENT_SOUTH_WEST)
-	RENDER_COMPONENT (0, 1, 2, 3);
-
-      if (component_mask & COMPONENT_SOUTH)
-	RENDER_COMPONENT (1, 2, 2, 3);
-
-      if (component_mask & COMPONENT_SOUTH_EAST)
-	RENDER_COMPONENT (2, 3, 2, 3);
-    }
-  else
-    {
-      if (center)
-	{
-	  x += (width - pixbuf_width) / 2;
-	  y += (height - pixbuf_height) / 2;
-	  
-	  pixbuf_render (pixbuf, 0, window, NULL, clip_rect,
-			 0, 0,
-			 pixbuf_width, pixbuf_height,
-			 x, y,
-			 pixbuf_width, pixbuf_height);
-	}
-      else
-	{
-	  GdkPixmap *tmp_pixmap;
-	  GdkGC *tmp_gc;
-	  GdkGCValues gc_values;
-
-	  tmp_pixmap = gdk_pixmap_new (window,
-				       pixbuf_width,
-				       pixbuf_height,
-				       -1);
-	  tmp_gc = gdk_gc_new (tmp_pixmap);
-	  gdk_pixbuf_render_to_drawable (pixbuf, tmp_pixmap, tmp_gc,
-					 0, 0, 
-					 0, 0,
-					 pixbuf_width, pixbuf_height,
-					 GDK_RGB_DITHER_NORMAL,
-					 0, 0);
-	  gdk_gc_unref (tmp_gc);
-
-	  gc_values.fill = GDK_TILED;
-	  gc_values.tile = tmp_pixmap;
-	  tmp_gc = gdk_gc_new_with_values (window,
-					   &gc_values, GDK_GC_FILL | GDK_GC_TILE);
-	  if (clip_rect)
-	    gdk_draw_rectangle (window, tmp_gc, TRUE,
-				clip_rect->x, clip_rect->y, clip_rect->width, clip_rect->height);
-	  else
-	    gdk_draw_rectangle (window, tmp_gc, TRUE, x, y, width, height);
-	  
-	  gdk_gc_unref (tmp_gc);
-	  gdk_pixmap_unref (tmp_pixmap);
-	}
-    }
-
-  g_object_unref(G_OBJECT(pixbuf));
-}
diff --git a/gtk-engine/svg-style.h b/gtk-engine/svg-style.h
deleted file mode 100644
index e1470b0..0000000
--- a/gtk-engine/svg-style.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* GTK+ Rsvg Engine
- * Copyright (C) 1998-2000 Red Hat, Inc.
- * Copyright (C) 20002 Dom Lachowicz
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Written by Owen Taylor <otaylor@redhat.com>, based on code by
- * Carsten Haitzler <raster@rasterman.com>
- */
-
-#include <gtk/gtk.h>
-
-typedef struct _RsvgStyle RsvgStyle;
-typedef struct _RsvgStyleClass RsvgStyleClass;
-
-extern GType rsvg_type_style;
-
-#define RSVG_TYPE_STYLE              rsvg_type_style
-#define RSVG_STYLE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), RSVG_TYPE_STYLE, RsvgStyle))
-#define RSVG_STYLE_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), RSVG_TYPE_STYLE, RsvgStyleClass))
-#define RSVG_IS_STYLE(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), RSVG_TYPE_STYLE))
-#define RSVG_IS_STYLE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), RSVG_TYPE_STYLE))
-#define RSVG_STYLE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), RSVG_TYPE_STYLE, RsvgStyleClass))
-
-struct _RsvgStyle
-{
-  GtkStyle parent_instance;
-};
-
-struct _RsvgStyleClass
-{
-  GtkStyleClass parent_class;
-};
-
-void rsvg_style_register_type (GTypeModule *module);
-
-
diff --git a/gtk-engine/svg.h b/gtk-engine/svg.h
deleted file mode 100644
index bf51cd9..0000000
--- a/gtk-engine/svg.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/* GTK+ Rsvg Engine
- * Copyright (C) 1998-2000 Red Hat, Inc.
- * Copyright (C) 2002 Dom Lachowicz
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Written by Owen Taylor <otaylor@redhat.com>, based on code by
- * Carsten Haitzler <raster@rasterman.com>
- */
-
-#include <gtk/gtk.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <rsvg.h>
-
-/* internals */
-
-typedef struct _ThemeData ThemeData;
-typedef struct _ThemeImage ThemeImage;
-typedef struct _ThemeMatchData ThemeMatchData;
-typedef struct _ThemePixbuf ThemePixbuf;
-
-enum
-{
-  TOKEN_IMAGE = G_TOKEN_LAST + 1,
-  TOKEN_FUNCTION,
-  TOKEN_FILE,
-  TOKEN_STRETCH,
-  TOKEN_RECOLORABLE,
-  TOKEN_BORDER,
-  TOKEN_DETAIL,
-  TOKEN_STATE,
-  TOKEN_SHADOW,
-  TOKEN_GAP_SIDE,
-  TOKEN_GAP_FILE,
-  TOKEN_GAP_BORDER,
-  TOKEN_GAP_START_FILE,
-  TOKEN_GAP_START_BORDER,
-  TOKEN_GAP_END_FILE,
-  TOKEN_GAP_END_BORDER,
-  TOKEN_OVERLAY_FILE,
-  TOKEN_OVERLAY_BORDER,
-  TOKEN_OVERLAY_STRETCH,
-  TOKEN_ARROW_DIRECTION,
-  TOKEN_D_HLINE,
-  TOKEN_D_VLINE,
-  TOKEN_D_SHADOW,
-  TOKEN_D_POLYGON,
-  TOKEN_D_ARROW,
-  TOKEN_D_DIAMOND,
-  TOKEN_D_OVAL,
-  TOKEN_D_STRING,
-  TOKEN_D_BOX,
-  TOKEN_D_FLAT_BOX,
-  TOKEN_D_CHECK,
-  TOKEN_D_OPTION,
-  TOKEN_D_CROSS,
-  TOKEN_D_RAMP,
-  TOKEN_D_TAB,
-  TOKEN_D_SHADOW_GAP,
-  TOKEN_D_BOX_GAP,
-  TOKEN_D_EXTENSION,
-  TOKEN_D_FOCUS,
-  TOKEN_D_SLIDER,
-  TOKEN_D_ENTRY,
-  TOKEN_D_HANDLE,
-  TOKEN_D_STEPPER,
-  TOKEN_TRUE,
-  TOKEN_FALSE,
-  TOKEN_TOP,
-  TOKEN_UP,
-  TOKEN_BOTTOM,
-  TOKEN_DOWN,
-  TOKEN_LEFT,
-  TOKEN_RIGHT,
-  TOKEN_NORMAL,
-  TOKEN_ACTIVE,
-  TOKEN_PRELIGHT,
-  TOKEN_SELECTED,
-  TOKEN_INSENSITIVE,
-  TOKEN_NONE,
-  TOKEN_IN,
-  TOKEN_OUT,
-  TOKEN_ETCHED_IN,
-  TOKEN_ETCHED_OUT,
-  TOKEN_ORIENTATION,
-  TOKEN_HORIZONTAL,
-  TOKEN_VERTICAL
-};
-
-typedef enum
-{
-  COMPONENT_NORTH_WEST = 1 << 0,
-  COMPONENT_NORTH      = 1 << 1,
-  COMPONENT_NORTH_EAST = 1 << 2, 
-  COMPONENT_WEST       = 1 << 3,
-  COMPONENT_CENTER     = 1 << 4,
-  COMPONENT_EAST       = 1 << 5, 
-  COMPONENT_SOUTH_EAST = 1 << 6,
-  COMPONENT_SOUTH      = 1 << 7,
-  COMPONENT_SOUTH_WEST = 1 << 8,
-  COMPONENT_ALL 	  = 1 << 9
-} ThemePixbufComponent;
-
-typedef enum {
-  THEME_MATCH_GAP_SIDE        = 1 << 0,
-  THEME_MATCH_ORIENTATION     = 1 << 1,
-  THEME_MATCH_STATE           = 1 << 2,
-  THEME_MATCH_SHADOW          = 1 << 3,
-  THEME_MATCH_ARROW_DIRECTION = 1 << 4
-} ThemeMatchFlags;
-
-typedef enum {
-  THEME_CONSTANT_ROWS = 1 << 0,
-  THEME_CONSTANT_COLS = 1 << 1,
-  THEME_MISSING = 1 << 2
-} ThemeRenderHints;
-
-struct _ThemePixbuf
-{
-  gchar      *filename;
-  RsvgHandle *handle;
-  gboolean    stretch;
-  gint        border_left;
-  gint        border_right;
-  gint        border_bottom;
-  gint        border_top;
-  guint       hints[3][3];
-};
-
-struct _ThemeMatchData
-{
-  guint           function;	/* Mandatory */
-  gchar          *detail;
-
-  ThemeMatchFlags flags;
-
-  GtkPositionType gap_side;
-  GtkOrientation  orientation;
-  GtkStateType    state;
-  GtkShadowType   shadow;
-  GtkArrowType    arrow_direction;
-};
-
-struct _ThemeImage
-{
-  guint           refcount;
-
-  ThemePixbuf    *background;
-  ThemePixbuf    *overlay;
-  ThemePixbuf    *gap_start;
-  ThemePixbuf    *gap;
-  ThemePixbuf    *gap_end;
-  
-  gchar           recolorable;
-
-  ThemeMatchData  match_data;
-};
-
-
-ThemePixbuf *theme_pixbuf_new          (void);
-void         theme_pixbuf_destroy      (ThemePixbuf  *theme_pb);
-void         theme_pixbuf_set_filename (ThemePixbuf  *theme_pb,
-					const char   *filename);
-GdkPixbuf *  theme_pixbuf_get_pixbuf   (ThemePixbuf  *theme_pb, gint width, gint height);
-void         theme_pixbuf_set_border   (ThemePixbuf  *theme_pb,
-					gint          left,
-					gint          right,
-					gint          top,
-					gint          bottom);
-void         theme_pixbuf_set_stretch  (ThemePixbuf  *theme_pb,
-					gboolean      stretch);
-void         theme_pixbuf_render       (ThemePixbuf  *theme_pb,
-					GdkWindow    *window,
-					GdkBitmap    *mask,
-					GdkRectangle *clip_rect,
-					guint         component_mask,
-					gboolean      center,
-					gint          dest_x,
-					gint          dest_y,
-					gint          dest_width,
-					gint          dest_height);
diff --git a/librsvg-2.0-uninstalled.pc.in b/librsvg-2.0-uninstalled.pc.in
index 214ee5a..37df60d 100644
--- a/librsvg-2.0-uninstalled.pc.in
+++ b/librsvg-2.0-uninstalled.pc.in
@@ -3,7 +3,7 @@ exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
 
-svgz_supported=@SVGZ_SUPPORTED@
+svgz_supported=true
 css_supported=@CSS_SUPPORTED@
 
 Name: librsvg
diff --git a/librsvg-2.0.pc.in b/librsvg-2.0.pc.in
index 236bdef..cc8b684 100644
--- a/librsvg-2.0.pc.in
+++ b/librsvg-2.0.pc.in
@@ -3,7 +3,7 @@ exec_prefix=@exec_prefix@
 libdir=@libdir@
 includedir=@includedir@
 
-svgz_supported=@SVGZ_SUPPORTED@
+svgz_supported=true
 css_supported=@CSS_SUPPORTED@
 
 Name: librsvg
diff --git a/librsvg-features.c b/librsvg-features.c
index 621d4f0..5e083c2 100644
--- a/librsvg-features.c
+++ b/librsvg-features.c
@@ -1,7 +1,7 @@
-#include "librsvg-features.h"
+#include "rsvg.h"
 
 /* General initialization hooks */
-const unsigned int librsvg_major_version = LIBRSVG_MAJOR_VERSION,
-    librsvg_minor_version = LIBRSVG_MINOR_VERSION, librsvg_micro_version = LIBRSVG_MICRO_VERSION;
-
-const char *librsvg_version = LIBRSVG_VERSION;
+const guint librsvg_major_version = LIBRSVG_MAJOR_VERSION;
+const guint librsvg_minor_version = LIBRSVG_MINOR_VERSION;
+const guint librsvg_micro_version = LIBRSVG_MICRO_VERSION;
+const char librsvg_version[] = LIBRSVG_VERSION;
diff --git a/librsvg-features.h.in b/librsvg-features.h.in
index 32c87d7..8950ed6 100644
--- a/librsvg-features.h.in
+++ b/librsvg-features.h.in
@@ -1,5 +1,9 @@
+#if !defined (__RSVG_RSVG_H_INSIDE__) && !defined (RSVG_COMPILATION)
+#warning "Including <librsvg/librsvg-features.h> directly is deprecated."
+#endif
+
 #ifndef LIBRSVG_FEATURES_H
-#define LIBRSVG_FEATURES_H 1
+#define LIBRSVG_FEATURES_H
 
 #define LIBRSVG_MAJOR_VERSION (@LIBRSVG_MAJOR_VERSION@)
 #define LIBRSVG_MINOR_VERSION (@LIBRSVG_MINOR_VERSION@)
@@ -11,15 +15,36 @@
    (LIBRSVG_MAJOR_VERSION == (major) && LIBRSVG_MINOR_VERSION > (minor)) || \
    (LIBRSVG_MAJOR_VERSION == (major) && LIBRSVG_MINOR_VERSION == (minor) && LIBRSVG_MICRO_VERSION >= (micro)))
 
-#define LIBRSVG_HAVE_SVGZ  (@LIBRSVG_HAVE_SVGZ@)
-#define LIBRSVG_HAVE_CSS   (@LIBRSVG_HAVE_CSS@)
+#ifndef __GI_SCANNER__
+#define LIBRSVG_HAVE_SVGZ  (TRUE)
+#define LIBRSVG_HAVE_CSS   (TRUE)
 
 #define LIBRSVG_CHECK_FEATURE(FEATURE) (defined(LIBRSVG_HAVE_##FEATURE) && LIBRSVG_HAVE_##FEATURE)
+#endif
 
-extern const unsigned int librsvg_major_version, librsvg_minor_version, librsvg_micro_version;
-extern const char *librsvg_version;
+/*
+ * On Windows builds, we need to decorate variables that are exposed in the public API
+ * so that they can be properly exported and linked to, for DLL builds
+ */
+#ifndef RSVG_VAR
+# ifdef G_PLATFORM_WIN32
+#  ifndef RSVG_STATIC
+#   ifdef RSVG_COMPILATION
+#    define RSVG_VAR extern __declspec (dllexport)
+#   else /* RSVG_COMPILATION */
+#    define RSVG_VAR extern __declspec (dllimport)
+#   endif /* !RSVG_COMPILATION */
+#  else /* !RSVG_STATIC */
+#   define RSVG_VAR extern
+#  endif /* RSVG_STATIC */
+# else /* G_PLATFORM_WIN32 */
+#  define RSVG_VAR extern
+# endif /* !G_PLATFORM_WIN32 */
+#endif
 
-void librsvg_preinit(void *app, void *modinfo);
-void librsvg_postinit(void *app, void *modinfo);
+RSVG_VAR const guint librsvg_major_version;
+RSVG_VAR const guint librsvg_minor_version;
+RSVG_VAR const guint librsvg_micro_version;
+RSVG_VAR const char librsvg_version[];
 
 #endif
diff --git a/librsvg.def b/librsvg.def
deleted file mode 100644
index 2ba9834..0000000
--- a/librsvg.def
+++ /dev/null
@@ -1,39 +0,0 @@
-rsvg_error_quark
-rsvg_error_get_type
-rsvg_init
-rsvg_term
-rsvg_set_default_dpi
-rsvg_set_default_dpi_x_y
-rsvg_handle_set_dpi
-rsvg_handle_set_dpi_x_y
-rsvg_handle_new
-rsvg_handle_write
-rsvg_handle_close
-rsvg_handle_get_pixbuf
-rsvg_handle_get_pixbuf_sub
-rsvg_handle_free
-rsvg_handle_get_base_uri
-rsvg_handle_set_base_uri
-rsvg_handle_get_dimensions
-rsvg_handle_get_dimensions_sub
-rsvg_handle_get_position_sub
-rsvg_handle_get_title
-rsvg_handle_get_desc
-rsvg_handle_get_metadata
-rsvg_handle_has_sub
-rsvg_handle_new_from_data
-rsvg_handle_new_from_file
-rsvg_handle_set_size_callback
-rsvg_pixbuf_from_file
-rsvg_pixbuf_from_file_at_zoom
-rsvg_pixbuf_from_file_at_size
-rsvg_pixbuf_from_file_at_max_size
-rsvg_pixbuf_from_file_at_zoom_with_max
-rsvg_handle_render_cairo
-rsvg_handle_render_cairo_sub
-rsvg_handle_get_type
-_rsvg_size_callback
-_rsvg_acquire_xlink_href_resource
-_rsvg_register_types
-rsvg_pixbuf_from_data_with_size_data
-rsvg_css_parse_color
diff --git a/moz-plugin/Makefile.am b/moz-plugin/Makefile.am
deleted file mode 100644
index 6fd9117..0000000
--- a/moz-plugin/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-plugindir = $(libdir)/mozilla/plugins
-
-if WITH_MOZILLA
-plugin_LTLIBRARIES = libmozsvgdec.la
-else
-plugin_LTLIBRARIES = 
-endif
-
-INCLUDES=$(MOZILLA_CFLAGS) -DBINDIR=\"$(bindir)/\" $(GLIB_CFLAGS) $(GDK_X11_CFLAGS)
-
-libmozsvgdec_la_SOURCES = moz-plugin.c
-libmozsvgdec_la_LDFLAGS = -module -avoid-version 
-libmozsvgdec_la_LIBADD = $(GLIB_LIBS)
-EXTRA_DIST=moz-plugin.c
diff --git a/moz-plugin/moz-plugin.c b/moz-plugin/moz-plugin.c
deleted file mode 100644
index 2bd5014..0000000
--- a/moz-plugin/moz-plugin.c
+++ /dev/null
@@ -1,587 +0,0 @@
-/* vim: set sw=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
-   moz-plugin.c: Mozilla plugin
-
-   Copyright (C) 2003-2004 Dom Lachowicz <cinamod@hotmail.com>
-   Copyright (C) 2003 David Schleef <ds@schleef.org>
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-
-   You should have received a copy of the GNU Library General Public
-   License along with this program; if not, write to the
-   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.
-
-   Author: Dom Lachowicz <cinamod@hotmail.com>  
-*/
-
-#include <config.h>
-
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <stdarg.h>
-
-#include <X11/Xlib.h>
-#include <X11/Intrinsic.h>
-
-#include <glib.h>
-
-#define XP_UNIX 1
-#define MOZ_X11 1
-#include "npapi.h"
-#include "npupp.h"
-
-#define DEBUG(x) _svg_plugin_debug x
-
-static void 
-_svg_plugin_debug(const char * fmt, ...)
-{
-#if 0
-	FILE * err;
-	va_list ap;
-
-	err = fopen("/tmp/moz.svg", "ab");
-
-	va_start(ap, fmt);
-	vfprintf(err, fmt, ap);
-	va_end(ap);
-
-	fclose(err);
-#endif
-}
-
-typedef struct
-{
-	NPP instance;
-	Window window;
-
-	int width, height, window_width, window_height;
-	int sizes_in_percentages;
-
-	GByteArray * bytes;
-
-	int send_fd;
-	int pid;
-
-	char *base_url;
-} SvgPlugin;
-
-static NPNetscapeFuncs mozilla_funcs;
-
-static void
-svg_plugin_kill (SvgPlugin * plugin)
-{
-	DEBUG(("plugin_kill\n"));
-
-	if(plugin->send_fd > 0)
-		{
-			close (plugin->send_fd);
-			plugin->send_fd = -1;
-		}
-
-	if(plugin->pid > 0)
-		{
-			kill (plugin->pid, SIGKILL);
-			waitpid (plugin->pid, NULL, 0);
-			
-			plugin->pid = -1;
-		}
-}
-
-static NPError
-svg_plugin_fork (SvgPlugin * plugin, int * plugin_pid, int * plugin_send_fd)
-{
-	char xid_str[20];
-	char width_str[G_ASCII_DTOSTR_BUF_SIZE];
-	char height_str[G_ASCII_DTOSTR_BUF_SIZE];
-	char window_width_str[G_ASCII_DTOSTR_BUF_SIZE];
-	char window_height_str[G_ASCII_DTOSTR_BUF_SIZE];
-	char *argv[32];
-	int argc = 0;
-	GError *err = NULL;
-	
-	DEBUG(("plugin_fork\n"));
-
-	sprintf (xid_str, "%ld", plugin->window);
-			
-	argv[argc++] = BINDIR "rsvg-view";
-
-	/* xid */
-	argv[argc++] = "-i";
-	argv[argc++] = xid_str;
-	
-	if (plugin->width)
-		{
-			/* width */
-			if (plugin->sizes_in_percentages) {
-
-				if(plugin->window_width > 0) {
-					sprintf (window_width_str, "%d", plugin->window_width);
-					argv[argc++] = "-w";
-					argv[argc++] = window_width_str;
-				}
-
-				g_ascii_dtostr (width_str, sizeof (width_str), (double)plugin->width / 100.);
-				argv[argc++] = "-x";
-			} 
-			else {
-				sprintf (width_str, "%d", plugin->width);
-				argv[argc++] = "-w";
-			}
-
-			argv[argc++] = width_str;
-		}
-	
-	if (plugin->height)
-		{
-			/* height */
-			if (plugin->sizes_in_percentages) {
-
-				if(plugin->window_height > 0) {
-					sprintf (window_height_str, "%d", plugin->window_height);
-					argv[argc++] = "-h";
-					argv[argc++] = window_height_str;
-				}
-
-				g_ascii_dtostr (height_str, sizeof (height_str), (double)plugin->height / 100.);
-				argv[argc++] = "-y";
-			} 
-			else {
-				sprintf (height_str, "%d", plugin->height);
-				argv[argc++] = "-h";
-			}
-
-			argv[argc++] = height_str;
-		}
-	
-	/* HACK!! hardcode bg color to white for Uraeus' viewing pleasure */
-	argv[argc++] = "-b";
-	argv[argc++] = "white";
-
-	if (plugin->base_url) {
-		argv[argc++] = "-u";
-		argv[argc++] = plugin->base_url;
-	}
-
-	/* HACK: keep aspect ratio */
-	if (plugin->sizes_in_percentages) {
-		argv[argc++] = "-k";
-	}
-
-	/* read from stdin */
-	argv[argc++] = "-s";
-	argv[argc] = NULL;
-
-	if(!g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL, 
-								 NULL, NULL, plugin_pid,
-								 plugin_send_fd, NULL, NULL, &err))
-		{
-			DEBUG(("Spawn failed\n"));
-
-			if(err) 
-				{
-					fprintf(stderr, "%s\n", err->message);
-					g_error_free(err);
-				}
-
-			return NPERR_INVALID_INSTANCE_ERROR;
-		}
-
-	return NPERR_NO_ERROR;
-}
-
-static NPError
-svg_plugin_redraw (SvgPlugin * plugin)
-{
-	NPError res = NPERR_NO_ERROR;
-
-	DEBUG(("plugin_redraw\n"));
-
-	if(plugin && plugin->bytes && plugin->bytes->len)
-		{
-			if (plugin->pid <= 0)
-				{
-					if ((res = svg_plugin_fork (plugin, &plugin->pid, &plugin->send_fd)) == NPERR_NO_ERROR) {
-						if(plugin->pid > 0)
-							{
-								size_t nwritten = 0;
-								while(nwritten < plugin->bytes->len)
-									nwritten += write (plugin->send_fd, plugin->bytes->data + nwritten, plugin->bytes->len - nwritten);
-							}
-						else
-							{
-								res = NPERR_INVALID_INSTANCE_ERROR;
-							}
-					}
-				}
-		}
-
-	return res;
-}
-
-static NPError
-svg_plugin_newp (NPMIMEType mime_type, NPP instance,
-				 guint16 mode, gint16 argc, char *argn[], char *argv[],
-				 NPSavedData * saved)
-{
-	SvgPlugin *plugin;
-	int i;
-	
-	DEBUG (("plugin_newp\n"));
-  
-	if (instance == NULL)
-		return NPERR_INVALID_INSTANCE_ERROR;
-	
-	instance->pdata = mozilla_funcs.memalloc (sizeof (SvgPlugin));
-	plugin = (SvgPlugin *) instance->pdata;
-
-	if (plugin == NULL)
-		return NPERR_OUT_OF_MEMORY_ERROR;
-	memset (plugin, 0, sizeof (SvgPlugin));
-	
-	/* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */
-	plugin->instance = instance;
-	
-	for (i = 0; i < argc; i++)
-		{
-			DEBUG (("argv[%d] %s %s\n", i, argn[i], argv[i]));
-			
-			if (strcmp (argn[i], "width") == 0) {
-				if (strstr (argv [i], "%") != NULL)
-					plugin->sizes_in_percentages = 1;
-				plugin->width = strtol (argv[i], NULL, 0);
-			}
-
-			if (strcmp (argn[i], "height") == 0) {
-				if (strstr (argv [i], "%") != NULL)
-					plugin->sizes_in_percentages = 1;
-				plugin->height = strtol (argv[i], NULL, 0);
-			}
-		}   
-
-  return NPERR_NO_ERROR;
-}
-
-static NPError
-svg_plugin_destroy (NPP instance, NPSavedData ** save)
-{
-	SvgPlugin *plugin;
-	
-	DEBUG (("plugin_destroy\n"));
-	
-	if (instance == NULL)
-		return NPERR_INVALID_INSTANCE_ERROR;
-	
-	plugin = (SvgPlugin *) instance->pdata;
-	if (plugin == NULL)
-		return NPERR_NO_ERROR;
-
-	if(plugin->bytes)
-		g_byte_array_free (plugin->bytes, TRUE);
-	
-	svg_plugin_kill (plugin);
-
-	if (plugin->base_url)
-		mozilla_funcs.memfree (plugin->base_url);
-
-	mozilla_funcs.memfree (instance->pdata);
-	instance->pdata = NULL;
-	
-	return NPERR_NO_ERROR;
-}
-
-static NPError
-svg_plugin_set_window (NPP instance, NPWindow * window)
-{
-	SvgPlugin *plugin;
-	NPError res = NPERR_NO_ERROR;
-	
-	DEBUG (("plugin_set_window\n"));
-	
-	if (instance == NULL)
-		return NPERR_INVALID_INSTANCE_ERROR;
-
-	plugin = (SvgPlugin *) instance->pdata;
-	if (plugin == NULL)
-		return NPERR_INVALID_INSTANCE_ERROR;
-	
-	if (plugin->window)
-		{
-			if (plugin->window == (Window) window->window)
-				{
-					DEBUG (("window resized\n"));
-
-#if 0
-					plugin->window_width = window->width;
-					plugin->window_height = window->height;
-
-					res = svg_plugin_redraw (plugin);
-#endif
-				}
-			else
-				{
-					DEBUG (("change. ack.  window changed!\n"));
-				}
-		}
-	else
-		{
-			NPSetWindowCallbackStruct *ws_info;
-			
-			ws_info = window->ws_info;
-			plugin->window = (Window) window->window;
-		}
-	
-	return res;
-}
-
-static NPError
-svg_plugin_new_stream (NPP instance, NPMIMEType type, NPStream *stream,
-		      NPBool seekable, uint16 *stype)
-{
-	SvgPlugin *plugin;
-
-	DEBUG (("plugin_new_stream\n"));
-
-	if (instance == NULL)
-		return NPERR_INVALID_INSTANCE_ERROR;
-	
-	plugin = (SvgPlugin *) instance->pdata;
-	if (plugin == NULL)
-		return NPERR_NO_ERROR;	
-	
-	g_return_val_if_fail(plugin->bytes == NULL, NPERR_NO_ERROR);
-
-	plugin->bytes = g_byte_array_new();
-	
-	*stype = NP_NORMAL;
-
-	return NPERR_NO_ERROR;
-}
-
-static NPError
-svg_plugin_destroy_stream (NPP instance, NPStream * stream, NPError reason)
-{
-	SvgPlugin *plugin;
-	NPError res = NPERR_NO_ERROR;
-	size_t url_len;
-
-	DEBUG (("plugin_destroy_stream\n"));
-
-	if (instance == NULL)
-		return NPERR_INVALID_INSTANCE_ERROR;
-	
-	plugin = (SvgPlugin *) instance->pdata;
-	if (plugin == NULL)
-		return NPERR_NO_ERROR;
-
-	if (stream->url) {
-		url_len = strlen(stream->url);
-		plugin->base_url = mozilla_funcs.memalloc (url_len + 1);
-		strcpy(plugin->base_url, stream->url);
-		plugin->base_url[url_len] = '\0';
-	}
-
-	/* trigger */
-	res = svg_plugin_redraw (plugin);
-
-	if(plugin->send_fd > 0)
-		{
-			close (plugin->send_fd);
-			plugin->send_fd = -1;
-		}
-
-	return res;
-}
-
-static gint32
-svg_plugin_write_ready (NPP instance, NPStream * stream)
-{
-	DEBUG (("plugin_write_ready\n"));
-	
-	/* This is arbitrary */
-	return (8*1024);
-}
-
-static void
-svg_plugin_print (NPP instance, NPPrint * platformPrint)
-{
-	SvgPlugin *plugin;
-
-	DEBUG (("plugin_print\n"));
-	
-	if (instance == NULL)
-		return;
-
-	plugin = (SvgPlugin *) instance->pdata;
-	
-	if (plugin == NULL)
-		return;
-
-	if (NP_EMBED == platformPrint->mode) {
-		NPPrintCallbackStruct *printer = (NPPrintCallbackStruct *)platformPrint->print.embedPrint.platformPrint;
-
-		if (0) {
-			gchar * temp_filename;
-			FILE * fp;
-			int fd;
-			
-			temp_filename = g_build_filename (g_get_tmp_dir(), ".mozilla-svg-print-XXXXXX", NULL);
-			fd = g_mkstemp (temp_filename);
-			close (fd);
-
-			/* todo: get rsvg-view to print the file */
-
-			if ((fp = fopen (temp_filename, "rb")) != NULL) {
-				gchar buf [8*1024];
-				gint nread;
-
-				while ((nread = fread (buf, 1, sizeof (buf), fp)) > 0) {
-					gint nwritten = 0;
-
-					do {
-						nwritten += fwrite (buf + nwritten, 1, (nread - nwritten), printer->fp);
-					} while (nwritten != nread);
-				}
-				
-				fclose (fp);
-			}
-
-			remove (temp_filename);
-
-			g_free (temp_filename);
-		}
-	}
-}
-
-static gint32
-svg_plugin_write (NPP instance, NPStream * stream, gint32 offset,
-			  gint32 len, void *buffer)
-{
-	SvgPlugin *plugin;
-
-	DEBUG (("plugin_write\n"));
-	
-	if (instance == NULL)
-		return 0;
-
-	plugin = (SvgPlugin *) instance->pdata;
-	
-	if (plugin == NULL)
-		return 0;
-	
-	if (!plugin->bytes)
-		return 0;
-	
-	(void)g_byte_array_append (plugin->bytes, buffer, len);
-
-	return len;
-}
-
-static void
-svg_plugin_stream_as_file (NPP instance, NPStream * stream, const char *fname)
-{
-	SvgPlugin *plugin;
-	
-	DEBUG (("plugin_stream_as_file\n"));
-	
-	if (instance == NULL)
-		return;
-	plugin = (SvgPlugin *) instance->pdata;
-	
-	if (plugin == NULL)
-		return;
-}
-
-/* exported functions */
-
-NPError
-NP_GetValue (void *future, NPPVariable variable, void *value)
-{
-	NPError err = NPERR_NO_ERROR;
-	
-	DEBUG (("NP_GetValue\n"));
-
-	switch (variable)
-		{
-		case NPPVpluginNameString:
-			*((char **) value) = "Scalable Vector Graphics";
-			break;
-		case NPPVpluginDescriptionString:
-			*((char **) value) =
-				"Scalable Vector Graphics, as handled by RSVG-" VERSION
-				".  "
-				"Views SVG images.<br><br>"
-				"This is alpha software.  It will probably behave in many situations, but "
-				"may also ride your motorcycle, drink all your milk, or use your computer "
-				"to browse porn.  Comments, feature requests, and patches are welcome.<br><br>"
-				"See <a href=\"http://librsvg.sourceforge.net/\">"
-				"http://librsvg.sourceforge.net/</a> for information.<br><br>";
-			break;
-		default:
-			err = NPERR_GENERIC_ERROR;
-		}
-	
-  return err;
-}
-
-char *
-NP_GetMIMEDescription (void)
-{
-	DEBUG (("NP_GetMIMEDescription\n"));
-
-	/* unfortunately, a lot of win32 servers serving up Adobe content return bogus mime-types... */
-	return ("image/svg+xml:svg,svgz:Scalable Vector Graphics;image/svg-xml:svg,svgz:Scalable Vector Graphics;"
-			"image/svg:svg,svgz:Scalable Vector Graphics;image/vnd.adobe.svg+xml:svg,svgz:Scalable Vector Graphics;"
-			"text/xml-svg:svg,svgz:Scalable Vector Graphics");
-}
-
-NPError
-NP_Initialize (NPNetscapeFuncs * moz_funcs, NPPluginFuncs * plugin_funcs)
-{
-	DEBUG (("NP_Initialize\n"));
-	
-	if (moz_funcs == NULL || plugin_funcs == NULL)
-		return NPERR_INVALID_FUNCTABLE_ERROR;
-	
-	if ((moz_funcs->version >> 8) > NP_VERSION_MAJOR)
-		return NPERR_INCOMPATIBLE_VERSION_ERROR;
-	if (moz_funcs->size < sizeof (NPNetscapeFuncs))
-		return NPERR_INVALID_FUNCTABLE_ERROR;
-	if (plugin_funcs->size < sizeof (NPPluginFuncs))
-		return NPERR_INVALID_FUNCTABLE_ERROR;
-
-	memcpy (&mozilla_funcs, moz_funcs, sizeof (NPNetscapeFuncs));
-	
-	plugin_funcs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
-	plugin_funcs->size = sizeof (NPPluginFuncs);
-	plugin_funcs->newp = NewNPP_NewProc (svg_plugin_newp);
-	plugin_funcs->destroy = NewNPP_DestroyProc (svg_plugin_destroy);
-	plugin_funcs->setwindow = NewNPP_SetWindowProc (svg_plugin_set_window);
-	plugin_funcs->newstream = NewNPP_NewStreamProc (svg_plugin_new_stream);
-	plugin_funcs->destroystream =
-		NewNPP_DestroyStreamProc (svg_plugin_destroy_stream);
-	plugin_funcs->writeready = NewNPP_WriteReadyProc (svg_plugin_write_ready);
-	plugin_funcs->asfile = NewNPP_StreamAsFileProc (svg_plugin_stream_as_file);
-	plugin_funcs->write = NewNPP_WriteProc (svg_plugin_write);
-	plugin_funcs->print = NewNPP_PrintProc (svg_plugin_print);
-
-	return NPERR_NO_ERROR;
-}
-
-NPError
-NP_Shutdown (void)
-{
-	DEBUG(("NP_Shutdown"));
-
-	return NPERR_NO_ERROR;
-}
diff --git a/rsvg-affine.c b/rsvg-affine.c
deleted file mode 100644
index 0a7928c..0000000
--- a/rsvg-affine.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/* Libart_LGPL - library of basic graphic primitives
- * Copyright (C) 1998 Raph Levien
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/* Simple manipulations with affine transformations */
-
-#include "config.h"
-#include "rsvg-private.h"
-
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
-
-/**
- * _rsvg_affine_invert: Find the inverse of an affine transformation.
- * @dst: Where the resulting affine is stored.
- * @src: The original affine transformation.
- *
- * All non-degenerate affine transforms are invertible. If the original
- * affine is degenerate or nearly so, expect numerical instability and
- * very likely core dumps on Alpha and other fp-picky architectures.
- * Otherwise, @dst multiplied with @src, or @src multiplied with @dst
- * will be (to within roundoff error) the identity affine.
- **/
-void
-_rsvg_affine_invert (double dst[6], const double src[6])
-{
-    double r_det;
-
-    r_det = 1.0 / (src[0] * src[3] - src[1] * src[2]);
-    dst[0] = src[3] * r_det;
-    dst[1] = -src[1] * r_det;
-    dst[2] = -src[2] * r_det;
-    dst[3] = src[0] * r_det;
-    dst[4] = -src[4] * dst[0] - src[5] * dst[2];
-    dst[5] = -src[4] * dst[1] - src[5] * dst[3];
-}
-
-/**
- * _rsvg_affine_flip: Flip an affine transformation horizontally and/or vertically.
- * @dst_affine: Where the resulting affine is stored.
- * @src_affine: The original affine transformation.
- * @horiz: Whether or not to flip horizontally.
- * @vert: Whether or not to flip horizontally.
- *
- * Flips the affine transform. FALSE for both @horiz and @vert implements
- * a simple copy operation. TRUE for both @horiz and @vert is a
- * 180 degree rotation. It is ok for @src_affine and @dst_affine to
- * be equal pointers.
- **/
-void
-_rsvg_affine_flip (double dst_affine[6], const double src_affine[6], int horz, int vert)
-{
-    dst_affine[0] = horz ? -src_affine[0] : src_affine[0];
-    dst_affine[1] = horz ? -src_affine[1] : src_affine[1];
-    dst_affine[2] = vert ? -src_affine[2] : src_affine[2];
-    dst_affine[3] = vert ? -src_affine[3] : src_affine[3];
-    dst_affine[4] = horz ? -src_affine[4] : src_affine[4];
-    dst_affine[5] = vert ? -src_affine[5] : src_affine[5];
-}
-
-#define EPSILON 1e-6
-
-/**
- * _rsvg_affine_multiply: Multiply two affine transformation matrices.
- * @dst: Where to store the result.
- * @src1: The first affine transform to multiply.
- * @src2: The second affine transform to multiply.
- *
- * Multiplies two affine transforms together, i.e. the resulting @dst
- * is equivalent to doing first @src1 then @src2. Note that the
- * PostScript concat operator multiplies on the left, i.e.  "M concat"
- * is equivalent to "CTM = multiply (M, CTM)";
- *
- * It is safe to call this function with @dst equal to @src1 or @src2.
- **/
-void
-_rsvg_affine_multiply (double dst[6], const double src1[6], const double src2[6])
-{
-    double d0, d1, d2, d3, d4, d5;
-
-    d0 = src1[0] * src2[0] + src1[1] * src2[2];
-    d1 = src1[0] * src2[1] + src1[1] * src2[3];
-    d2 = src1[2] * src2[0] + src1[3] * src2[2];
-    d3 = src1[2] * src2[1] + src1[3] * src2[3];
-    d4 = src1[4] * src2[0] + src1[5] * src2[2] + src2[4];
-    d5 = src1[4] * src2[1] + src1[5] * src2[3] + src2[5];
-    dst[0] = d0;
-    dst[1] = d1;
-    dst[2] = d2;
-    dst[3] = d3;
-    dst[4] = d4;
-    dst[5] = d5;
-}
-
-/**
- * _rsvg_affine_identity: Set up the identity matrix.
- * @dst: Where to store the resulting affine transform.
- *
- * Sets up an identity matrix.
- **/
-void
-_rsvg_affine_identity (double dst[6])
-{
-    dst[0] = 1;
-    dst[1] = 0;
-    dst[2] = 0;
-    dst[3] = 1;
-    dst[4] = 0;
-    dst[5] = 0;
-}
-
-
-/**
- * _rsvg_affine_scale: Set up a scaling matrix.
- * @dst: Where to store the resulting affine transform.
- * @sx: X scale factor.
- * @sy: Y scale factor.
- *
- * Sets up a scaling matrix.
- **/
-void
-_rsvg_affine_scale (double dst[6], double sx, double sy)
-{
-    dst[0] = sx;
-    dst[1] = 0;
-    dst[2] = 0;
-    dst[3] = sy;
-    dst[4] = 0;
-    dst[5] = 0;
-}
-
-/**
- * _rsvg_affine_rotate: Set up a rotation affine transform.
- * @dst: Where to store the resulting affine transform.
- * @theta: Rotation angle in degrees.
- *
- * Sets up a rotation matrix. In the standard libart coordinate
- * system, in which increasing y moves downward, this is a
- * counterclockwise rotation. In the standard PostScript coordinate
- * system, which is reversed in the y direction, it is a clockwise
- * rotation.
- **/
-void
-_rsvg_affine_rotate (double dst[6], double theta)
-{
-    double s, c;
-
-    s = sin (theta * M_PI / 180.0);
-    c = cos (theta * M_PI / 180.0);
-    dst[0] = c;
-    dst[1] = s;
-    dst[2] = -s;
-    dst[3] = c;
-    dst[4] = 0;
-    dst[5] = 0;
-}
-
-/**
- * _rsvg_affine_shear: Set up a shearing matrix.
- * @dst: Where to store the resulting affine transform.
- * @theta: Shear angle in degrees.
- *
- * Sets up a shearing matrix. In the standard libart coordinate system
- * and a small value for theta, || becomes \\. Horizontal lines remain
- * unchanged.
- **/
-void
-_rsvg_affine_shear (double dst[6], double theta)
-{
-    double t;
-
-    t = tan (theta * M_PI / 180.0);
-    dst[0] = 1;
-    dst[1] = 0;
-    dst[2] = t;
-    dst[3] = 1;
-    dst[4] = 0;
-    dst[5] = 0;
-}
-
-/**
- * _rsvg_affine_translate: Set up a translation matrix.
- * @dst: Where to store the resulting affine transform.
- * @tx: X translation amount.
- * @tx: Y translation amount.
- *
- * Sets up a translation matrix.
- **/
-void
-_rsvg_affine_translate (double dst[6], double tx, double ty)
-{
-    dst[0] = 1;
-    dst[1] = 0;
-    dst[2] = 0;
-    dst[3] = 1;
-    dst[4] = tx;
-    dst[5] = ty;
-}
-
-/**
- * _rsvg_affine_expansion: Find the affine's expansion factor.
- * @src: The affine transformation.
- *
- * Finds the expansion factor, i.e. the square root of the factor
- * by which the affine transform affects area. In an affine transform
- * composed of scaling, rotation, shearing, and translation, returns
- * the amount of scaling.
- *
- * Return value: the expansion factor.
- **/
-double
-_rsvg_affine_expansion (const double src[6])
-{
-    return sqrt (fabs (src[0] * src[3] - src[1] * src[2]));
-}
-
-/**
- * _rsvg_affine_rectilinear: Determine whether the affine transformation is rectilinear.
- * @src: The original affine transformation.
- *
- * Determines whether @src is rectilinear, i.e.  grid-aligned
- * rectangles are transformed to other grid-aligned rectangles.  The
- * implementation has epsilon-tolerance for roundoff errors.
- *
- * Return value: TRUE if @src is rectilinear.
- **/
-int
-_rsvg_affine_rectilinear (const double src[6])
-{
-    return ((fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON) ||
-            (fabs (src[0]) < EPSILON && fabs (src[3]) < EPSILON));
-}
-
-/**
- * _rsvg_affine_equal: Determine whether two affine transformations are equal.
- * @matrix1: An affine transformation.
- * @matrix2: Another affine transformation.
- *
- * Determines whether @matrix1 and @matrix2 are equal, with
- * epsilon-tolerance for roundoff errors.
- *
- * Return value: TRUE if @matrix1 and @matrix2 are equal.
- **/
-int
-_rsvg_affine_equal (double matrix1[6], double matrix2[6])
-{
-    return (fabs (matrix1[0] - matrix2[0]) < EPSILON &&
-            fabs (matrix1[1] - matrix2[1]) < EPSILON &&
-            fabs (matrix1[2] - matrix2[2]) < EPSILON &&
-            fabs (matrix1[3] - matrix2[3]) < EPSILON &&
-            fabs (matrix1[4] - matrix2[4]) < EPSILON && fabs (matrix1[5] - matrix2[5]) < EPSILON);
-}
diff --git a/rsvg-base-file-util.c b/rsvg-base-file-util.c
index cda64af..bf89a14 100644
--- a/rsvg-base-file-util.c
+++ b/rsvg-base-file-util.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-file-util.c: SAX-based renderer for SVG files into a GdkPixbuf.
 
@@ -25,32 +26,32 @@
 
 #include "config.h"
 #include "rsvg.h"
+#include "rsvg-io.h"
 #include "rsvg-private.h"
 
 static gboolean
 rsvg_handle_fill_with_data (RsvgHandle * handle,
-                            const guint8 * data, gsize data_len, GError ** error)
+                            const char * data, gsize data_len, GError ** error)
 {
+    gboolean rv;
+
     rsvg_return_val_if_fail (data != NULL, FALSE, error);
     rsvg_return_val_if_fail (data_len != 0, FALSE, error);
 
-    if (!rsvg_handle_write (handle, data, data_len, error))
-        return FALSE;
-    if (!rsvg_handle_close (handle, error))
-        return FALSE;
+    rv = rsvg_handle_write (handle, (guchar *) data, data_len, error);
 
-    return TRUE;
+    return rsvg_handle_close (handle, rv ? error : NULL) && rv;
 }
 
 /**
  * rsvg_handle_new_from_data:
- * @data: The SVG data
- * @data_len: The length of #data, in bytes
+ * @data: (array length=data_len): The SVG data
+ * @data_len: The length of @data, in bytes
  * @error: return location for errors
  *
- * Loads the SVG specified by #data.
+ * Loads the SVG specified by @data.
  *
- * Returns: A RsvgHandle or %NULL if an error occurs.
+ * Returns: A #RsvgHandle or %NULL if an error occurs.
  * Since: 2.14
  */
 RsvgHandle *
@@ -61,8 +62,8 @@ rsvg_handle_new_from_data (const guint8 * data, gsize data_len, GError ** error)
     handle = rsvg_handle_new ();
 
     if (handle) {
-        if (!rsvg_handle_fill_with_data (handle, data, data_len, error)) {
-            g_object_unref (G_OBJECT (handle));
+        if (!rsvg_handle_fill_with_data (handle, (char *) data, data_len, error)) {
+            g_object_unref (handle);
             handle = NULL;
         }
     }
@@ -75,33 +76,32 @@ rsvg_handle_new_from_data (const guint8 * data, gsize data_len, GError ** error)
  * @file_name: The file name to load. If built with gnome-vfs, can be a URI.
  * @error: return location for errors
  *
- * Loads the SVG specified by #file_name.
+ * Loads the SVG specified by @file_name.
  *
- * Returns: A RsvgHandle or %NULL if an error occurs.
+ * Returns: A #RsvgHandle or %NULL if an error occurs.
  * Since: 2.14
  */
 RsvgHandle *
 rsvg_handle_new_from_file (const gchar * file_name, GError ** error)
 {
     gchar *base_uri;
-    GByteArray *f;
+    char *data;
+    gsize data_len;
     RsvgHandle *handle = NULL;
 
     rsvg_return_val_if_fail (file_name != NULL, NULL, error);
 
     base_uri = rsvg_get_base_uri_from_filename (file_name);
-    f = _rsvg_acquire_xlink_href_resource (file_name, base_uri, error);
+    data = _rsvg_io_acquire_data (file_name, base_uri, NULL, &data_len, NULL, error);
 
-    if (f) {
+    if (data) {
         handle = rsvg_handle_new ();
-        if (handle) {
-            rsvg_handle_set_base_uri (handle, base_uri);
-            if (!rsvg_handle_fill_with_data (handle, f->data, f->len, error)) {
-                g_object_unref (G_OBJECT (handle));
-                handle = NULL;
-            }
+        rsvg_handle_set_base_uri (handle, base_uri);
+        if (!rsvg_handle_fill_with_data (handle, data, data_len, error)) {
+            g_object_unref (handle);
+            handle = NULL;
         }
-        g_byte_array_free (f, TRUE);
+        g_free (data);
     }
 
     g_free (base_uri);
diff --git a/rsvg-base.c b/rsvg-base.c
index d6b1f23..b0b7feb 100644
--- a/rsvg-base.c
+++ b/rsvg-base.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg.c: SAX-based renderer for SVG files into a GdkPixbuf.
 
@@ -24,20 +25,17 @@
 */
 
 #include "config.h"
-
-#ifdef HAVE_SVGZ
-#include <gsf/gsf-input-gzip.h>
-#include <gsf/gsf-input-memory.h>
-#include <gsf/gsf-output-memory.h>
-#include <gsf/gsf-utils.h>
-#endif
+#define _GNU_SOURCE 1
 
 #include "rsvg.h"
+#include "rsvg-compat.h"
 #include "rsvg-private.h"
 #include "rsvg-css.h"
 #include "rsvg-styles.h"
 #include "rsvg-shapes.h"
+#include "rsvg-structure.h"
 #include "rsvg-image.h"
+#include "rsvg-io.h"
 #include "rsvg-text.h"
 #include "rsvg-filter.h"
 #include "rsvg-mask.h"
@@ -45,21 +43,64 @@
 #include "rsvg-cairo-render.h"
 
 #include <libxml/uri.h>
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+
+#include <gio/gio.h>
 
 #include <math.h>
 #include <string.h>
 #include <stdarg.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include <gsf/gsf-input-gzip.h>
+#include <gsf/gsf-input-memory.h>
+#include <gsf/gsf-output-memory.h>
+#include <gsf/gsf-utils.h>
 
-#include "rsvg-bpath-util.h"
 #include "rsvg-path.h"
 #include "rsvg-paint-server.h"
+#include "rsvg-xml.h"
+
+#ifdef G_OS_WIN32
+static char *
+rsvg_realpath_utf8 (const char *filename, const char *unused)
+{
+    wchar_t *wfilename;
+    wchar_t *wfull;
+    char *full;
+
+    wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
+    if (!wfilename)
+        return NULL;
+
+    wfull = _wfullpath (NULL, wfilename, 0);
+    g_free (wfilename);
+    if (!wfull)
+        return NULL;
+
+    full = g_utf16_to_utf8 (wfull, -1, NULL, NULL, NULL);
+    free (wfull);
+
+    if (!full)
+        return NULL;
+
+    return full;
+}
+
+#define realpath(a,b) rsvg_realpath_utf8 (a, b)
+#endif
 
 /*
  * This is configurable at runtime
  */
 #define RSVG_DEFAULT_DPI_X 90.0
 #define RSVG_DEFAULT_DPI_Y 90.0
+
+G_GNUC_INTERNAL
 double rsvg_internal_dpi_x = RSVG_DEFAULT_DPI_X;
+G_GNUC_INTERNAL
 double rsvg_internal_dpi_y = RSVG_DEFAULT_DPI_Y;
 
 static xmlSAXHandler rsvgSAXHandlerStruct;
@@ -75,12 +116,21 @@ typedef struct _RsvgSaxHandlerStyle {
     RsvgSaxHandlerDefs *parent;
     RsvgHandle *ctx;
     GString *style;
+    gboolean is_text_css;
 } RsvgSaxHandlerStyle;
 
+typedef struct {
+    RsvgSaxHandler super;
+    RsvgHandle *ctx;
+    const char *name;
+    GString *string;
+    GString **stringptr;
+} RsvgSaxHandlerExtra;
+
 /* hide this fact from the general public */
-typedef RsvgSaxHandlerDefs RsvgSaxHandlerTitle;
-typedef RsvgSaxHandlerDefs RsvgSaxHandlerDesc;
-typedef RsvgSaxHandlerDefs RsvgSaxHandlerMetadata;
+typedef RsvgSaxHandlerExtra RsvgSaxHandlerTitle;
+typedef RsvgSaxHandlerExtra RsvgSaxHandlerDesc;
+typedef RsvgSaxHandlerExtra RsvgSaxHandlerMetadata;
 
 static void
 rsvg_style_handler_free (RsvgSaxHandler * self)
@@ -88,7 +138,8 @@ rsvg_style_handler_free (RsvgSaxHandler * self)
     RsvgSaxHandlerStyle *z = (RsvgSaxHandlerStyle *) self;
     RsvgHandle *ctx = z->ctx;
 
-    rsvg_parse_cssbuffer (ctx, z->style->str, z->style->len);
+    if (z->is_text_css)
+        rsvg_parse_cssbuffer (ctx, z->style->str, z->style->len);
 
     g_string_free (z->style, TRUE);
     g_free (z);
@@ -122,9 +173,12 @@ rsvg_style_handler_end (RsvgSaxHandler * self, const char *name)
 }
 
 static void
-rsvg_start_style (RsvgHandle * ctx, RsvgPropertyBag * atts)
+rsvg_start_style (RsvgHandle * ctx, RsvgPropertyBag *atts)
 {
     RsvgSaxHandlerStyle *handler = g_new0 (RsvgSaxHandlerStyle, 1);
+    const char *type;
+
+    type = rsvg_property_bag_lookup (atts, "type");
 
     handler->super.free = rsvg_style_handler_free;
     handler->super.characters = rsvg_style_handler_characters;
@@ -133,6 +187,7 @@ 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;
 
     handler->parent = (RsvgSaxHandlerDefs *) ctx->priv->handler;
     ctx->priv->handler = &handler->super;
@@ -142,7 +197,6 @@ rsvg_start_style (RsvgHandle * ctx, RsvgPropertyBag * atts)
 static void
 rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag * atts)
 {
-
     /*replace this stuff with a hash for fast reading! */
     RsvgNode *newnode = NULL;
     if (!strcmp (name, "g"))
@@ -196,7 +250,7 @@ rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag
     else if (!strcmp (name, "feBlend"))
         newnode = rsvg_new_filter_primitive_blend ();
     else if (!strcmp (name, "feColorMatrix"))
-        newnode = rsvg_new_filter_primitive_colour_matrix ();
+        newnode = rsvg_new_filter_primitive_color_matrix ();
     else if (!strcmp (name, "feComponentTransfer"))
         newnode = rsvg_new_filter_primitive_component_transfer ();
     else if (!strcmp (name, "feComposite"))
@@ -228,7 +282,7 @@ rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag
     else if (!strcmp (name, "feMergeNode"))
         newnode = rsvg_new_filter_primitive_merge_node ();
     else if (!strcmp (name, "feFuncR"))
-        newnode = rsvg_new_node_component_transfer_function ('r');
+        newnode = rsvg_new_node_component_transfer_function ('r'); /* See rsvg_filter_primitive_component_transfer_render() for where these values are used */
     else if (!strcmp (name, "feFuncG"))
         newnode = rsvg_new_node_component_transfer_function ('g');
     else if (!strcmp (name, "feFuncB"))
@@ -236,11 +290,11 @@ rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag
     else if (!strcmp (name, "feFuncA"))
         newnode = rsvg_new_node_component_transfer_function ('a');
     else if (!strcmp (name, "feDistantLight"))
-        newnode = rsvg_new_filter_primitive_light_source ('d');
+        newnode = rsvg_new_node_light_source ('d');
     else if (!strcmp (name, "feSpotLight"))
-        newnode = rsvg_new_filter_primitive_light_source ('s');
+        newnode = rsvg_new_node_light_source ('s');
     else if (!strcmp (name, "fePointLight"))
-        newnode = rsvg_new_filter_primitive_light_source ('p');
+        newnode = rsvg_new_node_light_source ('p');
     /* hack to make multiImage sort-of work */
     else if (!strcmp (name, "multiImage"))
         newnode = rsvg_new_switch ();
@@ -254,43 +308,56 @@ rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag
         newnode = rsvg_new_tspan ();
     else if (!strcmp (name, "tref"))
         newnode = rsvg_new_tref ();
-	else {
+    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 ();
 	}
 
     if (newnode) {
-        newnode->type = g_string_new (name);
-	newnode->parent = ctx->priv->currentnode;
+        g_assert (RSVG_NODE_TYPE (newnode) != RSVG_NODE_TYPE_INVALID);
+        newnode->name = (char *) name; /* libxml will keep this while parsing */
+        newnode->parent = ctx->priv->currentnode;
         rsvg_node_set_atts (newnode, ctx, atts);
         rsvg_defs_register_memory (ctx->priv->defs, newnode);
         if (ctx->priv->currentnode) {
             rsvg_node_group_pack (ctx->priv->currentnode, newnode);
             ctx->priv->currentnode = newnode;
-        } else if (!strcmp (name, "svg")) {
+        } else if (RSVG_NODE_TYPE (newnode) == RSVG_NODE_TYPE_SVG) {
             ctx->priv->treebase = newnode;
             ctx->priv->currentnode = newnode;
         }
     }
 }
 
-/* start desc */
+/* extra (title, desc, metadata) */
 
 static void
-rsvg_desc_handler_free (RsvgSaxHandler * self)
+rsvg_extra_handler_free (RsvgSaxHandler * self)
 {
+    RsvgSaxHandlerExtra *z = (RsvgSaxHandlerExtra *) self;
+
+    if (z->stringptr) {
+        if (*z->stringptr)
+            g_string_free (*z->stringptr, TRUE);
+        *z->stringptr = z->string;
+    } else if (z->string) {
+        g_string_free (z->string, TRUE);
+    }
+
     g_free (self);
 }
 
 static void
-rsvg_desc_handler_characters (RsvgSaxHandler * self, const char *ch, int len)
+rsvg_extra_handler_characters (RsvgSaxHandler * self, const char *ch, int len)
 {
-    RsvgSaxHandlerDesc *z = (RsvgSaxHandlerDesc *) self;
-    RsvgHandle *ctx = z->ctx;
+    RsvgSaxHandlerExtra *z = (RsvgSaxHandlerExtra *) self;
 
     /* This isn't quite the correct behavior - in theory, any graphics
-       element may contain a title or desc element */
+       element may contain a title, desc, or metadata element */
+
+    if (!z->string)
+        return;
 
     if (!ch || !len)
         return;
@@ -298,25 +365,25 @@ rsvg_desc_handler_characters (RsvgSaxHandler * self, const char *ch, int len)
     if (!g_utf8_validate ((char *) ch, len, NULL)) {
         char *utf8;
         utf8 = rsvg_make_valid_utf8 ((char *) ch, len);
-        g_string_append (ctx->priv->desc, utf8);
+        g_string_append (z->string, utf8);
         g_free (utf8);
     } else {
-        g_string_append_len (ctx->priv->desc, (char *) ch, len);
+        g_string_append_len (z->string, (char *) ch, len);
     }
 }
 
 static void
-rsvg_desc_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
+rsvg_extra_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
 {
 }
 
 static void
-rsvg_desc_handler_end (RsvgSaxHandler * self, const char *name)
+rsvg_extra_handler_end (RsvgSaxHandler * self, const char *name)
 {
-    RsvgSaxHandlerDesc *z = (RsvgSaxHandlerDesc *) self;
+    RsvgSaxHandlerExtra *z = (RsvgSaxHandlerExtra *) self;
     RsvgHandle *ctx = z->ctx;
 
-    if (!strcmp (name, "desc")) {
+    if (!strcmp (name, z->name)) {
         if (ctx->priv->handler != NULL) {
             ctx->priv->handler->free (ctx->priv->handler);
             ctx->priv->handler = NULL;
@@ -324,85 +391,52 @@ rsvg_desc_handler_end (RsvgSaxHandler * self, const char *name)
     }
 }
 
-static void
-rsvg_start_desc (RsvgHandle * ctx, RsvgPropertyBag * atts)
+static RsvgSaxHandlerExtra *
+rsvg_start_extra (RsvgHandle * ctx,
+                  const char *name,
+                  GString **stringptr)
 {
-    RsvgSaxHandlerDesc *handler = g_new0 (RsvgSaxHandlerDesc, 1);
+    RsvgSaxHandlerExtra *handler = g_new0 (RsvgSaxHandlerExtra, 1);
+    RsvgNode *treebase = ctx->priv->treebase;
+    RsvgNode *currentnode = ctx->priv->currentnode;
+    gboolean do_care;
+
+    /* only parse <extra> for the <svg> node.
+     * This isn't quite the correct behavior - any graphics
+     * element may contain a <extra> element.
+     */
+    do_care = treebase != NULL && treebase == currentnode;
 
-    handler->super.free = rsvg_desc_handler_free;
-    handler->super.characters = rsvg_desc_handler_characters;
-    handler->super.start_element = rsvg_desc_handler_start;
-    handler->super.end_element = rsvg_desc_handler_end;
+    handler->super.free = rsvg_extra_handler_free;
+    handler->super.characters = rsvg_extra_handler_characters;
+    handler->super.start_element = rsvg_extra_handler_start;
+    handler->super.end_element = rsvg_extra_handler_end;
     handler->ctx = ctx;
+    handler->name = name; /* interned */
+    handler->string = do_care ? g_string_new (NULL) : NULL;
+    handler->stringptr = do_care ? stringptr : NULL;
 
-    ctx->priv->desc = g_string_new (NULL);
     ctx->priv->handler = &handler->super;
-}
-
-/* end desc */
 
-/* start title */
-
-static void
-rsvg_title_handler_free (RsvgSaxHandler * self)
-{
-    g_free (self);
+    return handler;
 }
 
-static void
-rsvg_title_handler_characters (RsvgSaxHandler * self, const char *ch, int len)
-{
-    RsvgSaxHandlerTitle *z = (RsvgSaxHandlerTitle *) self;
-    RsvgHandle *ctx = z->ctx;
-
-    /* This isn't quite the correct behavior - in theory, any graphics
-       element may contain a title or desc element */
-
-    if (!ch || !len)
-        return;
-
-    if (!g_utf8_validate ((char *) ch, len, NULL)) {
-        char *utf8;
-        utf8 = rsvg_make_valid_utf8 ((char *) ch, len);
-        g_string_append (ctx->priv->title, utf8);
-        g_free (utf8);
-    } else {
-        g_string_append_len (ctx->priv->title, (char *) ch, len);
-    }
-}
+/* start desc */
 
 static void
-rsvg_title_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
+rsvg_start_desc (RsvgHandle * ctx)
 {
+    rsvg_start_extra (ctx, "desc", &ctx->priv->desc);
 }
 
-static void
-rsvg_title_handler_end (RsvgSaxHandler * self, const char *name)
-{
-    RsvgSaxHandlerTitle *z = (RsvgSaxHandlerTitle *) self;
-    RsvgHandle *ctx = z->ctx;
+/* end desc */
 
-    if (!strcmp (name, "title")) {
-        if (ctx->priv->handler != NULL) {
-            ctx->priv->handler->free (ctx->priv->handler);
-            ctx->priv->handler = NULL;
-        }
-    }
-}
+/* start title */
 
 static void
-rsvg_start_title (RsvgHandle * ctx, RsvgPropertyBag * atts)
+rsvg_start_title (RsvgHandle * ctx)
 {
-    RsvgSaxHandlerTitle *handler = g_new0 (RsvgSaxHandlerTitle, 1);
-
-    handler->super.free = rsvg_title_handler_free;
-    handler->super.characters = rsvg_title_handler_characters;
-    handler->super.start_element = rsvg_title_handler_start;
-    handler->super.end_element = rsvg_title_handler_end;
-    handler->ctx = ctx;
-
-    ctx->priv->title = g_string_new (NULL);
-    ctx->priv->handler = &handler->super;
+    rsvg_start_extra (ctx, "title", &ctx->priv->title);
 }
 
 /* end title */
@@ -410,34 +444,6 @@ rsvg_start_title (RsvgHandle * ctx, RsvgPropertyBag * atts)
 /* start metadata */
 
 static void
-rsvg_metadata_handler_free (RsvgSaxHandler * self)
-{
-    g_free (self);
-}
-
-static void
-rsvg_metadata_handler_characters (RsvgSaxHandler * self, const char *ch, int len)
-{
-    RsvgSaxHandlerDesc *z = (RsvgSaxHandlerDesc *) self;
-    RsvgHandle *ctx = z->ctx;
-
-    /* This isn't quite the correct behavior - in theory, any graphics
-       element may contain a metadata or desc element */
-
-    if (!ch || !len)
-        return;
-
-    if (!g_utf8_validate ((char *) ch, len, NULL)) {
-        char *utf8;
-        utf8 = rsvg_make_valid_utf8 ((char *) ch, len);
-        g_string_append (ctx->priv->metadata, utf8);
-        g_free (utf8);
-    } else {
-        g_string_append_len (ctx->priv->metadata, (char *) ch, len);
-    }
-}
-
-static void
 rsvg_metadata_props_enumerate (const char *key, const char *value, gpointer user_data)
 {
     GString *metadata = (GString *) user_data;
@@ -448,41 +454,37 @@ static void
 rsvg_metadata_handler_start (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts)
 {
     RsvgSaxHandlerMetadata *z = (RsvgSaxHandlerMetadata *) self;
-    RsvgHandle *ctx = z->ctx;
 
-    g_string_append_printf (ctx->priv->metadata, "<%s ", name);
-    rsvg_property_bag_enumerate (atts, rsvg_metadata_props_enumerate, ctx->priv->metadata);
-    g_string_append (ctx->priv->metadata, ">\n");
+    rsvg_extra_handler_start (self, name, atts);
+
+    if (!z->string)
+        return;
+
+    g_string_append_printf (z->string, "<%s ", name);
+    rsvg_property_bag_enumerate (atts, rsvg_metadata_props_enumerate, z->string);
+    g_string_append (z->string, ">\n");
 }
 
 static void
 rsvg_metadata_handler_end (RsvgSaxHandler * self, const char *name)
 {
     RsvgSaxHandlerMetadata *z = (RsvgSaxHandlerMetadata *) self;
-    RsvgHandle *ctx = z->ctx;
 
-    if (!strcmp (name, "metadata")) {
-        if (ctx->priv->handler != NULL) {
-            ctx->priv->handler->free (ctx->priv->handler);
-            ctx->priv->handler = NULL;
-        }
-    } else
-        g_string_append_printf (ctx->priv->metadata, "</%s>\n", name);
+    if (strcmp (name, z->name) != 0) {
+        if (z->string)
+            g_string_append_printf (z->string, "</%s>\n", name);
+    } else {
+        rsvg_extra_handler_end (self, name);
+    }
 }
 
 static void
-rsvg_start_metadata (RsvgHandle * ctx, RsvgPropertyBag * atts)
+rsvg_start_metadata (RsvgHandle * ctx)
 {
-    RsvgSaxHandlerMetadata *handler = g_new0 (RsvgSaxHandlerMetadata, 1);
+    RsvgSaxHandlerMetadata *handler = rsvg_start_extra (ctx, "metadata", &ctx->priv->metadata);
 
-    handler->super.free = rsvg_metadata_handler_free;
-    handler->super.characters = rsvg_metadata_handler_characters;
     handler->super.start_element = rsvg_metadata_handler_start;
     handler->super.end_element = rsvg_metadata_handler_end;
-    handler->ctx = ctx;
-
-    ctx->priv->metadata = g_string_new (NULL);
-    ctx->priv->handler = &handler->super;
 }
 
 /* end metadata */
@@ -556,63 +558,103 @@ rsvg_xinclude_handler_end (RsvgSaxHandler * self, const char *name)
     }
 }
 
+static void
+_rsvg_set_xml_parse_options(xmlParserCtxtPtr xml_parser,
+                            RsvgHandle *ctx)
+{
+    xml_parser->options |= XML_PARSE_NONET;
+
+    if (ctx->priv->flags & RSVG_HANDLE_FLAG_UNLIMITED) {
+#if LIBXML_VERSION > 20632
+        xml_parser->options |= XML_PARSE_HUGE;
+#endif
+    }
+
+#if LIBXML_VERSION > 20800
+    xml_parser->options |= XML_PARSE_BIG_LINES;
+#endif
+}
+
 /* http://www.w3.org/TR/xinclude/ */
 static void
 rsvg_start_xinclude (RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
     RsvgSaxHandlerXinclude *handler;
-    GByteArray *data;
-    const char *href;
+    const char *href, *parse;
     gboolean success = FALSE;
 
     href = rsvg_property_bag_lookup (atts, "href");
-    if (href) {
-        data = _rsvg_acquire_xlink_href_resource (href, rsvg_handle_get_base_uri (ctx), NULL);
-        if (data) {
-            const char *parse;
-
-            parse = rsvg_property_bag_lookup (atts, "parse");
-            if (parse && !strcmp (parse, "text")) {
-                const char *encoding;
-                char *text_data;
-                gsize text_data_len;
-                gboolean text_data_needs_free = FALSE;
-
-                encoding = rsvg_property_bag_lookup (atts, "encoding");
-                if (encoding) {
-                    text_data =
-                        g_convert ((const char *) data->data, data->len, "utf-8", encoding, NULL,
-                                   &text_data_len, NULL);
-                    text_data_needs_free = TRUE;
-                } else {
-                    text_data = (char *) data->data;
-                    text_data_len = data->len;
-                }
+    if (href == NULL)
+        goto fallback;
 
-                rsvg_characters_impl (ctx, (const xmlChar *) text_data, text_data_len);
+    parse = rsvg_property_bag_lookup (atts, "parse");
+    if (parse && !strcmp (parse, "text")) {
+        char *data;
+        gsize data_len;
+        const char *encoding;
 
-                if (text_data_needs_free)
-                    g_free (text_data);
-            } else {
-                /* xml */
-                xmlDocPtr xml_doc;
-                xmlParserCtxtPtr xml_parser;
-                int result;
+        data = _rsvg_handle_acquire_data (ctx, href, NULL, &data_len, NULL);
+        if (data == NULL)
+            goto fallback;
 
-                xml_parser = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, ctx, NULL, 0, NULL);
-                result = xmlParseChunk (xml_parser, (char *) data->data, data->len, 0);
-                result = xmlParseChunk (xml_parser, "", 0, TRUE);
+        encoding = rsvg_property_bag_lookup (atts, "encoding");
+        if (encoding && g_ascii_strcasecmp (encoding, "UTF-8") != 0) {
+            char *text_data;
+            gsize text_data_len;
 
-                xml_doc = xml_parser->myDoc;
-                xmlFreeParserCtxt (xml_parser);
-                xmlFreeDoc (xml_doc);
-            }
+            text_data = g_convert (data, data_len, "utf-8", encoding, NULL,
+                                   &text_data_len, NULL);
+            g_free (data);
 
-            g_byte_array_free (data, TRUE);
-            success = TRUE;
+            data = text_data;
+            data_len = text_data_len;
         }
+
+        rsvg_characters_impl (ctx, (xmlChar *) data, data_len);
+
+        g_free (data);
+    } else {
+        /* xml */
+        GInputStream *stream;
+        GError *err = NULL;
+        xmlDocPtr xml_doc;
+        xmlParserCtxtPtr xml_parser;
+        xmlParserInputBufferPtr buffer;
+        xmlParserInputPtr input;
+
+        stream = _rsvg_handle_acquire_stream (ctx, href, NULL, NULL);
+        if (stream == NULL)
+            goto fallback;
+
+        xml_parser = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, ctx, NULL, 0, NULL);
+        _rsvg_set_xml_parse_options(xml_parser, ctx);
+
+        buffer = _rsvg_xml_input_buffer_new_from_stream (stream, NULL /* cancellable */, XML_CHAR_ENCODING_NONE, &err);
+        g_object_unref (stream);
+
+        input = xmlNewIOInputStream (xml_parser, buffer /* adopts */, XML_CHAR_ENCODING_NONE);
+
+        if (xmlPushInput (xml_parser, input) < 0) {
+            g_clear_error (&err);
+            xmlFreeInputStream (input);
+            xmlFreeParserCtxt (xml_parser);
+            goto fallback;
+        }
+
+        (void) xmlParseDocument (xml_parser);
+
+        xml_doc = xml_parser->myDoc;
+        xmlFreeParserCtxt (xml_parser);
+        if (xml_doc)
+            xmlFreeDoc (xml_doc);
+
+        g_clear_error (&err);
     }
 
+    success = TRUE;
+
+  fallback:
+
     /* needed to handle xi:fallback */
     handler = g_new0 (RsvgSaxHandlerXinclude, 1);
 
@@ -650,11 +692,11 @@ rsvg_start_element (void *data, const xmlChar * name, const xmlChar ** atts)
         if (!strcmp ((const char *) name, "style"))
             rsvg_start_style (ctx, bag);
         else if (!strcmp ((const char *) name, "title"))
-            rsvg_start_title (ctx, bag);
+            rsvg_start_title (ctx);
         else if (!strcmp ((const char *) name, "desc"))
-            rsvg_start_desc (ctx, bag);
+            rsvg_start_desc (ctx);
         else if (!strcmp ((const char *) name, "metadata"))
-            rsvg_start_metadata (ctx, bag);
+            rsvg_start_metadata (ctx);
         else if (!strcmp ((const char *) name, "include"))      /* xi:include */
             rsvg_start_xinclude (ctx, bag);
         else
@@ -684,10 +726,13 @@ rsvg_end_element (void *data, const xmlChar * name)
             ctx->priv->handler = NULL;
         }
 
-        if (ctx->priv->currentnode
-            && !strcmp ((const char *) name, ctx->priv->currentnode->type->str))
-            rsvg_pop_def_group (ctx);
+        if (ctx->priv->currentnode &&
+            !strcmp ((const char *) name, ctx->priv->currentnode->name))
+                rsvg_pop_def_group (ctx);
 
+        /* FIXMEchpe: shouldn't this check that currentnode == treebase or sth like that? */
+        if (ctx->priv->treebase && !strcmp ((const char *)name, "svg"))
+            _rsvg_node_svg_apply_atts ((RsvgNodeSvg *)ctx->priv->treebase, ctx);
     }
 }
 
@@ -699,62 +744,74 @@ _rsvg_node_chars_free (RsvgNode * node)
     _rsvg_node_free (node);
 }
 
-static void
-rsvg_characters_impl (RsvgHandle * ctx, const xmlChar * ch, int len)
+static RsvgNodeChars *
+rsvg_new_node_chars (const char *text,
+                     int len)
 {
     RsvgNodeChars *self;
 
-    if (!ch || !len)
-        return;
-
-	if (ctx->priv->currentnode)
-		{
-			if (!strcmp ("tspan", ctx->priv->currentnode->type->str) ||
-				!strcmp ("text", ctx->priv->currentnode->type->str))
-				{
-					guint i;
-
-					/* find the last CHARS node in the text or tspan node, so that we
-					   can coalesce the text, and thus avoid screwing up the Pango layouts */
-					self = NULL;
-					for (i = 0; i < ctx->priv->currentnode->children->len; i++) {
-						RsvgNode *node = g_ptr_array_index (ctx->priv->currentnode->children, i);
-						if (!strcmp (node->type->str, "RSVG_NODE_CHARS")) {
-							self = (RsvgNodeChars*)node;
-						}
-					}
-
-					if (self != NULL) {
-						if (!g_utf8_validate ((char *) ch, len, NULL)) {
-							char *utf8;
-							utf8 = rsvg_make_valid_utf8 ((char *) ch, len);
-							g_string_append (self->contents, utf8);
-							g_free (utf8);
-						} else {
-							g_string_append_len (self->contents, (char *)ch, len);
-						}
-
-						return;
-					}
-				}
-		}
-
     self = g_new (RsvgNodeChars, 1);
-    _rsvg_node_init (&self->super);
+    _rsvg_node_init (&self->super, RSVG_NODE_TYPE_CHARS);
 
-    if (!g_utf8_validate ((char *) ch, len, NULL)) {
+    if (!g_utf8_validate (text, len, NULL)) {
         char *utf8;
-        utf8 = rsvg_make_valid_utf8 ((char *) ch, len);
+        utf8 = rsvg_make_valid_utf8 (text, len);
         self->contents = g_string_new (utf8);
         g_free (utf8);
     } else {
-        self->contents = g_string_new_len ((char *) ch, len);
+        self->contents = g_string_new_len (text, len);
     }
 
-    self->super.type = g_string_new ("RSVG_NODE_CHARS");
     self->super.free = _rsvg_node_chars_free;
     self->super.state->cond_true = FALSE;
 
+    return self;
+}
+
+static void
+rsvg_characters_impl (RsvgHandle * ctx, const xmlChar * ch, int len)
+{
+    RsvgNodeChars *self;
+
+    if (!ch || !len)
+        return;
+
+    if (ctx->priv->currentnode) {
+        RsvgNodeType type = RSVG_NODE_TYPE (ctx->priv->currentnode);
+        if (type == RSVG_NODE_TYPE_TSPAN ||
+            type == RSVG_NODE_TYPE_TEXT) {
+            guint i;
+
+            /* find the last CHARS node in the text or tspan node, so that we
+               can coalesce the text, and thus avoid screwing up the Pango layouts */
+            self = NULL;
+            for (i = 0; i < ctx->priv->currentnode->children->len; i++) {
+                RsvgNode *node = g_ptr_array_index (ctx->priv->currentnode->children, i);
+                if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_CHARS) {
+                    self = (RsvgNodeChars*)node;
+                }
+                else if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_TSPAN) {
+                    self = NULL;
+                }
+            }
+
+            if (self != NULL) {
+                if (!g_utf8_validate ((char *) ch, len, NULL)) {
+                    char *utf8;
+                    utf8 = rsvg_make_valid_utf8 ((char *) ch, len);
+                    g_string_append (self->contents, utf8);
+                    g_free (utf8);
+                } else {
+                    g_string_append_len (self->contents, (char *)ch, len);
+                }
+
+                return;
+            }
+        }
+    }
+
+    self = rsvg_new_node_chars ((char *) ch, len);
+
     rsvg_defs_register_memory (ctx->priv->defs, (RsvgNode *) self);
     if (ctx->priv->currentnode)
         rsvg_node_group_pack (ctx->priv->currentnode, (RsvgNode *) self);
@@ -773,14 +830,6 @@ rsvg_characters (void *data, const xmlChar * ch, int len)
     rsvg_characters_impl (ctx, ch, len);
 }
 
-#if LIBXML_VERSION >= 20621
-#define RSVG_ENABLE_ENTITIES
-#elif defined(__GNUC__)
-#warning "libxml version less than 2.6.22. XML entities won't work"
-#endif
-
-#ifdef RSVG_ENABLE_ENTITIES
-
 static xmlEntityPtr
 rsvg_get_entity (void *data, const xmlChar * name)
 {
@@ -799,7 +848,6 @@ rsvg_entity_decl (void *data, const xmlChar * name, int type,
     RsvgHandle *ctx = (RsvgHandle *) data;
     GHashTable *entities = ctx->priv->entities;
     xmlEntityPtr entity;
-#if LIBXML_VERSION >= 20700
     xmlChar *resolvedSystemId = NULL, *resolvedPublicId = NULL;
 
     if (systemId)
@@ -808,66 +856,36 @@ rsvg_entity_decl (void *data, const xmlChar * name, int type,
         resolvedPublicId = xmlBuildRelativeURI (publicId, (xmlChar*) rsvg_handle_get_base_uri (ctx));
 
     if (type == XML_EXTERNAL_PARAMETER_ENTITY && !content) {
-      GByteArray *arr;
-
-      if (systemId)
-        arr = _rsvg_acquire_xlink_href_resource ((const char *) systemId,
-                                                 rsvg_handle_get_base_uri (ctx), NULL);
-      else if (publicId)
-        arr = _rsvg_acquire_xlink_href_resource ((const char *) publicId,
-                                                 rsvg_handle_get_base_uri (ctx), NULL);
-      if (arr) {
-        content = xmlCharStrndup ((const char*)arr->data, arr->len);
-        g_byte_array_free(arr, TRUE);
-      }
-    }
-      
-    entity = xmlNewEntity(NULL, name, type, resolvedPublicId, resolvedSystemId, content);
-
-    free(resolvedPublicId);
-    free(resolvedSystemId);
-
-    g_hash_table_insert (entities, g_strdup ((const char*) name), entity);
-#else
-    xmlChar *dupname;
-
-    entity = xmlMalloc (sizeof (xmlEntity));
-    memset (entity, 0, sizeof (xmlEntity));
-    entity->type = XML_ENTITY_DECL;
-    dupname = (xmlChar *) xmlMemStrdup ((const char *) name);
-    entity->name = dupname;
-
-    entity->etype = type;
-    if (content) {
-        entity->content = (xmlChar *) xmlMemStrdup ((const char *) content);
-        entity->length = strlen ((const char *) content);
-    } else if (systemId || publicId) {
-        GByteArray *data = NULL;
+        char *entity_data;
+        gsize entity_data_len;
 
         if (systemId)
-            data =
-                _rsvg_acquire_xlink_href_resource ((const char *) systemId,
-                                                   rsvg_handle_get_base_uri (ctx), NULL);
+            entity_data = _rsvg_handle_acquire_data (ctx,
+                                                     (const char *) systemId,
+                                                     NULL,
+                                                     &entity_data_len,
+                                                     NULL);
         else if (publicId)
-            data =
-                _rsvg_acquire_xlink_href_resource ((const char *) publicId,
-                                                   rsvg_handle_get_base_uri (ctx), NULL);
-
-        if (data) {
-            entity->SystemID = (xmlChar *) xmlMemStrdup ((const char *) systemId);
-            entity->ExternalID = (xmlChar *) xmlMemStrdup ((const char *) publicId);
-            entity->content = (xmlChar *) xmlMemStrdup ((const char *) data->data);
-            entity->length = data->len;
-
-            /* fool libxml2 into supporting SYSTEM and PUBLIC entities */
-            entity->etype = XML_INTERNAL_GENERAL_ENTITY;
+            entity_data = _rsvg_handle_acquire_data (ctx,
+                                                     (const char *) publicId,
+                                                     NULL,
+                                                     &entity_data_len,
+                                                     NULL);
+        else
+            entity_data = NULL;
 
-            g_byte_array_free (data, TRUE);
+        if (entity_data) {
+            content = xmlCharStrndup (entity_data, entity_data_len);
+            g_free (entity_data);
         }
     }
 
-    g_hash_table_insert (entities, dupname, entity);
-#endif
+    entity = xmlNewEntity(NULL, name, type, resolvedPublicId, resolvedSystemId, content);
+
+    xmlFree(resolvedPublicId);
+    xmlFree(resolvedSystemId);
+
+    g_hash_table_insert (entities, g_strdup ((const char*) name), entity);
 }
 
 static void
@@ -890,8 +908,6 @@ rsvg_get_parameter_entity (void *data, const xmlChar * name)
     return entity;
 }
 
-#endif
-
 static void
 rsvg_error_cb (void *data, const char *msg, ...)
 {
@@ -915,33 +931,40 @@ rsvg_processing_instruction (void *ctx, const xmlChar * target, const xmlChar *
         char **xml_atts;
 
         xml_atts = rsvg_css_parse_xml_attribute_string ((const char *) data);
-        atts = rsvg_property_bag_new ((const char **) xml_atts);
 
-        if (atts) {
+        if (xml_atts) {
             const char *value;
 
+            atts = rsvg_property_bag_new ((const char **) xml_atts);
             value = rsvg_property_bag_lookup (atts, "alternate");
-            if (!value || (strcmp (value, "no") != 0)) {
+            if (!value || !value[0] || (strcmp (value, "no") != 0)) {
                 value = rsvg_property_bag_lookup (atts, "type");
                 if (value && strcmp (value, "text/css") == 0) {
                     value = rsvg_property_bag_lookup (atts, "href");
-                    if (value) {
-                        GByteArray *style;
-
-                        style =
-                            _rsvg_acquire_xlink_href_resource (value,
-                                                               rsvg_handle_get_base_uri (handle),
-                                                               NULL);
-                        if (style) {
-                            rsvg_parse_cssbuffer (handle, (char *) style->data, style->len);
-                            g_byte_array_free (style, TRUE);
+                    if (value && value[0]) {
+                        char *style_data;
+                        gsize style_data_len;
+                        char *mime_type = NULL;
+
+                        style_data = _rsvg_handle_acquire_data (handle,
+                                                                value,
+                                                                &mime_type,
+                                                                &style_data_len,
+                                                                NULL);
+                        if (style_data && 
+                            mime_type &&
+                            strcmp (mime_type, "text/css") == 0) {
+                            rsvg_parse_cssbuffer (handle, style_data, style_data_len);
                         }
+
+                        g_free (mime_type);
+                        g_free (style_data);
                     }
                 }
             }
 
-            g_strfreev (xml_atts);
             rsvg_property_bag_free (atts);
+            g_strfreev (xml_atts);
         }
     }
 }
@@ -954,12 +977,10 @@ rsvg_SAX_handler_struct_init (void)
 
         memset (&rsvgSAXHandlerStruct, 0, sizeof (rsvgSAXHandlerStruct));
 
-#ifdef RSVG_ENABLE_ENTITIES
         rsvgSAXHandlerStruct.getEntity = rsvg_get_entity;
         rsvgSAXHandlerStruct.entityDecl = rsvg_entity_decl;
         rsvgSAXHandlerStruct.unparsedEntityDecl = rsvg_unparsed_entity_decl;
         rsvgSAXHandlerStruct.getParameterEntity = rsvg_get_parameter_entity;
-#endif
         rsvgSAXHandlerStruct.characters = rsvg_characters;
         rsvgSAXHandlerStruct.error = rsvg_error_cb;
         rsvgSAXHandlerStruct.cdataBlock = rsvg_characters;
@@ -977,26 +998,27 @@ rsvg_path_is_uri (char const *path)
     char const *p;
 
     if (path == NULL)
-	return FALSE;
+        return FALSE;
 
     if (strlen (path) < 4)
-	return FALSE;
+        return FALSE;
 
-    if (   (path[0] < 'a' || path[0] > 'z')
-	&& (path[0] < 'A' || path[0] > 'Z'))
-	return FALSE;
+    if ((path[0] < 'a' || path[0] > 'z') &&
+        (path[0] < 'A' || path[0] > 'Z')) {
+        return FALSE;
+    }
 
     for (p = &path[1];
-	    (*p >= 'a' && *p <= 'z') 
-	 || (*p >= 'A' && *p <= 'Z') 
-	 || (*p >= '0' && *p <= '9') 
-	 || *p == '+' 
-	 || *p == '-' 
-	 || *p == '.';
-	 p++);
+	    (*p >= 'a' && *p <= 'z') ||
+        (*p >= 'A' && *p <= 'Z') ||
+        (*p >= '0' && *p <= '9') ||
+         *p == '+' ||
+         *p == '-' ||
+         *p == '.';
+        p++);
 
     if (strlen (p) < 3)
-	return FALSE;
+        return FALSE;
 
     return (p[0] == ':' && p[1] == '/' && p[2] == '/');
 }
@@ -1022,7 +1044,7 @@ rsvg_get_base_uri_from_filename (const gchar * filename)
 }
 
 /**
- * rsvg_handle_set_base_uri
+ * rsvg_handle_set_base_uri:
  * @handle: A #RsvgHandle
  * @base_uri: The base uri
  *
@@ -1035,6 +1057,7 @@ void
 rsvg_handle_set_base_uri (RsvgHandle * handle, const char *base_uri)
 {
     gchar *uri;
+    GFile *file;
 
     g_return_if_fail (handle != NULL);
 
@@ -1042,16 +1065,45 @@ rsvg_handle_set_base_uri (RsvgHandle * handle, const char *base_uri)
 	return;
 
     if (rsvg_path_is_uri (base_uri)) 
-	uri = g_strdup (base_uri);
+        uri = g_strdup (base_uri);
     else
-	uri = rsvg_get_base_uri_from_filename (base_uri);
+        uri = rsvg_get_base_uri_from_filename (base_uri);
 
-    if (uri) {
-        if (handle->priv->base_uri)
-            g_free (handle->priv->base_uri);
-        handle->priv->base_uri = uri;
-        rsvg_defs_set_base_uri (handle->priv->defs, handle->priv->base_uri);
-    }
+    file = g_file_new_for_uri (uri ? uri : "data:");
+    rsvg_handle_set_base_gfile (handle, file);
+    g_object_unref (file);
+    g_free (uri);
+}
+
+/**
+ * rsvg_handle_set_base_gfile:
+ * @handle: a #RsvgHandle
+ * @base_file: a #GFile
+ *
+ * Set the base URI for @handle from @file.
+ * Note: This function may only be called before rsvg_handle_write()
+ * or rsvg_handle_read_stream_sync() has been called.
+ *
+ * Since: 2.32
+ */
+void
+rsvg_handle_set_base_gfile (RsvgHandle *handle,
+                            GFile      *base_file)
+{
+    RsvgHandlePrivate *priv;
+
+    g_return_if_fail (RSVG_IS_HANDLE (handle));
+    g_return_if_fail (G_IS_FILE (base_file));
+
+    priv = handle->priv;
+
+    g_object_ref (base_file);
+    if (priv->base_gfile)
+        g_object_unref (priv->base_gfile);
+    priv->base_gfile = base_file;
+
+    g_free (priv->base_uri);
+    priv->base_uri = g_file_get_uri (base_file);
 }
 
 /**
@@ -1061,9 +1113,9 @@ rsvg_handle_set_base_uri (RsvgHandle * handle, const char *base_uri)
  * Gets the base uri for this #RsvgHandle.
  *
  * Returns: the base uri, possibly null
- * Since: 2.9 (really present in 2.8 as well)
+ * Since: 2.8
  */
-G_CONST_RETURN char *
+const char *
 rsvg_handle_get_base_uri (RsvgHandle * handle)
 {
     g_return_val_if_fail (handle, NULL);
@@ -1071,7 +1123,7 @@ rsvg_handle_get_base_uri (RsvgHandle * handle)
 }
 
 /**
- * rsvg_error_quark
+ * rsvg_error_quark:
  *
  * The error domain for RSVG
  *
@@ -1088,39 +1140,46 @@ rsvg_error_quark (void)
 static void
 rsvg_set_error (GError **error, xmlParserCtxtPtr ctxt)
 {
-  xmlErrorPtr xerr;
+    xmlErrorPtr xerr;
 
-  xerr = xmlCtxtGetLastError (ctxt);
-  if (xerr) {
-    g_set_error (error, rsvg_error_quark (), 0,
-                 _("Error domain %d code %d on line %d column %d of %s: %s"),
-                 xerr->domain, xerr->code,
-                 xerr->line, xerr->int2,
-                 xerr->file ? xerr->file : "data",
-                 xerr->message ? xerr->message: "-");
-  } else {
-    g_set_error (error, rsvg_error_quark (), 0, _("Error parsing XML data"));
-  }
+    xerr = xmlCtxtGetLastError (ctxt);
+    if (xerr) {
+        g_set_error (error, rsvg_error_quark (), 0,
+                     _("Error domain %d code %d on line %d column %d of %s: %s"),
+                     xerr->domain, xerr->code,
+                     xerr->line, xerr->int2,
+                     xerr->file ? xerr->file : "data",
+                     xerr->message ? xerr->message: "-");
+    } else {
+        g_set_error (error, rsvg_error_quark (), 0, _("Error parsing XML data"));
+    }
 }
 
-static gboolean
-rsvg_handle_write_impl (RsvgHandle * handle, const guchar * buf, gsize count, GError ** error)
+static void
+create_xml_push_parser_ctxt (RsvgHandle *handle)
 {
-    GError *real_error = NULL;
-    int result;
-
-    rsvg_return_val_if_fail (handle != NULL, FALSE, error);
-
-    handle->priv->error = &real_error;
     if (handle->priv->ctxt == NULL) {
         handle->priv->ctxt = xmlCreatePushParserCtxt (&rsvgSAXHandlerStruct, handle, NULL, 0,
                                                       rsvg_handle_get_base_uri (handle));
+        _rsvg_set_xml_parse_options(handle->priv->ctxt, handle);
 
         /* if false, external entities work, but internal ones don't. if true, internal entities
            work, but external ones don't. favor internal entities, in order to not cause a
            regression */
         handle->priv->ctxt->replaceEntities = TRUE;
     }
+}
+
+static gboolean
+rsvg_handle_write_impl (RsvgHandle * handle, const guchar * buf, gsize count, GError ** error)
+{
+    GError *real_error = NULL;
+    int result;
+
+    rsvg_return_val_if_fail (handle != NULL, FALSE, error);
+
+    handle->priv->error = &real_error;
+    create_xml_push_parser_ctxt (handle);
 
     result = xmlParseChunk (handle->priv->ctxt, (char *) buf, count, 0);
     if (result != 0) {
@@ -1148,22 +1207,23 @@ rsvg_handle_close_impl (RsvgHandle * handle, GError ** error)
     handle->priv->error = &real_error;
 
     if (handle->priv->ctxt != NULL) {
-        xmlDocPtr xmlDoc;
+        xmlDocPtr xml_doc;
         int result;
 
-        xmlDoc = handle->priv->ctxt->myDoc;
+        xml_doc = handle->priv->ctxt->myDoc;
 
         result = xmlParseChunk (handle->priv->ctxt, "", 0, TRUE);
-        xmlFreeParserCtxt (handle->priv->ctxt);
-        xmlFreeDoc (xmlDoc);
-
         if (result != 0) {
             rsvg_set_error (error, handle->priv->ctxt);
+            xmlFreeParserCtxt (handle->priv->ctxt);
+            xmlFreeDoc (xml_doc);
             return FALSE;
         }
+
+        xmlFreeParserCtxt (handle->priv->ctxt);
+        xmlFreeDoc (xml_doc);
     }
 
-    rsvg_defs_resolve_all (handle->priv->defs);
     handle->priv->finished = TRUE;
     handle->priv->error = NULL;
 
@@ -1175,29 +1235,19 @@ rsvg_handle_close_impl (RsvgHandle * handle, GError ** error)
     return TRUE;
 }
 
-static void
-rsvg_state_free_func (gpointer data, gpointer user_data)
-{
-    rsvg_state_finalize ((RsvgState *) data);
-    g_slice_free (RsvgState, data);
-}
-
 void
 rsvg_drawing_ctx_free (RsvgDrawingCtx * handle)
 {
     rsvg_render_free (handle->render);
 
-    g_slist_foreach (handle->state, rsvg_state_free_func, (gpointer) handle);
-    g_slist_free (handle->state);
+    rsvg_state_free_all (handle->state);
 
 	/* the drawsub stack's nodes are owned by the ->defs */
 	g_slist_free (handle->drawsub_stack);
 
-    g_slist_free (handle->ptrs);
+    g_warn_if_fail (handle->acquired_nodes == NULL);
+    g_slist_free (handle->acquired_nodes);
 	
-    if (handle->base_uri)
-        g_free (handle->base_uri);
-
     if (handle->pango_context != NULL)
         g_object_unref (handle->pango_context);
 
@@ -1209,13 +1259,15 @@ rsvg_drawing_ctx_free (RsvgDrawingCtx * handle)
  * @handle: An #RsvgHandle
  *
  * Returns the SVG's metadata in UTF-8 or %NULL. You must make a copy
- * of this metadata if you wish to use it after #handle has been freed.
+ * of this metadata if you wish to use it after @handle has been freed.
  *
- * Returns: The SVG's title
+ * Returns: (nullable): The SVG's title
  *
  * Since: 2.9
+ *
+ * Deprecated: 2.36
  */
-G_CONST_RETURN char *
+const char *
 rsvg_handle_get_metadata (RsvgHandle * handle)
 {
     g_return_val_if_fail (handle, NULL);
@@ -1231,13 +1283,15 @@ rsvg_handle_get_metadata (RsvgHandle * handle)
  * @handle: An #RsvgHandle
  *
  * Returns the SVG's title in UTF-8 or %NULL. You must make a copy
- * of this title if you wish to use it after #handle has been freed.
+ * of this title if you wish to use it after @handle has been freed.
  *
- * Returns: The SVG's title
+ * Returns: (nullable): The SVG's title
  *
  * Since: 2.4
+ *
+ * Deprecated: 2.36
  */
-G_CONST_RETURN char *
+const char *
 rsvg_handle_get_title (RsvgHandle * handle)
 {
     g_return_val_if_fail (handle, NULL);
@@ -1253,13 +1307,15 @@ rsvg_handle_get_title (RsvgHandle * handle)
  * @handle: An #RsvgHandle
  *
  * Returns the SVG's description in UTF-8 or %NULL. You must make a copy
- * of this description if you wish to use it after #handle has been freed.
+ * of this description if you wish to use it after @handle has been freed.
  *
- * Returns: The SVG's description
+ * Returns: (nullable): The SVG's description
  *
  * Since: 2.4
+ *
+ * Deprecated: 2.36
  */
-G_CONST_RETURN char *
+const char *
 rsvg_handle_get_desc (RsvgHandle * handle)
 {
     g_return_val_if_fail (handle, NULL);
@@ -1271,9 +1327,9 @@ rsvg_handle_get_desc (RsvgHandle * handle)
 }
 
 /**
- * rsvg_handle_get_dimensions
+ * rsvg_handle_get_dimensions:
  * @handle: A #RsvgHandle
- * @dimension_data: A place to store the SVG's size
+ * @dimension_data: (out): A place to store the SVG's size
  *
  * Get the SVG's size. Do not call from within the size_func callback, because an infinite loop will occur.
  *
@@ -1286,23 +1342,24 @@ rsvg_handle_get_dimensions (RsvgHandle * handle, RsvgDimensionData * dimension_d
      * To prevent an infinite loop we are saving the state.
      */
     if (!handle->priv->in_loop) {
-	handle->priv->in_loop = TRUE;
-	rsvg_handle_get_dimensions_sub (handle, dimension_data, NULL);
-	handle->priv->in_loop = FALSE;
+        handle->priv->in_loop = TRUE;
+        rsvg_handle_get_dimensions_sub (handle, dimension_data, NULL);
+        handle->priv->in_loop = FALSE;
     } else {
-	/* Called within the size function, so return a standard size */
-	dimension_data->em = dimension_data->width = 1;
-	dimension_data->ex = dimension_data->height = 1;
+        /* Called within the size function, so return a standard size */
+        dimension_data->em = dimension_data->width = 1;
+        dimension_data->ex = dimension_data->height = 1;
     }
 }
 
 /**
- * rsvg_handle_get_dimensions_sub
+ * rsvg_handle_get_dimensions_sub:
  * @handle: A #RsvgHandle
- * @dimension_data: A place to store the SVG's size
- * @id: An element's id within the SVG, or NULL to get the dimension of the whole SVG. 
- * For example, if you have a layer called "layer1" for that you want to get the dimension, 
- * pass "#layer1" as the id.
+ * @dimension_data: (out): A place to store the SVG's size
+ * @id: (nullable): An element's id within the SVG, or %NULL to get
+ *   the dimension of the whole SVG.  For example, if you have a layer
+ *   called "layer1" for that you want to get the dimension, pass
+ *   "#layer1" as the id.
  *
  * Get the size of a subelement of the SVG file. Do not call from within the size_func callback, because an infinite loop will occur.
  *
@@ -1314,7 +1371,6 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
     cairo_t *cr;
     cairo_surface_t *target;
     RsvgDrawingCtx *draw;
-    RsvgCairoRender *render;
     RsvgNodeSvg *root = NULL;
     RsvgNode *sself = NULL;
     RsvgBbox bbox;
@@ -1327,81 +1383,73 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
     memset (dimension_data, 0, sizeof (RsvgDimensionData));
 
     if (id && *id) {
-	sself = (RsvgNode *) rsvg_defs_lookup (handle->priv->defs, id);
+        sself = rsvg_defs_lookup (handle->priv->defs, id);
 
-	if (sself == (RsvgNode *) handle->priv->treebase)
-	    id = NULL;
+        if (sself == handle->priv->treebase)
+            id = NULL;
     }
     else
-	sself = (RsvgNode *) handle->priv->treebase;
+        sself = handle->priv->treebase;
 
     if (!sself && id)
-	return FALSE;
+        return FALSE;
 
     root = (RsvgNodeSvg *) handle->priv->treebase;
 
     if (!root)
-	return FALSE;
+        return FALSE;
 
-    bbox.x = bbox.y = 0;
-    bbox.w = bbox.h = 1;
+    bbox.rect.x = bbox.rect.y = 0;
+    bbox.rect.width = bbox.rect.height = 1;
 
     if (!id && (root->w.factor == 'p' || root->h.factor == 'p')
-	    && root->vbox.active && root->vbox.w > 0. && root->vbox.h > 0.)
-	handle_subelement = TRUE;
+            && !root->vbox.active)
+        handle_subelement = TRUE;
     else if (!id && root->w.length != -1 && root->h.length != -1)
-	handle_subelement = FALSE;
+        handle_subelement = FALSE;
 
     if (handle_subelement == TRUE) {
-	target = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
-					 1, 1);
-	cr = cairo_create  (target);
+        target = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+                                             1, 1);
+        cr = cairo_create  (target);
 
-	draw = rsvg_cairo_new_drawing_ctx (cr, handle);
-    
-	if (!draw) {
-	    cairo_destroy (cr);
-	    cairo_surface_destroy (target);
-	    
-	    return FALSE;
-	}
+        draw = rsvg_cairo_new_drawing_ctx (cr, handle);
 
-	while (sself != NULL) {
-	    draw->drawsub_stack = g_slist_prepend (draw->drawsub_stack, sself);
-	    sself = sself->parent;
-	}
+        if (!draw) {
+            cairo_destroy (cr);
+            cairo_surface_destroy (target);
 
-	rsvg_state_push (draw);
-	cairo_save (cr);
+            return FALSE;
+        }
 
-	rsvg_node_draw ((RsvgNode *) handle->priv->treebase, draw, 0);
-	render = (RsvgCairoRender *) draw->render;
+        while (sself != NULL) {
+            draw->drawsub_stack = g_slist_prepend (draw->drawsub_stack, sself);
+            sself = sself->parent;
+        }
+
+        rsvg_state_push (draw);
+        cairo_save (cr);
 
-	bbox.x = render->bbox.x;
-	bbox.y = render->bbox.y;
-	bbox.w = render->bbox.w;
-	bbox.h = render->bbox.h;
+        rsvg_node_draw (handle->priv->treebase, draw, 0);
+        bbox = RSVG_CAIRO_RENDER (draw->render)->bbox;
 
-	cairo_restore (cr);
-	rsvg_state_pop (draw);
-	rsvg_drawing_ctx_free (draw);
-	cairo_destroy (cr);
-	cairo_surface_destroy (target);
+        cairo_restore (cr);
+        rsvg_state_pop (draw);
+        rsvg_drawing_ctx_free (draw);
+        cairo_destroy (cr);
+        cairo_surface_destroy (target);
 
-	dimension_data->width = (int) (_rsvg_css_hand_normalize_length_sub (&root->w, bbox.w, handle->priv->dpi_x,
-									    bbox.w + bbox.x * 2, 12) + 0.5);
-	dimension_data->height = (int) (_rsvg_css_hand_normalize_length_sub (&root->h, bbox.h, handle->priv->dpi_y, 
-									    bbox.h + bbox.y * 2,
-									    12) + 0.5);
+        dimension_data->width = bbox.rect.width;
+        dimension_data->height = bbox.rect.height;
     } else {
-	bbox.w = root->vbox.w; 
-	bbox.h = root->vbox.h; 
-
-	dimension_data->width = (int) (_rsvg_css_hand_normalize_length (&root->w, handle->priv->dpi_x,
-									bbox.w + bbox.x * 2, 12) + 0.5);
-	dimension_data->height = (int) (_rsvg_css_hand_normalize_length (&root->h, handle->priv->dpi_y, 
-									bbox.h + bbox.y * 2,
-									12) + 0.5);
+        bbox.rect.width = root->vbox.rect.width;
+        bbox.rect.height = root->vbox.rect.height;
+
+        dimension_data->width = (int) (_rsvg_css_hand_normalize_length (&root->w, handle->priv->dpi_x,
+                                       bbox.rect.width + bbox.rect.x * 2, 12) + 0.5);
+        dimension_data->height = (int) (_rsvg_css_hand_normalize_length (&root->h, handle->priv->dpi_y,
+                                         bbox.rect.height + bbox.rect.y * 2,
+                                         12) + 0.5);
     }
     
     dimension_data->em = dimension_data->width;
@@ -1415,12 +1463,12 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
 }
 
 /**
- * rsvg_handle_get_position_sub
+ * rsvg_handle_get_position_sub:
  * @handle: A #RsvgHandle
- * @position_data: A place to store the SVG fragment's position.
+ * @position_data: (out): A place to store the SVG fragment's position.
  * @id: An element's id within the SVG.
  * For example, if you have a layer called "layer1" for that you want to get
- * the position, pass "#layer1" as the id.
+ * the position, pass "##layer1" as the id.
  *
  * Get the position of a subelement of the SVG file. Do not call from within
  * the size_func callback, because an infinite loop will occur.
@@ -1430,96 +1478,84 @@ rsvg_handle_get_dimensions_sub (RsvgHandle * handle, RsvgDimensionData * dimensi
 gboolean
 rsvg_handle_get_position_sub (RsvgHandle * handle, RsvgPositionData * position_data, const char *id)
 {
-	RsvgDrawingCtx		*draw;
-	RsvgNodeSvg			*root;
-	RsvgNode			*node;
-	RsvgBbox			 bbox;
-	RsvgDimensionData    dimension_data;
-	cairo_surface_t		*target = NULL;
-	cairo_t				*cr = NULL;
-	gboolean			 ret = FALSE;
-
-	g_return_val_if_fail (handle, FALSE);
-	g_return_val_if_fail (position_data, FALSE);
-
-	/* Short-cut when no id is given. */
-	if (NULL == id || '\0' == *id) {
-		position_data->x = 0;
-		position_data->y = 0;
-		return TRUE;
-	}
+    RsvgDrawingCtx		*draw;
+    RsvgNodeSvg			*root;
+    RsvgNode			*node;
+    RsvgBbox			 bbox;
+    RsvgDimensionData    dimension_data;
+    cairo_surface_t		*target = NULL;
+    cairo_t				*cr = NULL;
+    gboolean			 ret = FALSE;
 
-	memset (position_data, 0, sizeof (*position_data));
-	memset (&dimension_data, 0, sizeof (dimension_data));
-
-	node = (RsvgNode *) rsvg_defs_lookup (handle->priv->defs, id);
-	if (!node) {
-		return FALSE;
-	} else if (node == (RsvgNode *) handle->priv->treebase) {
-		/* Root node. */
-		position_data->x = 0;
-		position_data->y = 0;
-		return TRUE;
-	}
+    g_return_val_if_fail (handle, FALSE);
+    g_return_val_if_fail (position_data, FALSE);
 
-	root = (RsvgNodeSvg *) handle->priv->treebase;
-	if (!root)
-		return FALSE;
+    /* Short-cut when no id is given. */
+    if (NULL == id || '\0' == *id) {
+        position_data->x = 0;
+        position_data->y = 0;
+        return TRUE;
+    }
 
-	target = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 1, 1);
-	cr = cairo_create  (target);
-	draw = rsvg_cairo_new_drawing_ctx (cr, handle);
-	if (!draw)
-	    goto bail;
+    memset (position_data, 0, sizeof (*position_data));
+    memset (&dimension_data, 0, sizeof (dimension_data));
 
-	while (node != NULL) {
-	    draw->drawsub_stack = g_slist_prepend (draw->drawsub_stack, node);
-	    node = node->parent;
-	}
+    node = rsvg_defs_lookup (handle->priv->defs, id);
+    if (!node) {
+        return FALSE;
+    } else if (node == handle->priv->treebase) {
+        /* Root node. */
+        position_data->x = 0;
+        position_data->y = 0;
+        return TRUE;
+    }
 
-	rsvg_state_push (draw);
-	cairo_save (cr);
+    root = (RsvgNodeSvg *) handle->priv->treebase;
+    if (!root)
+        return FALSE;
 
-	rsvg_node_draw ((RsvgNode *) handle->priv->treebase, draw, 0);
-	bbox.x = ((RsvgCairoRender *) draw->render)->bbox.x;
-	bbox.y = ((RsvgCairoRender *) draw->render)->bbox.y;
-	bbox.w = ((RsvgCairoRender *) draw->render)->bbox.w;
-	bbox.h = ((RsvgCairoRender *) draw->render)->bbox.h;
+    target = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 1, 1);
+    cr = cairo_create  (target);
+    draw = rsvg_cairo_new_drawing_ctx (cr, handle);
+    if (!draw)
+        goto bail;
 
-	cairo_restore (cr);
-	rsvg_state_pop (draw);
-	rsvg_drawing_ctx_free (draw);
+    while (node != NULL) {
+        draw->drawsub_stack = g_slist_prepend (draw->drawsub_stack, node);
+        node = node->parent;
+    }
 
-	/* Calculate position.
-	 * TODO: figure out what "bbox.w + bbox.x * 2" is or means
-	 * (copied from rsvg_handle_get_dimensions_sub(). - robsta */
-	position_data->x = (int) (_rsvg_css_hand_normalize_length_sub (&root->x, bbox.x, handle->priv->dpi_x,
-									    bbox.w + bbox.x * 2, 12) + 0.5);
-	position_data->y = (int) (_rsvg_css_hand_normalize_length_sub (&root->y, bbox.y, handle->priv->dpi_y, 
-									    bbox.h + bbox.y * 2, 12) + 0.5);
+    rsvg_state_push (draw);
+    cairo_save (cr);
 
-	/* Also calculate dimension for the size callback, while re're at it. */
-	dimension_data.width = (int) (_rsvg_css_hand_normalize_length_sub (&root->w, bbox.w, handle->priv->dpi_x,
-									    bbox.w + bbox.x * 2, 12) + 0.5);
-	dimension_data.height = (int) (_rsvg_css_hand_normalize_length_sub (&root->h, bbox.h, handle->priv->dpi_y, 
-									    bbox.h + bbox.y * 2, 12) + 0.5);
+    rsvg_node_draw (handle->priv->treebase, draw, 0);
+    bbox = RSVG_CAIRO_RENDER (draw->render)->bbox;
 
-	dimension_data.em = dimension_data.width;
-	dimension_data.ex = dimension_data.height;
+    cairo_restore (cr);
+    rsvg_state_pop (draw);
+    rsvg_drawing_ctx_free (draw);
 
-	if (handle->priv->size_func)
-	    (*handle->priv->size_func) (&dimension_data.width, &dimension_data.height,
-	                                handle->priv->user_data);
+    position_data->x = bbox.rect.x;
+    position_data->y = bbox.rect.y;
+    dimension_data.width = bbox.rect.width;
+    dimension_data.height = bbox.rect.height;
 
-        ret = TRUE;
+    dimension_data.em = dimension_data.width;
+    dimension_data.ex = dimension_data.height;
+
+    if (handle->priv->size_func)
+        (*handle->priv->size_func) (&dimension_data.width, &dimension_data.height,
+                                    handle->priv->user_data);
+
+    ret = TRUE;
 
 bail:
-	if (cr)
-		cairo_destroy (cr);
-	if (target)
-		cairo_surface_destroy (target);
+    if (cr)
+        cairo_destroy (cr);
+    if (target)
+        cairo_surface_destroy (target);
 
-	return ret;
+    return ret;
 }
 
 /** 
@@ -1546,11 +1582,11 @@ rsvg_handle_has_sub (RsvgHandle * handle,
 }
 
 /** 
- * rsvg_set_default_dpi
+ * rsvg_set_default_dpi:
  * @dpi: Dots Per Inch (aka Pixels Per Inch)
  *
  * Sets the DPI for the all future outgoing pixbufs. Common values are
- * 75, 90, and 300 DPI. Passing a number <= 0 to #dpi will 
+ * 75, 90, and 300 DPI. Passing a number <= 0 to @dpi will
  * reset the DPI to whatever the default value happens to be.
  *
  * Since: 2.8
@@ -1562,12 +1598,12 @@ rsvg_set_default_dpi (double dpi)
 }
 
 /** 
- * rsvg_set_default_dpi_x_y
+ * rsvg_set_default_dpi_x_y:
  * @dpi_x: Dots Per Inch (aka Pixels Per Inch)
  * @dpi_y: Dots Per Inch (aka Pixels Per Inch)
  *
  * Sets the DPI for the all future outgoing pixbufs. Common values are
- * 75, 90, and 300 DPI. Passing a number <= 0 to #dpi will 
+ * 75, 90, and 300 DPI. Passing a number <= 0 to @dpi will
  * reset the DPI to whatever the default value happens to be.
  *
  * Since: 2.8
@@ -1587,12 +1623,12 @@ rsvg_set_default_dpi_x_y (double dpi_x, double dpi_y)
 }
 
 /**
- * rsvg_handle_set_dpi
+ * rsvg_handle_set_dpi:
  * @handle: An #RsvgHandle
  * @dpi: Dots Per Inch (aka Pixels Per Inch)
  *
  * Sets the DPI for the outgoing pixbuf. Common values are
- * 75, 90, and 300 DPI. Passing a number <= 0 to #dpi will 
+ * 75, 90, and 300 DPI. Passing a number <= 0 to @dpi will
  * reset the DPI to whatever the default value happens to be.
  *
  * Since: 2.8
@@ -1604,13 +1640,13 @@ rsvg_handle_set_dpi (RsvgHandle * handle, double dpi)
 }
 
 /**
- * rsvg_handle_set_dpi_x_y
+ * rsvg_handle_set_dpi_x_y:
  * @handle: An #RsvgHandle
  * @dpi_x: Dots Per Inch (aka Pixels Per Inch)
  * @dpi_y: Dots Per Inch (aka Pixels Per Inch)
  *
  * Sets the DPI for the outgoing pixbuf. Common values are
- * 75, 90, and 300 DPI. Passing a number <= 0 to #dpi_x or #dpi_y will 
+ * 75, 90, and 300 DPI. Passing a number <= 0 to #dpi_x or @dpi_y will
  * reset the DPI to whatever the default value happens to be.
  *
  * Since: 2.8
@@ -1634,7 +1670,7 @@ rsvg_handle_set_dpi_x_y (RsvgHandle * handle, double dpi_x, double dpi_y)
 /**
  * rsvg_handle_set_size_callback:
  * @handle: An #RsvgHandle
- * @size_func: A sizing function, or %NULL
+ * @size_func: (nullable): A sizing function, or %NULL
  * @user_data: User data to pass to @size_func, or %NULL
  * @user_data_destroy: Destroy function for @user_data, or %NULL
  *
@@ -1645,6 +1681,31 @@ rsvg_handle_set_dpi_x_y (RsvgHandle * handle, double dpi_x, double dpi_y)
  * arguments are set to -1.
  *
  * Deprecated: Set up a cairo matrix and use rsvg_handle_render_cairo() instead.
+ * You can call rsvg_handle_get_dimensions() to figure out the size of your SVG,
+ * and then scale it to the desired size via Cairo.  For example, the following
+ * code renders an SVG at a specified size, scaled proportionally from whatever
+ * original size it may have had:
+ *
+ * |[<!-- language="C" -->
+ * void
+ * render_scaled_proportionally (RsvgHandle *handle, cairo_t cr, int width, int height)
+ * {
+ *     RsvgDimensionData dimensions;
+ *     double x_factor, y_factor;
+ *     double scale_factor;
+ * 
+ *     rsvg_handle_get_dimensions (handle, &dimensions);
+ * 
+ *     x_factor = (double) width / dimensions.width;
+ *     y_factor = (double) height / dimensions.height;
+ * 
+ *     scale_factor = MIN (x_factor, y_factor);
+ * 
+ *     cairo_scale (cr, scale_factor, scale_factor);
+ * 
+ *     rsvg_handle_render_cairo (handle, cr);
+ * }
+ * ]|
  **/
 void
 rsvg_handle_set_size_callback (RsvgHandle * handle,
@@ -1663,79 +1724,98 @@ rsvg_handle_set_size_callback (RsvgHandle * handle,
 
 /**
  * rsvg_handle_write:
- * @handle: An #RsvgHandle
- * @buf: Pointer to svg data
+ * @handle: an #RsvgHandle
+ * @buf: (array length=count) (element-type guchar): pointer to svg data
  * @count: length of the @buf buffer in bytes
- * @error: return location for errors
+ * @error: (allow-none): a location to store a #GError, or %NULL
  *
- * Loads the next @count bytes of the image.  This will return #TRUE if the data
- * was loaded successful, and #FALSE if an error occurred.  In the latter case,
- * the loader will be closed, and will not accept further writes. If FALSE is
- * returned, @error will be set to an error from the #RSVG_ERROR domain.
+ * Loads the next @count bytes of the image.  This will return %TRUE if the data
+ * was loaded successful, and %FALSE if an error occurred.  In the latter case,
+ * the loader will be closed, and will not accept further writes. If %FALSE is
+ * returned, @error will be set to an error from the #RsvgError domain. Errors
+ * from #GIOErrorEnum are also possible.
  *
- * Returns: #TRUE if the write was successful, or #FALSE if there was an
- * error.
+ * Returns: %TRUE on success, or %FALSE on error
  **/
 gboolean
 rsvg_handle_write (RsvgHandle * handle, const guchar * buf, gsize count, GError ** error)
 {
+    RsvgHandlePrivate *priv;
+
     rsvg_return_val_if_fail (handle, FALSE, error);
-    rsvg_return_val_if_fail (!handle->priv->is_closed, FALSE, error);
+    priv = handle->priv;
 
-    if (handle->priv->first_write) {
-        handle->priv->first_write = FALSE;
+    rsvg_return_val_if_fail (!priv->is_closed, FALSE, error);
+
+    if (priv->first_write) {
+        priv->first_write = FALSE;
 
         /* 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)) {
-            handle->priv->is_gzipped = TRUE;
-
-#ifdef HAVE_SVGZ
-            handle->priv->gzipped_data = GSF_OUTPUT (gsf_output_memory_new ());
+#if GLIB_CHECK_VERSION (2, 24, 0)
+            priv->data_input_stream = g_memory_input_stream_new ();
+#else
+            priv->gzipped_data = GSF_OUTPUT (gsf_output_memory_new ());
 #endif
         }
     }
 
-    if (handle->priv->is_gzipped) {
-#ifdef HAVE_SVGZ
-        return gsf_output_write (handle->priv->gzipped_data, count, buf);
+#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;
+    }
 #else
-        return FALSE;
+    if (priv->gzipped_data)
+        return gsf_output_write (handle->priv->gzipped_data, count, buf);
 #endif
-    }
 
     return rsvg_handle_write_impl (handle, buf, count, error);
 }
 
 /**
  * rsvg_handle_close:
- * @handle: A #RsvgHandle
- * @error: A #GError
+ * @handle: a #RsvgHandle
+ * @error: (allow-none): a location to store a #GError, or %NULL
  *
  * Closes @handle, to indicate that loading the image is complete.  This will
- * return #TRUE if the loader closed successfully.  Note that @handle isn't
+ * return %TRUE if the loader closed successfully.  Note that @handle isn't
  * freed until @g_object_unref is called.
  *
- * Returns: #TRUE if the loader closed successfully, or #FALSE if there was
- * an error.
+ * Returns: %TRUE on success, or %FALSE on error
  **/
 gboolean
 rsvg_handle_close (RsvgHandle * handle, GError ** error)
 {
+    RsvgHandlePrivate *priv;
+
     rsvg_return_val_if_fail (handle, FALSE, error);
+    priv = handle->priv;
+
+    if (priv->is_closed)
+          return TRUE;
 
-	if (handle->priv->is_closed)
-		return TRUE;
+#if GLIB_CHECK_VERSION (2, 24, 0)
+    if (priv->data_input_stream) {
+        gboolean ret;
 
-#if HAVE_SVGZ
-    if (handle->priv->is_gzipped) {
+        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;
+
+        return ret;
+    }
+#else
+    if (priv->gzipped_data) {
         GsfInput *gzip;
         const guchar *bytes;
         gsize size;
         gsize remaining;
 
-        bytes = gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (handle->priv->gzipped_data));
-        size = gsf_output_size (handle->priv->gzipped_data);
+        bytes = gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (priv->gzipped_data));
+        size = gsf_output_size (priv->gzipped_data);
 
         gzip =
             GSF_INPUT (gsf_input_gzip_new
@@ -1762,47 +1842,302 @@ rsvg_handle_close (RsvgHandle * handle, GError ** error)
             }
             remaining = gsf_input_remaining (gzip);
         }
-        g_object_unref (G_OBJECT (gzip));
+        g_object_unref (gzip);
 
         /* close parent */
-        gsf_output_close (handle->priv->gzipped_data);
+        gsf_output_close (priv->gzipped_data);
+        g_object_unref (priv->gzipped_data);
+        priv->gzipped_data = NULL;
     }
-#endif
+#endif /* GIO >= 2.24.0 */
 
     return rsvg_handle_close_impl (handle, error);
 }
 
 /**
+ * rsvg_handle_read_stream_sync:
+ * @handle: a #RsvgHandle
+ * @stream: a #GInputStream
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): a location to store a #GError, or %NULL
+ *
+ * Reads @stream and writes the data from it to @handle.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the
+ * operation was cancelled, the error %G_IO_ERROR_CANCELLED will be
+ * returned.
+ *
+ * Returns: %TRUE if reading @stream succeeded, or %FALSE otherwise
+ *   with @error filled in
+ *
+ * Since: 2.32
+ */
+gboolean
+rsvg_handle_read_stream_sync (RsvgHandle   *handle,
+                              GInputStream *stream,
+                              GCancellable *cancellable,
+                              GError      **error)
+{
+    RsvgHandlePrivate *priv;
+    xmlParserInputBufferPtr buffer;
+    xmlParserInputPtr input;
+    int result;
+    xmlDocPtr doc;
+    GError *err = NULL;
+    gboolean res = FALSE;
+    const guchar *buf;
+
+    g_return_val_if_fail (RSVG_IS_HANDLE (handle), FALSE);
+    g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+    g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    priv = handle->priv;
+
+    /* 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) {
+        g_object_unref (stream);
+        return FALSE;
+    }
+    buf = g_buffered_input_stream_peek_buffer (G_BUFFERED_INPUT_STREAM (stream), NULL);
+    if ((buf[0] == 0x1f) && (buf[1] == 0x8b)) {
+#if GLIB_CHECK_VERSION (2, 24, 0)
+        GConverter *converter;
+        GInputStream *conv_stream;
+
+        converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP));
+        conv_stream = g_converter_input_stream_new (stream, converter);
+        g_object_unref (converter);
+        g_object_unref (stream);
+
+        stream = conv_stream;
+#else
+        /* Defer all writes to the old implementation; it will handle
+         * decompression by itself.
+         */
+        if (!rsvg_handle_write (handle, buf, 2, error)) {
+            g_object_unref (stream);
+            return FALSE;
+        }
+#endif
+    }
+
+    priv->error = &err;
+    priv->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+#if GLIB_CHECK_VERSION (2, 24, 0)
+    create_xml_push_parser_ctxt (handle);
+    buffer = _rsvg_xml_input_buffer_new_from_stream (stream, cancellable, XML_CHAR_ENCODING_NONE, &err);
+    input = xmlNewIOInputStream (priv->ctxt, buffer, XML_CHAR_ENCODING_NONE);
+
+    if (xmlPushInput (priv->ctxt, input) < 0) {
+        rsvg_set_error (error, priv->ctxt);
+        xmlFreeInputStream (input);
+        goto out;
+    }
+
+    result = xmlParseDocument (priv->ctxt);
+    if (result != 0) {
+        if (err)
+            g_propagate_error (error, err);
+        else
+            rsvg_set_error (error, handle->priv->ctxt);
+
+        goto out;
+    }
+
+    if (err != NULL) {
+        g_propagate_error (error, err);
+        goto out;
+    }
+
+    doc = priv->ctxt->myDoc;
+    xmlFreeParserCtxt (priv->ctxt);
+    priv->ctxt = NULL;
+
+    xmlFreeDoc (doc);
+
+    priv->finished = TRUE;
+
+    res = TRUE;
+#else
+    {
+        gssize num_read;
+        char read_buf[8192];
+
+        while ((num_read = g_input_stream_read (stream,
+                                                read_buf,
+                                                sizeof (read_buf),
+                                                priv->cancellable,
+                                                priv->error)) != 0) {
+            res = rsvg_handle_write (handle, read_buf, num_read, priv->error);
+            if (!res) {
+                /* We can't report the GError from g_input_stream_close() if
+                 * there's already an error from rsvg_handle_write().
+                 */
+                g_input_stream_close (stream, priv->cancellable, NULL);
+                goto out;
+            }
+        }
+
+        if (!g_input_stream_close (stream, priv->cancellable, priv->error))
+            goto out;
+
+        if (!rsvg_handle_close (handle, priv->error))
+            goto out;
+
+        res = TRUE;
+    }
+#endif
+
+  out:
+
+    g_object_unref (stream);
+
+    priv->error = NULL;
+
+    if (priv->cancellable) {
+        g_object_unref (priv->cancellable);
+        priv->cancellable = NULL;
+    }
+
+    return res;
+}
+
+/**
+ * rsvg_handle_new_from_gfile_sync:
+ * @file: a #GFile
+ * @flags: flags from #RsvgHandleFlags
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): a location to store a #GError, or %NULL
+ *
+ * Creates a new #RsvgHandle for @file.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the
+ * operation was cancelled, the error %G_IO_ERROR_CANCELLED will be
+ * returned.
+ *
+ * Returns: a new #RsvgHandle on success, or %NULL with @error filled in
+ *
+ * Since: 2.32
+ */
+RsvgHandle *
+rsvg_handle_new_from_gfile_sync (GFile          *file,
+                                 RsvgHandleFlags flags,
+                                 GCancellable   *cancellable,
+                                 GError        **error)
+{
+    RsvgHandle *handle;
+    GFileInputStream *stream;
+
+    g_return_val_if_fail (G_IS_FILE (file), NULL);
+    g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+    stream = g_file_read (file, cancellable, error);
+    if (stream == NULL)
+        return NULL;
+
+    handle = rsvg_handle_new_from_stream_sync (G_INPUT_STREAM (stream), file,
+                                               flags, cancellable, error);
+    g_object_unref (stream);
+
+    return handle;
+}
+
+/**
+ * rsvg_handle_new_from_stream_sync:
+ * @input_stream: a #GInputStream
+ * @base_file: (allow-none): a #GFile, or %NULL
+ * @flags: flags from #RsvgHandleFlags
+ * @cancellable: (allow-none): a #GCancellable, or %NULL
+ * @error: (allow-none): a location to store a #GError, or %NULL
+ *
+ * Creates a new #RsvgHandle for @stream.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the
+ * operation was cancelled, the error %G_IO_ERROR_CANCELLED will be
+ * returned.
+ *
+ * Returns: a new #RsvgHandle on success, or %NULL with @error filled in
+ *
+ * Since: 2.32
+ */
+RsvgHandle *
+rsvg_handle_new_from_stream_sync (GInputStream   *input_stream,
+                                  GFile          *base_file,
+                                  RsvgHandleFlags flags,
+                                  GCancellable    *cancellable,
+                                  GError         **error)
+{
+    RsvgHandle *handle;
+
+    g_return_val_if_fail (G_IS_INPUT_STREAM (input_stream), NULL);
+    g_return_val_if_fail (base_file == NULL || G_IS_FILE (base_file), NULL);
+    g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+    g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+    handle = rsvg_handle_new_with_flags (flags);
+
+    if (base_file)
+        rsvg_handle_set_base_gfile (handle, base_file);
+
+    if (!rsvg_handle_read_stream_sync (handle, input_stream, cancellable, error)) {
+        g_object_unref (handle);
+        return NULL;
+    }
+
+    return handle;
+}
+
+/**
  * rsvg_init:
  *
  * Initializes librsvg
  * Since: 2.9
+ * Deprecated: 2.36: Use g_type_init()
  **/
 void
 rsvg_init (void)
 {
-    g_type_init ();
+    RSVG_G_TYPE_INIT;
 
-#ifdef HAVE_SVGZ
+#if !(GLIB_CHECK_VERSION (2, 24, 0))
     gsf_init ();
 #endif
-
-    xmlInitParser ();
 }
 
 /**
  * rsvg_term:
  *
- * De-initializes librsvg
+ * This function does nothing.
+ *
  * Since: 2.9
+ * Deprecated: 2.36
  **/
 void
 rsvg_term (void)
 {
-#ifdef HAVE_SVGZ
+#if !(GLIB_CHECK_VERSION (2, 24, 0))
     gsf_shutdown ();
 #endif
+}
 
+/**
+ * rsvg_cleanup:
+ *
+ * This function should not be called from normal programs.
+ * See xmlCleanupParser() for more information.
+ *
+ * Since: 2.36
+ **/
+void
+rsvg_cleanup (void)
+{
     xmlCleanupParser ();
 }
 
@@ -1824,25 +2159,73 @@ rsvg_push_discrete_layer (RsvgDrawingCtx * ctx)
     ctx->render->push_discrete_layer (ctx);
 }
 
+/*
+ * rsvg_acquire_node:
+ * @ctx: The drawing context in use
+ * @url: The IRI to lookup
+ *
+ * Use this function when looking up urls to other nodes. This
+ * function does proper recursion checking and thereby avoids
+ * infinite loops.
+ *
+ * Nodes acquired by this function must be released using
+ * rsvg_release_node() in reverse acquiring order.
+ *
+ * Returns: The node referenced by @url or %NULL if the @url
+ *          does not reference a node.
+ */
+RsvgNode *
+rsvg_acquire_node (RsvgDrawingCtx * ctx, const char *url)
+{
+  RsvgNode *node;
+
+  node = rsvg_defs_lookup (ctx->defs, url);
+  if (node == NULL)
+    return NULL;
+
+  if (g_slist_find (ctx->acquired_nodes, node))
+    return NULL;
+
+  ctx->acquired_nodes = g_slist_prepend (ctx->acquired_nodes, node);
+
+  return node;
+}
+
+/*
+ * rsvg_release_node:
+ * @ctx: The drawing context the node was acquired from
+ * @node: Node to release
+ *
+ * Releases a node previously acquired via rsvg_acquire_node().
+ *
+ * if @node is %NULL, this function does nothing.
+ */
 void
-rsvg_render_path (RsvgDrawingCtx * ctx, const char *d)
+rsvg_release_node (RsvgDrawingCtx * ctx, RsvgNode *node)
 {
-    /* todo: store and use the bpath higher up */
-    RsvgBpathDef *bpath_def;
+  if (node == NULL)
+    return;
 
-    bpath_def = rsvg_parse_path (d);
-    rsvg_bpath_def_art_finish (bpath_def);
+  g_return_if_fail (ctx->acquired_nodes != NULL);
+  g_return_if_fail (ctx->acquired_nodes->data == node);
 
-    ctx->render->render_path (ctx, bpath_def);
-    rsvg_render_markers (bpath_def, ctx);
+  ctx->acquired_nodes = g_slist_remove (ctx->acquired_nodes, node);
+}
 
-    rsvg_bpath_def_free (bpath_def);
+void
+rsvg_render_path (RsvgDrawingCtx * ctx, const cairo_path_t *path)
+{
+    ctx->render->render_path (ctx, path);
+    rsvg_render_markers (ctx, path);
 }
 
 void
-rsvg_render_image (RsvgDrawingCtx * ctx, GdkPixbuf * pb, double x, double y, double w, double h)
+rsvg_render_surface (RsvgDrawingCtx * ctx, cairo_surface_t *surface, double x, double y, double w, double h)
 {
-    ctx->render->render_image (ctx, pb, x, y, w, h);
+    /* surface must be a cairo image surface */
+    g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+    ctx->render->render_surface (ctx, surface, x, y, w, h);
 }
 
 void
@@ -1851,10 +2234,10 @@ rsvg_add_clipping_rect (RsvgDrawingCtx * ctx, double x, double y, double w, doub
     ctx->render->add_clipping_rect (ctx, x, y, w, h);
 }
 
-GdkPixbuf *
-rsvg_get_image_of_node (RsvgDrawingCtx * ctx, RsvgNode * drawable, double w, double h)
+cairo_surface_t *
+rsvg_get_surface_of_node (RsvgDrawingCtx * ctx, RsvgNode * drawable, double w, double h)
 {
-    return ctx->render->get_image_of_node (ctx, drawable, w, h);
+    return ctx->render->get_surface_of_node (ctx, drawable, w, h);
 }
 
 void
@@ -1864,18 +2247,16 @@ rsvg_render_free (RsvgRender * render)
 }
 
 void
-rsvg_bbox_init (RsvgBbox * self, double *affine)
+rsvg_bbox_init (RsvgBbox * self, cairo_matrix_t *affine)
 {
-    int i;
     self->virgin = 1;
-    for (i = 0; i < 6; i++)
-        self->affine[i] = affine[i];
+    self->affine = *affine;
 }
 
 void
 rsvg_bbox_insert (RsvgBbox * dst, RsvgBbox * src)
 {
-    double affine[6];
+    cairo_matrix_t affine;
     double xmin, ymin;
     double xmax, ymax;
     int i;
@@ -1883,25 +2264,25 @@ rsvg_bbox_insert (RsvgBbox * dst, RsvgBbox * src)
     if (src->virgin)
         return;
 
-    if (!dst->virgin)
-        {
-            xmin = dst->x, ymin = dst->y;
-            xmax = dst->x + dst->w, ymax = dst->y + dst->h;
-        }
-    else
-        {
-            xmin = ymin = xmax = ymax = 0;
-        }
+    if (!dst->virgin) {
+        xmin = dst->rect.x, ymin = dst->rect.y;
+        xmax = dst->rect.x + dst->rect.width, ymax = dst->rect.y + dst->rect.height;
+    } else {
+        xmin = ymin = xmax = ymax = 0;
+    }
 
-    _rsvg_affine_invert (affine, dst->affine);
-    _rsvg_affine_multiply (affine, src->affine, affine);
+    affine = dst->affine;
+    if (cairo_matrix_invert (&affine) != CAIRO_STATUS_SUCCESS)
+      return; //FIXMEchpe correct??
+
+    cairo_matrix_multiply (&affine, &src->affine, &affine);
 
     for (i = 0; i < 4; i++) {
         double rx, ry, x, y;
-        rx = src->x + src->w * (double) (i % 2);
-        ry = src->y + src->h * (double) (i / 2);
-        x = affine[0] * rx + affine[2] * ry + affine[4];
-        y = affine[1] * rx + affine[3] * ry + affine[5];
+        rx = src->rect.x + src->rect.width * (double) (i % 2);
+        ry = src->rect.y + src->rect.height * (double) (i / 2);
+        x = affine.xx * rx + affine.xy * ry + affine.x0;
+        y = affine.yx * rx + affine.yy * ry + affine.y0;
         if (dst->virgin) {
             xmin = xmax = x;
             ymin = ymax = y;
@@ -1917,16 +2298,16 @@ rsvg_bbox_insert (RsvgBbox * dst, RsvgBbox * src)
                 ymax = y;
         }
     }
-    dst->x = xmin;
-    dst->y = ymin;
-    dst->w = xmax - xmin;
-    dst->h = ymax - ymin;
+    dst->rect.x = xmin;
+    dst->rect.y = ymin;
+    dst->rect.width = xmax - xmin;
+    dst->rect.height = ymax - ymin;
 }
 
 void
 rsvg_bbox_clip (RsvgBbox * dst, RsvgBbox * src)
 {
-    double affine[6];
+    cairo_matrix_t affine;
 	double xmin, ymin;
 	double xmax, ymax;
     int i;
@@ -1934,25 +2315,25 @@ rsvg_bbox_clip (RsvgBbox * dst, RsvgBbox * src)
     if (src->virgin)
         return;
 
-	if (!dst->virgin)
-		{
-			xmin = dst->x + dst->w, ymin = dst->y + dst->h;
-			xmax = dst->x, ymax = dst->y;
-		}
-	else
-		{
-			xmin = ymin = xmax = ymax = 0;
-		}
+    if (!dst->virgin) {
+        xmin = dst->rect.x + dst->rect.width, ymin = dst->rect.y + dst->rect.height;
+        xmax = dst->rect.x, ymax = dst->rect.y;
+    } else {
+        xmin = ymin = xmax = ymax = 0;
+    }
+
+    affine = dst->affine;
+    if (cairo_matrix_invert (&affine) != CAIRO_STATUS_SUCCESS)
+      return;
 
-    _rsvg_affine_invert (affine, dst->affine);
-    _rsvg_affine_multiply (affine, src->affine, affine);
+    cairo_matrix_multiply (&affine, &src->affine, &affine);
 
     for (i = 0; i < 4; i++) {
         double rx, ry, x, y;
-        rx = src->x + src->w * (double) (i % 2);
-        ry = src->y + src->h * (double) (i / 2);
-        x = affine[0] * rx + affine[2] * ry + affine[4];
-        y = affine[1] * rx + affine[3] * ry + affine[5];
+        rx = src->rect.x + src->rect.width * (double) (i % 2);
+        ry = src->rect.y + src->rect.height * (double) (i / 2);
+        x = affine.xx * rx + affine.xy * ry + affine.x0;
+        y = affine.yx * rx + affine.yy * ry + affine.y0;
         if (dst->virgin) {
             xmin = xmax = x;
             ymin = ymax = y;
@@ -1969,19 +2350,19 @@ rsvg_bbox_clip (RsvgBbox * dst, RsvgBbox * src)
         }
     }
 
-    if (xmin < dst->x)
-        xmin = dst->x;
-    if (ymin < dst->y)
-        ymin = dst->y;
-    if (xmax > dst->x + dst->w)
-        xmax = dst->x + dst->w;
-    if (ymax > dst->y + dst->h)
-        ymax = dst->y + dst->h;
+    if (xmin < dst->rect.x)
+        xmin = dst->rect.x;
+    if (ymin < dst->rect.y)
+        ymin = dst->rect.y;
+    if (xmax > dst->rect.x + dst->rect.width)
+        xmax = dst->rect.x + dst->rect.width;
+    if (ymax > dst->rect.y + dst->rect.height)
+        ymax = dst->rect.y + dst->rect.height;
 
-    dst->x = xmin;
-    dst->w = xmax - xmin;
-    dst->y = ymin;
-    dst->h = ymax - ymin;
+    dst->rect.x = xmin;
+    dst->rect.width = xmax - xmin;
+    dst->rect.y = ymin;
+    dst->rect.height = ymax - ymin;
 }
 
 void
@@ -1990,8 +2371,8 @@ _rsvg_push_view_box (RsvgDrawingCtx * ctx, double w, double h)
     RsvgViewBox *vb = g_new (RsvgViewBox, 1);
     *vb = ctx->vb;
     ctx->vb_stack = g_slist_prepend (ctx->vb_stack, vb);
-    ctx->vb.w = w;
-    ctx->vb.h = h;
+    ctx->vb.rect.width = w;
+    ctx->vb.rect.height = h;
 }
 
 void
@@ -2007,3 +2388,167 @@ 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)
+{
+    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);
+
+    scheme = g_uri_parse_scheme (uri);
+
+    /* Not a valid URI */
+    if (scheme == NULL)
+        goto deny;
+
+    /* Allow loads of data: from any location */
+    if (g_str_equal (scheme, "data"))
+        goto allow;
+
+    /* No base to compare to? */
+    if (priv->base_gfile == NULL)
+        goto deny;
+
+    /* Deny loads from differing URI schemes */
+    if (!g_file_has_uri_scheme (priv->base_gfile, scheme))
+        goto deny;
+
+    /* resource: is allowed to load anything from other resources */
+    if (g_str_equal (scheme, "resource"))
+        goto allow;
+
+    /* Non-file: isn't allowed to load anything */
+    if (!g_str_equal (scheme, "file"))
+        goto deny;
+
+    base = g_file_get_parent (priv->base_gfile);
+    if (base == NULL)
+        goto deny;
+
+    dir = g_file_get_path (base);
+    g_object_unref (base);
+
+    cdir = realpath (dir, NULL);
+    g_free (dir);
+    if (cdir == NULL)
+        goto deny;
+
+    path = g_filename_from_uri (uri, NULL, NULL);
+    if (path == NULL)
+        goto deny;
+
+    cpath = realpath (path, NULL);
+    g_free (path);
+
+    if (cpath == NULL)
+        goto deny;
+
+    /* Now check that @cpath is below @cdir */
+    if (!g_str_has_prefix (cpath, cdir) ||
+        cpath[strlen (cdir)] != G_DIR_SEPARATOR)
+        goto deny;
+
+    /* Allow load! */
+
+ allow:
+    g_free (scheme);
+    free (cpath);
+    free (cdir);
+    return TRUE;
+
+ deny:
+    g_free (scheme);
+    free (cpath);
+    free (cdir);
+
+    g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                 "File may not link to URI \"%s\"", uri);
+    return FALSE;
+}
+
+static char *
+_rsvg_handle_resolve_uri (RsvgHandle *handle,
+                          const char *uri)
+{
+    RsvgHandlePrivate *priv = handle->priv;
+    char *scheme, *resolved_uri;
+    GFile *base, *resolved;
+
+    if (uri == NULL)
+        return NULL;
+
+    scheme = g_uri_parse_scheme (uri);
+    if (scheme != NULL ||
+        priv->base_gfile == NULL ||
+        (base = g_file_get_parent (priv->base_gfile)) == NULL) {
+        g_free (scheme);
+        return g_strdup (uri);
+    }
+
+    resolved = g_file_resolve_relative_path (base, uri);
+    resolved_uri = g_file_get_uri (resolved);
+
+    g_free (scheme);
+    g_object_unref (base);
+    g_object_unref (resolved);
+
+    return resolved_uri;
+}
+
+char * 
+_rsvg_handle_acquire_data (RsvgHandle *handle,
+                           const char *url,
+                           char **content_type,
+                           gsize *len,
+                           GError **error)
+{
+    char *uri;
+    char *data;
+
+    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, 
+                                      handle->priv->cancellable,
+                                      error);
+    } else {
+        data = NULL;
+    }
+
+    g_free (uri);
+    return data;
+}
+
+GInputStream *
+_rsvg_handle_acquire_stream (RsvgHandle *handle,
+                             const char *url,
+                             char **content_type,
+                             GError **error)
+{
+    char *uri;
+    GInputStream *stream;
+
+    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, 
+                                          handle->priv->cancellable,
+                                          error);
+    } else {
+        stream = NULL;
+    }
+
+    g_free (uri);
+    return stream;
+}
diff --git a/rsvg-bpath-util.c b/rsvg-bpath-util.c
deleted file mode 100644
index 2f35e1f..0000000
--- a/rsvg-bpath-util.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* 
-   rsvg-bpath-util.c: Data structure and convenience functions for creating bezier paths.
- 
-   Copyright (C) 2000 Eazel, Inc.
-  
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-  
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-  
-   You should have received a copy of the GNU Library General Public
-   License along with this program; if not, write to the
-   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.
-  
-   Author: Raph Levien <raph@artofcode.com>
-*/
-
-#include "config.h"
-#include "rsvg-bpath-util.h"
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-
-#include <glib.h>
-
-/* This is adapted from gnome-canvas-bpath-util in libgnomeprint
-   (originally developed as part of Gill). */
-
-RsvgBpathDef *
-rsvg_bpath_def_new (void)
-{
-    RsvgBpathDef *bpd;
-
-    bpd = g_new (RsvgBpathDef, 1);
-    bpd->n_bpath = 0;
-    bpd->n_bpath_max = 16;
-    bpd->moveto_idx = -1;
-    bpd->bpath = g_new (RsvgBpath, bpd->n_bpath_max);
-
-    return bpd;
-}
-
-RsvgBpathDef *
-rsvg_bpath_def_new_from (RsvgBpath * path)
-{
-    RsvgBpathDef *bpd;
-    int i;
-
-    g_return_val_if_fail (path != NULL, NULL);
-
-    for (i = 0; path[i].code != RSVG_END; i++);
-    if (i <= 0)
-        return rsvg_bpath_def_new ();
-
-    bpd = g_new (RsvgBpathDef, 1);
-
-    bpd->n_bpath = i;
-    bpd->n_bpath_max = i;
-    bpd->moveto_idx = -1;
-    bpd->bpath = g_new (RsvgBpath, i);
-
-    memcpy (bpd->bpath, path, i * sizeof (RsvgBpath));
-    return bpd;
-}
-
-void
-rsvg_bpath_def_free (RsvgBpathDef * bpd)
-{
-    g_return_if_fail (bpd != NULL);
-
-    g_free (bpd->bpath);
-    g_free (bpd);
-}
-
-void
-rsvg_bpath_def_moveto (RsvgBpathDef * bpd, double x, double y)
-{
-    RsvgBpath *bpath;
-    int n_bpath;
-
-    g_return_if_fail (bpd != NULL);
-
-    /* if the last command was a moveto then change that last moveto instead of
-       creating a new one */
-    bpath = bpd->bpath;
-    n_bpath = bpd->n_bpath;
-
-    if (n_bpath > 0)
-        if (bpath[n_bpath - 1].code == RSVG_MOVETO_OPEN) {
-            bpath[n_bpath - 1].x3 = x;
-            bpath[n_bpath - 1].y3 = y;
-            bpd->moveto_idx = n_bpath - 1;
-            return;
-        }
-
-    n_bpath = bpd->n_bpath++;
-
-    if (n_bpath == bpd->n_bpath_max)
-        bpd->bpath = g_realloc (bpd->bpath, (bpd->n_bpath_max <<= 1) * sizeof (RsvgBpath));
-    bpath = bpd->bpath;
-    bpath[n_bpath].code = RSVG_MOVETO_OPEN;
-    bpath[n_bpath].x3 = x;
-    bpath[n_bpath].y3 = y;
-    bpd->moveto_idx = n_bpath;
-}
-
-void
-rsvg_bpath_def_lineto (RsvgBpathDef * bpd, double x, double y)
-{
-    RsvgBpath *bpath;
-    int n_bpath;
-
-    g_return_if_fail (bpd != NULL);
-    g_return_if_fail (bpd->moveto_idx >= 0);
-
-    n_bpath = bpd->n_bpath++;
-
-    if (n_bpath == bpd->n_bpath_max)
-        bpd->bpath = g_realloc (bpd->bpath, (bpd->n_bpath_max <<= 1) * sizeof (RsvgBpath));
-    bpath = bpd->bpath;
-    bpath[n_bpath].code = RSVG_LINETO;
-    bpath[n_bpath].x3 = x;
-    bpath[n_bpath].y3 = y;
-}
-
-void
-rsvg_bpath_def_curveto (RsvgBpathDef * bpd, double x1, double y1, double x2, double y2, double x3,
-                        double y3)
-{
-    RsvgBpath *bpath;
-    int n_bpath;
-
-    g_return_if_fail (bpd != NULL);
-    g_return_if_fail (bpd->moveto_idx >= 0);
-
-    n_bpath = bpd->n_bpath++;
-
-    if (n_bpath == bpd->n_bpath_max)
-        bpd->bpath = g_realloc (bpd->bpath, (bpd->n_bpath_max <<= 1) * sizeof (RsvgBpath));
-    bpath = bpd->bpath;
-    bpath[n_bpath].code = RSVG_CURVETO;
-    bpath[n_bpath].x1 = x1;
-    bpath[n_bpath].y1 = y1;
-    bpath[n_bpath].x2 = x2;
-    bpath[n_bpath].y2 = y2;
-    bpath[n_bpath].x3 = x3;
-    bpath[n_bpath].y3 = y3;
-}
-
-static void
-rsvg_bpath_def_replicate (RsvgBpathDef * bpd, int index)
-{
-    RsvgBpath *bpath;
-    int n_bpath;
-
-    n_bpath = bpd->n_bpath++;
-
-    if (n_bpath == bpd->n_bpath_max)
-        bpd->bpath = g_realloc (bpd->bpath, (bpd->n_bpath_max <<= 1) * sizeof (RsvgBpath));
-    bpath = bpd->bpath;
-    bpath[n_bpath] = bpath[index];
-}
-
-void
-rsvg_bpath_def_closepath (RsvgBpathDef * bpd)
-{
-    RsvgBpath *bpath;
-
-    g_return_if_fail (bpd != NULL);
-    g_return_if_fail (bpd->moveto_idx >= 0);
-    g_return_if_fail (bpd->n_bpath > 0);
-
-    rsvg_bpath_def_replicate (bpd, bpd->moveto_idx);
-    bpath = bpd->bpath;
-
-    bpath[bpd->n_bpath - 1].code = RSVG_MOVETO;
-    bpd->moveto_idx = bpd->n_bpath - 1;
-}
-
-void
-rsvg_bpath_def_art_finish (RsvgBpathDef * bpd)
-{
-    int n_bpath;
-
-    g_return_if_fail (bpd != NULL);
-
-    n_bpath = bpd->n_bpath++;
-
-    if (n_bpath == bpd->n_bpath_max)
-        bpd->bpath = g_realloc (bpd->bpath, (bpd->n_bpath_max <<= 1) * sizeof (RsvgBpath));
-    bpd->bpath[n_bpath].code = RSVG_END;
-}
diff --git a/rsvg-bpath-util.h b/rsvg-bpath-util.h
deleted file mode 100644
index e531f12..0000000
--- a/rsvg-bpath-util.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/*
-   rsvg-bpath-util.h: Path utility functions
-
-   Copyright (C) 2000 Eazel, Inc.
-   Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
-
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-
-   You should have received a copy of the GNU Library General Public
-   License along with this program; if not, write to the
-   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.
-
-   Author: Raph Levien <raph@artofcode.com>
-*/
-
-#ifndef RSVG_BPATH_UTIL_H
-#define RSVG_BPATH_UTIL_H
-
-#include <glib.h>
-
-G_BEGIN_DECLS 
-typedef enum {
-    RSVG_MOVETO,
-    RSVG_MOVETO_OPEN,
-    RSVG_CURVETO,
-    RSVG_LINETO,
-    RSVG_END
-} RsvgPathcode;
-
-typedef struct _RsvgBpath RsvgBpath;
-struct _RsvgBpath {
-    /*< public > */
-    RsvgPathcode code;
-    double x1;
-    double y1;
-    double x2;
-    double y2;
-    double x3;
-    double y3;
-};
-
-typedef struct _RsvgBpathDef RsvgBpathDef;
-
-struct _RsvgBpathDef {
-    RsvgBpath *bpath;
-    int n_bpath;
-    int n_bpath_max;
-    int moveto_idx;
-};
-
-RsvgBpathDef *rsvg_bpath_def_new	(void);
-RsvgBpathDef *rsvg_bpath_def_new_from	(RsvgBpath * bpath);
-
-void rsvg_bpath_def_free	(RsvgBpathDef * bpd);
-
-void rsvg_bpath_def_moveto	(RsvgBpathDef * bpd, double x, double y);
-void rsvg_bpath_def_lineto	(RsvgBpathDef * bpd, double x, double y);
-void rsvg_bpath_def_curveto	(RsvgBpathDef * bpd,
-				 double x1, double y1, double x2, double y2, double x3, double y3);
-void rsvg_bpath_def_closepath	(RsvgBpathDef * bpd);
-
-void rsvg_bpath_def_art_finish	(RsvgBpathDef * bpd);
-
-G_END_DECLS
-
-#endif
diff --git a/rsvg-cairo-clip.c b/rsvg-cairo-clip.c
index d38cf87..f2af3e5 100644
--- a/rsvg-cairo-clip.c
+++ b/rsvg-cairo-clip.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-shapes.c: Draw shapes with cairo
 
@@ -30,7 +31,6 @@
 #include "rsvg-cairo-clip.h"
 #include "rsvg-cairo-render.h"
 #include "rsvg-styles.h"
-#include "rsvg-bpath-util.h"
 #include "rsvg-path.h"
 
 #include <math.h>
@@ -39,69 +39,51 @@
 typedef struct RsvgCairoClipRender RsvgCairoClipRender;
 
 struct RsvgCairoClipRender {
-    RsvgRender super;
-    cairo_t *cr;
+    RsvgCairoRender super;
     RsvgCairoRender *parent;
 };
 
+#define RSVG_CAIRO_CLIP_RENDER(render) (_RSVG_RENDER_CIC ((render), RSVG_RENDER_TYPE_CAIRO_CLIP, RsvgCairoClipRender))
+
 static void
-rsvg_cairo_clip_apply_affine (RsvgCairoClipRender *render, const double affine[6])
+rsvg_cairo_clip_apply_affine (RsvgCairoClipRender *render, cairo_matrix_t *affine)
 {
-	cairo_matrix_t matrix;
-	gboolean nest = render->cr != render->parent->initial_cr;
-
-	cairo_matrix_init (&matrix,
-					   affine[0], affine[1],
-					   affine[2], affine[3],
-					   affine[4] + (nest ? 0 : render->parent->offset_x),
-					   affine[5] + (nest ? 0 : render->parent->offset_y));
-	cairo_set_matrix (render->cr, &matrix);
+    RsvgCairoRender *cairo_render = &render->super;
+    cairo_matrix_t matrix;
+    gboolean nest = cairo_render->cr != cairo_render->initial_cr;
+
+    cairo_matrix_init (&matrix,
+                       affine->xx, affine->yx,
+                       affine->xy, affine->yy,
+                       affine->x0 + (nest ? 0 : render->parent->offset_x),
+                       affine->y0 + (nest ? 0 : render->parent->offset_y));
+    cairo_set_matrix (cairo_render->cr, &matrix);
 }
 
 static void
-rsvg_cairo_clip_render_path (RsvgDrawingCtx * ctx, const RsvgBpathDef * bpath_def)
+rsvg_cairo_clip_render_path (RsvgDrawingCtx * ctx, const cairo_path_t *path)
 {
-    RsvgCairoClipRender *render = (RsvgCairoClipRender *) ctx->render;
-	RsvgState *state = rsvg_state_current (ctx);
+    RsvgCairoClipRender *render = RSVG_CAIRO_CLIP_RENDER (ctx->render);
+    RsvgCairoRender *cairo_render = &render->super;
+    RsvgState *state = rsvg_current_state (ctx);
     cairo_t *cr;
-    RsvgBpath *bpath;
-    int i;
-
-    cr = render->cr;
-
-	rsvg_cairo_clip_apply_affine (render, state->affine);
-
-    if (rsvg_state_current (ctx)->clip_rule == FILL_RULE_EVENODD)
-        cairo_set_fill_rule (((RsvgCairoRender *) ctx->render)->cr, CAIRO_FILL_RULE_EVEN_ODD);
-    else                        /* state->fill_rule == FILL_RULE_NONZERO */
-        cairo_set_fill_rule (((RsvgCairoRender *) ctx->render)->cr, CAIRO_FILL_RULE_WINDING);
-
-    for (i = 0; i < bpath_def->n_bpath; i++) {
-        bpath = &bpath_def->bpath[i];
-
-        switch (bpath->code) {
-        case RSVG_MOVETO:
-            cairo_close_path (cr);
-            /* fall-through */
-        case RSVG_MOVETO_OPEN:
-            cairo_move_to (cr, bpath->x3, bpath->y3);
-            break;
-        case RSVG_CURVETO:
-            cairo_curve_to (cr, bpath->x1, bpath->y1, bpath->x2, bpath->y2, bpath->x3, bpath->y3);
-            break;
-        case RSVG_LINETO:
-            cairo_line_to (cr, bpath->x3, bpath->y3);
-            break;
-        case RSVG_END:
-            break;
-        }
-    }
+
+    cr = cairo_render->cr;
+
+    rsvg_cairo_clip_apply_affine (render, &state->affine);
+
+    cairo_set_fill_rule (cr, rsvg_current_state (ctx)->clip_rule);
+
+    cairo_append_path (cr, path);
 }
 
 static void
-rsvg_cairo_clip_render_image (RsvgDrawingCtx * ctx,
-                              const GdkPixbuf * pixbuf,
-                              double pixbuf_x, double pixbuf_y, double w, double h)
+rsvg_cairo_clip_render_surface (RsvgDrawingCtx *ctx,
+                                cairo_surface_t *surface,
+                                double src_x,
+                                double src_y, 
+                                double w, 
+                                double h)
 {
 }
 
@@ -109,8 +91,9 @@ rsvg_cairo_clip_render_image (RsvgDrawingCtx * ctx,
 static void
 rsvg_cairo_clip_render_free (RsvgRender * self)
 {
-    RsvgCairoClipRender *me = (RsvgCairoClipRender *) self;
-    g_free (me);
+    RsvgCairoClipRender *clip_render = RSVG_CAIRO_CLIP_RENDER (self);
+
+    g_free (clip_render);
 }
 
 static void
@@ -131,41 +114,49 @@ 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)
 {
-    RsvgCairoClipRender *cairo_render = g_new0 (RsvgCairoClipRender, 1);
-
-    cairo_render->super.free = rsvg_cairo_clip_render_free;
-    cairo_render->super.render_image = rsvg_cairo_clip_render_image;
-    cairo_render->super.render_path = rsvg_cairo_clip_render_path;
-    cairo_render->super.pop_discrete_layer = rsvg_cairo_clip_pop_discrete_layer;
-    cairo_render->super.push_discrete_layer = rsvg_cairo_clip_push_discrete_layer;
-    cairo_render->super.add_clipping_rect = rsvg_cairo_clip_add_clipping_rect;
-    cairo_render->super.get_image_of_node = NULL;
+    RsvgCairoClipRender *clip_render = g_new0 (RsvgCairoClipRender, 1);
+    RsvgCairoRender *cairo_render = &clip_render->super;
+    RsvgRender *render = &cairo_render->super;
+
+    g_assert (parent->super.type == RSVG_RENDER_TYPE_CAIRO);
+
+    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_path = rsvg_cairo_clip_render_path;
+    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->parent = parent;
+    clip_render->parent = parent;
 
-    return &cairo_render->super;
+    return render;
 }
 
 void
 rsvg_cairo_clip (RsvgDrawingCtx * ctx, RsvgClipPath * clip, RsvgBbox * bbox)
 {
-    RsvgCairoRender *save = (RsvgCairoRender *) ctx->render;
-    double affinesave[6];
-    int i;
+    RsvgCairoRender *save = RSVG_CAIRO_RENDER (ctx->render);
+    cairo_matrix_t affinesave;
+
     ctx->render = rsvg_cairo_clip_render_new (save->cr, save);
 
     /* Horribly dirty hack to have the bbox premultiplied to everything */
     if (clip->units == objectBoundingBox) {
-        double bbtransform[6];
-        bbtransform[0] = bbox->w;
-        bbtransform[1] = 0.;
-        bbtransform[2] = 0.;
-        bbtransform[3] = bbox->h;
-        bbtransform[4] = bbox->x;
-        bbtransform[5] = bbox->y;
-        for (i = 0; i < 6; i++)
-            affinesave[i] = clip->super.state->affine[i];
-        _rsvg_affine_multiply (clip->super.state->affine, bbtransform, clip->super.state->affine);
+        cairo_matrix_t bbtransform;
+        cairo_matrix_init (&bbtransform,
+                           bbox->rect.width,
+                           0,
+                           0,
+                           bbox->rect.height,
+                           bbox->rect.x,
+                           bbox->rect.y);
+        affinesave = clip->super.state->affine;
+        cairo_matrix_multiply (&clip->super.state->affine, &bbtransform, &clip->super.state->affine);
     }
 
     rsvg_state_push (ctx);
@@ -173,8 +164,7 @@ rsvg_cairo_clip (RsvgDrawingCtx * ctx, RsvgClipPath * clip, RsvgBbox * bbox)
     rsvg_state_pop (ctx);
 
     if (clip->units == objectBoundingBox)
-        for (i = 0; i < 6; i++)
-            clip->super.state->affine[i] = affinesave[i];
+        clip->super.state->affine = affinesave;
 
     g_free (ctx->render);
     cairo_clip (save->cr);
diff --git a/rsvg-cairo-clip.h b/rsvg-cairo-clip.h
index 61407de..f4cff9b 100644
--- a/rsvg-cairo-clip.h
+++ b/rsvg-cairo-clip.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-cairo-render.h: The cairo backend plugin
 
@@ -33,6 +34,7 @@
 
 G_BEGIN_DECLS 
 
+G_GNUC_INTERNAL
 void rsvg_cairo_clip (RsvgDrawingCtx * ctx, RsvgClipPath * clip, RsvgBbox * bbox);
 
 G_END_DECLS
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index 6c71a60..f100d9f 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-shapes.c: Draw shapes with cairo
 
@@ -30,7 +31,6 @@
 #include "rsvg-cairo-render.h"
 #include "rsvg-cairo-clip.h"
 #include "rsvg-styles.h"
-#include "rsvg-bpath-util.h"
 #include "rsvg-path.h"
 #include "rsvg-filter.h"
 #include "rsvg-structure.h"
@@ -41,97 +41,7 @@
 
 #include <pango/pangocairo.h>
 
-static void
-_rsvg_cairo_set_shape_antialias (cairo_t * cr, ShapeRenderingProperty aa)
-{
-	if (SHAPE_RENDERING_AUTO == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
-	else if (SHAPE_RENDERING_OPTIMIZE_SPEED == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
-	else if (SHAPE_RENDERING_CRISP_EDGES == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
-	else if (SHAPE_RENDERING_GEOMETRIC_PRECISION == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
-}
-
-static void
-_rsvg_cairo_set_text_antialias (cairo_t * cr, TextRenderingProperty aa)
-{
-	if (TEXT_RENDERING_AUTO == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
-	else if (TEXT_RENDERING_OPTIMIZE_SPEED == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
-	else if (TEXT_RENDERING_OPTIMIZE_LEGIBILITY == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
-	else if (TEXT_RENDERING_GEOMETRIC_PRECISION == aa)
-		cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
-}
-
-static void
-_rsvg_cairo_set_operator (cairo_t * cr, RsvgCompOpType comp_op)
-{
-    cairo_operator_t op;
-
-    switch (comp_op) {
-    case RSVG_COMP_OP_CLEAR:
-        op = CAIRO_OPERATOR_CLEAR;
-        break;
-    case RSVG_COMP_OP_SRC:
-        op = CAIRO_OPERATOR_SOURCE;
-        break;
-    case RSVG_COMP_OP_DST:
-        op = CAIRO_OPERATOR_DEST;
-        break;
-    case RSVG_COMP_OP_DST_OVER:
-        op = CAIRO_OPERATOR_DEST_OVER;
-        break;
-    case RSVG_COMP_OP_SRC_IN:
-        op = CAIRO_OPERATOR_IN;
-        break;
-    case RSVG_COMP_OP_DST_IN:
-        op = CAIRO_OPERATOR_DEST_IN;
-        break;
-    case RSVG_COMP_OP_SRC_OUT:
-        op = CAIRO_OPERATOR_OUT;
-        break;
-    case RSVG_COMP_OP_DST_OUT:
-        op = CAIRO_OPERATOR_DEST_OUT;
-        break;
-    case RSVG_COMP_OP_SRC_ATOP:
-        op = CAIRO_OPERATOR_ATOP;
-        break;
-    case RSVG_COMP_OP_DST_ATOP:
-        op = CAIRO_OPERATOR_DEST_ATOP;
-        break;
-    case RSVG_COMP_OP_XOR:
-        op = CAIRO_OPERATOR_XOR;
-        break;
-    case RSVG_COMP_OP_PLUS:
-	op = CAIRO_OPERATOR_ADD;
-	break;
-    case RSVG_COMP_OP_MULTIPLY:
-    case RSVG_COMP_OP_SCREEN:
-    case RSVG_COMP_OP_OVERLAY:
-    case RSVG_COMP_OP_DARKEN:
-    case RSVG_COMP_OP_LIGHTEN:
-    case RSVG_COMP_OP_COLOR_DODGE:
-    case RSVG_COMP_OP_COLOR_BURN:
-    case RSVG_COMP_OP_HARD_LIGHT:
-    case RSVG_COMP_OP_SOFT_LIGHT:
-    case RSVG_COMP_OP_DIFFERENCE:
-    case RSVG_COMP_OP_EXCLUSION:
-    default:
-        op = CAIRO_OPERATOR_OVER;
-        break;
-    }
-    cairo_set_operator (cr, op);
-}
-
-static void
-rsvg_pixmap_destroy (gchar * pixels, gpointer data)
-{
-    g_free (pixels);
-}
+static const cairo_user_data_key_t surface_pixel_data_key;
 
 static void
 _pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
@@ -144,7 +54,7 @@ _pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
 
     for (i = 0; i < stops->len; i++) {
         node = (RsvgNode *) g_ptr_array_index (stops, i);
-        if (strcmp (node->type->str, "stop"))
+        if (RSVG_NODE_TYPE (node) != RSVG_NODE_TYPE_STOP)
             continue;
         stop = (RsvgGradientStop *) node;
         rgba = stop->rgba;
@@ -161,13 +71,14 @@ _set_source_rsvg_linear_gradient (RsvgDrawingCtx * ctx,
                                   RsvgLinearGradient * linear,
                                   guint32 current_color_rgb, guint8 opacity, RsvgBbox bbox)
 {
-    cairo_t *cr = ((RsvgCairoRender *) ctx->render)->cr;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+    cairo_t *cr = render->cr;
     cairo_pattern_t *pattern;
     cairo_matrix_t matrix;
     RsvgLinearGradient statlinear;
     statlinear = *linear;
     linear = &statlinear;
-    rsvg_linear_gradient_fix_fallback (linear);
+    rsvg_linear_gradient_fix_fallback (ctx, linear);
 
     if (linear->has_current_color)
         current_color_rgb = linear->current_color;
@@ -182,21 +93,16 @@ _set_source_rsvg_linear_gradient (RsvgDrawingCtx * ctx,
     if (linear->obj_bbox)
         _rsvg_pop_view_box (ctx);
 
-    cairo_matrix_init (&matrix,
-                       linear->affine[0], linear->affine[1],
-                       linear->affine[2], linear->affine[3], linear->affine[4], linear->affine[5]);
+    matrix = linear->affine;
     if (linear->obj_bbox) {
         cairo_matrix_t bboxmatrix;
-        cairo_matrix_init (&bboxmatrix, bbox.w, 0, 0, bbox.h, bbox.x, bbox.y);
+        cairo_matrix_init (&bboxmatrix, bbox.rect.width, 0, 0, bbox.rect.height,
+                           bbox.rect.x, bbox.rect.y);
         cairo_matrix_multiply (&matrix, &matrix, &bboxmatrix);
     }
     cairo_matrix_invert (&matrix);
     cairo_pattern_set_matrix (pattern, &matrix);
-
-    if (linear->spread == RSVG_GRADIENT_REFLECT)
-        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
-    else if (linear->spread == RSVG_GRADIENT_REPEAT)
-        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_pattern_set_extend (pattern, linear->spread);
 
     _pattern_add_rsvg_color_stops (pattern, linear->super.children, current_color_rgb, opacity);
 
@@ -209,13 +115,14 @@ _set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
                                   RsvgRadialGradient * radial,
                                   guint32 current_color_rgb, guint8 opacity, RsvgBbox bbox)
 {
-    cairo_t *cr = ((RsvgCairoRender *) ctx->render)->cr;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+    cairo_t *cr = render->cr;
     cairo_pattern_t *pattern;
     cairo_matrix_t matrix;
     RsvgRadialGradient statradial;
     statradial = *radial;
     radial = &statradial;
-    rsvg_radial_gradient_fix_fallback (radial);
+    rsvg_radial_gradient_fix_fallback (ctx, radial);
 
     if (radial->has_current_color)
         current_color_rgb = radial->current_color;
@@ -231,22 +138,17 @@ _set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
     if (radial->obj_bbox)
         _rsvg_pop_view_box (ctx);
 
-    cairo_matrix_init (&matrix,
-                       radial->affine[0], radial->affine[1],
-                       radial->affine[2], radial->affine[3], radial->affine[4], radial->affine[5]);
+    matrix = radial->affine;
     if (radial->obj_bbox) {
         cairo_matrix_t bboxmatrix;
-        cairo_matrix_init (&bboxmatrix, bbox.w, 0, 0, bbox.h, bbox.x, bbox.y);
+        cairo_matrix_init (&bboxmatrix, bbox.rect.width, 0, 0, bbox.rect.height,
+                           bbox.rect.x, bbox.rect.y);
         cairo_matrix_multiply (&matrix, &matrix, &bboxmatrix);
     }
 
     cairo_matrix_invert (&matrix);
     cairo_pattern_set_matrix (pattern, &matrix);
-
-    if (radial->spread == RSVG_GRADIENT_REFLECT)
-        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
-    else if (radial->spread == RSVG_GRADIENT_REPEAT)
-        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+    cairo_pattern_set_extend (pattern, radial->spread);
 
     _pattern_add_rsvg_color_stops (pattern, radial->super.children, current_color_rgb, opacity);
 
@@ -255,46 +157,43 @@ _set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
 }
 
 static void
-_set_source_rsvg_solid_colour (RsvgDrawingCtx * ctx,
-                               RsvgSolidColour * colour, guint8 opacity, guint32 current_colour)
+_set_source_rsvg_solid_color (RsvgDrawingCtx * ctx,
+                              RsvgSolidColor * color, guint8 opacity, guint32 current_color)
 {
-    cairo_t *cr = ((RsvgCairoRender *) ctx->render)->cr;
-    guint32 rgb = colour->rgb;
-    double r, g, b;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+    cairo_t *cr = render->cr;
+    guint32 argb = color->argb;
+    double r, g, b, a;
 
-    if (colour->currentcolour)
-        rgb = current_colour;
+    if (color->currentcolor)
+        argb = current_color;
 
-    r = ((rgb >> 16) & 0xff) / 255.0;
-    g = ((rgb >> 8) & 0xff) / 255.0;
-    b = ((rgb >> 0) & 0xff) / 255.0;
+    r = ((argb >> 16) & 0xff) / 255.0;
+    g = ((argb >>  8) & 0xff) / 255.0;
+    b = ((argb >>  0) & 0xff) / 255.0;
+    a =  (argb >> 24) / 255.0 * (opacity / 255.0);
 
-    if (opacity == 0xff)
-        cairo_set_source_rgb (cr, r, g, b);
-    else
-        cairo_set_source_rgba (cr, r, g, b, opacity / 255.0);
+    cairo_set_source_rgba (cr, r, g, b, a);
 }
 
 static void
 _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
                           RsvgPattern * rsvg_pattern, guint8 opacity, RsvgBbox bbox)
 {
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
     RsvgPattern local_pattern = *rsvg_pattern;
     cairo_t *cr_render, *cr_pattern;
     cairo_pattern_t *pattern;
     cairo_surface_t *surface;
     cairo_matrix_t matrix;
-    int i;
-    double affine[6], caffine[6], bbwscale, bbhscale, scwscale, schscale;
-    double taffine[6], patternw, patternh, patternx, patterny;
+    cairo_matrix_t affine, caffine, taffine;
+    double bbwscale, bbhscale, scwscale, schscale;
+    double patternw, patternh, patternx, patterny;
     int pw, ph;
 
     rsvg_pattern = &local_pattern;
-    rsvg_pattern_fix_fallback (rsvg_pattern);
+    rsvg_pattern_fix_fallback (ctx, rsvg_pattern);
     cr_render = render->cr;
-    _rsvg_affine_identity (affine);
-    _rsvg_affine_identity (caffine);
 
     if (rsvg_pattern->obj_bbox)
         _rsvg_push_view_box (ctx, 1., 1.);
@@ -312,17 +211,17 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
 
 
     if (rsvg_pattern->obj_bbox) {
-        bbwscale = bbox.w;
-        bbhscale = bbox.h;
+        bbwscale = bbox.rect.width;
+        bbhscale = bbox.rect.height;
     } else {
         bbwscale = 1.0;
         bbhscale = 1.0;
     }
 
-    _rsvg_affine_multiply (taffine, rsvg_pattern->affine, rsvg_state_current (ctx)->affine);
+    cairo_matrix_multiply (&taffine, &rsvg_pattern->affine, &rsvg_current_state (ctx)->affine);
 
-    scwscale = sqrt (taffine[0] * taffine[0] + taffine[2] * taffine[2]);
-    schscale = sqrt (taffine[1] * taffine[1] + taffine[3] * taffine[3]);
+    scwscale = sqrt (taffine.xx * taffine.xx + taffine.xy * taffine.xy);
+    schscale = sqrt (taffine.yx * taffine.yx + taffine.yy * taffine.yy);
 
     pw = patternw * bbwscale * scwscale;
     ph = patternh * bbhscale * schscale;
@@ -337,15 +236,15 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
     /* Create the pattern coordinate system */
     if (rsvg_pattern->obj_bbox) {
         /* subtract the pattern origin */
-        affine[4] = bbox.x + patternx * bbox.w;
-        affine[5] = bbox.y + patterny * bbox.h;
+        cairo_matrix_init_translate (&affine,
+                                     bbox.rect.x + patternx * bbox.rect.width,
+                                     bbox.rect.y + patterny * bbox.rect.height);
     } else {
         /* subtract the pattern origin */
-        affine[4] = patternx;
-        affine[5] = patterny;
+        cairo_matrix_init_translate (&affine, patternx, patterny);
     }
     /* Apply the pattern transform */
-    _rsvg_affine_multiply (affine, affine, rsvg_pattern->affine);
+    cairo_matrix_multiply (&affine, &affine, &rsvg_pattern->affine);
 
     /* Create the pattern contents coordinate system */
     if (rsvg_pattern->vbox.active) {
@@ -356,29 +255,35 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
         x = 0;
         y = 0;
         rsvg_preserve_aspect_ratio (rsvg_pattern->preserve_aspect_ratio,
-                                    rsvg_pattern->vbox.w, rsvg_pattern->vbox.h, &w, &h, &x, &y);
-
-        x -= rsvg_pattern->vbox.x * w / rsvg_pattern->vbox.w;
-        y -= rsvg_pattern->vbox.y * h / rsvg_pattern->vbox.h;
-
-        caffine[0] = w / rsvg_pattern->vbox.w;
-        caffine[3] = h / rsvg_pattern->vbox.h;
-        caffine[4] = x;
-        caffine[5] = y;
-        _rsvg_push_view_box (ctx, rsvg_pattern->vbox.w, rsvg_pattern->vbox.h);
+                                    rsvg_pattern->vbox.rect.width, rsvg_pattern->vbox.rect.height,
+                                    &w, &h, &x, &y);
+
+        x -= rsvg_pattern->vbox.rect.x * w / rsvg_pattern->vbox.rect.width;
+        y -= rsvg_pattern->vbox.rect.y * h / rsvg_pattern->vbox.rect.height;
+
+        cairo_matrix_init (&caffine,
+                           w / rsvg_pattern->vbox.rect.width,
+                           0,
+                           0,
+                           h / rsvg_pattern->vbox.rect.height,
+                           x,
+                           y);
+        _rsvg_push_view_box (ctx, rsvg_pattern->vbox.rect.width, rsvg_pattern->vbox.rect.height);
     } else if (rsvg_pattern->obj_cbbox) {
         /* If coords are in terms of the bounding box, use them */
-        caffine[0] = bbox.w;
-        caffine[3] = bbox.h;
+        cairo_matrix_init_scale (&caffine, bbox.rect.width, bbox.rect.height);
         _rsvg_push_view_box (ctx, 1., 1.);
+    } else {
+        cairo_matrix_init_identity (&caffine);
     }
 
     if (scwscale != 1.0 || schscale != 1.0) {
-        double scalematrix[6];
-        _rsvg_affine_scale (scalematrix, scwscale, schscale);
-        _rsvg_affine_multiply (caffine, caffine, scalematrix);
-        _rsvg_affine_scale (scalematrix, 1. / scwscale, 1. / schscale);
-        _rsvg_affine_multiply (affine, scalematrix, affine);
+        cairo_matrix_t scalematrix;
+
+        cairo_matrix_init_scale (&scalematrix, scwscale, schscale);
+        cairo_matrix_multiply (&caffine, &caffine, &scalematrix);
+        cairo_matrix_init_scale (&scalematrix, 1. / scwscale, 1. / schscale);
+        cairo_matrix_multiply (&affine, &scalematrix, &affine);
     }
 
     /* Draw to another surface */
@@ -386,9 +291,8 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
 
     /* Set up transformations to be determined by the contents units */
     rsvg_state_push (ctx);
-    for (i = 0; i < 6; i++)
-        rsvg_state_current (ctx)->personal_affine[i] =
-            rsvg_state_current (ctx)->affine[i] = caffine[i];
+    rsvg_current_state (ctx)->personal_affine =
+            rsvg_current_state (ctx)->affine = caffine;
 
     /* Draw everything */
     _rsvg_node_draw_children ((RsvgNode *) rsvg_pattern, ctx, 2);
@@ -401,10 +305,10 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
     pattern = cairo_pattern_create_for_surface (surface);
     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
 
-    cairo_matrix_init (&matrix, affine[0], affine[1], affine[2], affine[3], affine[4], affine[5]);
+    matrix = affine;
+    if (cairo_matrix_invert (&matrix) != CAIRO_STATUS_SUCCESS)
+      goto out;
 
-
-    cairo_matrix_invert (&matrix);
     cairo_pattern_set_matrix (pattern, &matrix);
     cairo_pattern_set_filter (pattern, CAIRO_FILTER_BEST);
 
@@ -413,6 +317,8 @@ _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
     cairo_pattern_destroy (pattern);
     cairo_destroy (cr_pattern);
     cairo_surface_destroy (surface);
+
+  out:
     if (rsvg_pattern->obj_cbbox || rsvg_pattern->vbox.active)
         _rsvg_pop_view_box (ctx);
 }
@@ -422,36 +328,41 @@ static void
 _set_source_rsvg_paint_server (RsvgDrawingCtx * ctx,
                                guint32 current_color_rgb,
                                RsvgPaintServer * ps,
-                               guint8 opacity, RsvgBbox bbox, guint32 current_colour)
+                               guint8 opacity, RsvgBbox bbox, guint32 current_color)
 {
+    RsvgNode *node;
+
     switch (ps->type) {
-    case RSVG_PAINT_SERVER_LIN_GRAD:
-        _set_source_rsvg_linear_gradient (ctx, ps->core.lingrad, current_color_rgb, opacity, bbox);
-        break;
-    case RSVG_PAINT_SERVER_RAD_GRAD:
-        _set_source_rsvg_radial_gradient (ctx, ps->core.radgrad, current_color_rgb, opacity, bbox);
+    case RSVG_PAINT_SERVER_IRI:
+        node = rsvg_acquire_node (ctx, ps->core.iri);
+        if (node == NULL)
+            break;
+        else if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT)
+            _set_source_rsvg_linear_gradient (ctx, (RsvgLinearGradient *) node, current_color_rgb, opacity, bbox);
+        else if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT)
+            _set_source_rsvg_radial_gradient (ctx, (RsvgRadialGradient *) node, current_color_rgb, opacity, bbox);
+        else if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_PATTERN)
+            _set_source_rsvg_pattern (ctx, (RsvgPattern *) node, opacity, bbox);
+        rsvg_release_node (ctx, node);
         break;
     case RSVG_PAINT_SERVER_SOLID:
-        _set_source_rsvg_solid_colour (ctx, ps->core.colour, opacity, current_colour);
-        break;
-    case RSVG_PAINT_SERVER_PATTERN:
-        _set_source_rsvg_pattern (ctx, ps->core.pattern, opacity, bbox);
+        _set_source_rsvg_solid_color (ctx, ps->core.color, opacity, current_color);
         break;
     }
 }
 
 static void
-_set_rsvg_affine (RsvgCairoRender * render, const double affine[6])
+_set_rsvg_affine (RsvgCairoRender * render, cairo_matrix_t *affine)
 {
     cairo_t * cr = render->cr;
     cairo_matrix_t matrix;
     gboolean nest = cr != render->initial_cr;
 
     cairo_matrix_init (&matrix,
-                       affine[0], affine[1],
-                       affine[2], affine[3],
-                       affine[4] + (nest ? 0 : render->offset_x),
-                       affine[5] + (nest ? 0 : render->offset_y));
+                       affine->xx, affine->yx,
+                       affine->xy, affine->yy,
+                       affine->x0 + (nest ? 0 : render->offset_x),
+                       affine->y0 + (nest ? 0 : render->offset_y));
     cairo_set_matrix (cr, &matrix);
 }
 
@@ -460,10 +371,10 @@ rsvg_cairo_create_pango_context (RsvgDrawingCtx * ctx)
 {
     PangoFontMap *fontmap;
     PangoContext *context;
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
 
     fontmap = pango_cairo_font_map_get_default ();
-    context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
+    context = pango_font_map_create_context (fontmap);
     pango_cairo_update_context (render->cr, context);
     pango_cairo_context_set_resolution (context, ctx->dpi_y);
     return context;
@@ -472,78 +383,91 @@ rsvg_cairo_create_pango_context (RsvgDrawingCtx * ctx)
 void
 rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, double x, double y)
 {
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
-    RsvgState *state = rsvg_state_current (ctx);
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+    RsvgState *state = rsvg_current_state (ctx);
     PangoRectangle ink;
     RsvgBbox bbox;
+    PangoGravity gravity = pango_context_get_gravity (pango_layout_get_context (layout));
+    double rotation;
 
-    _rsvg_cairo_set_text_antialias (render->cr, state->text_rendering_type);
+    cairo_set_antialias (render->cr, state->text_rendering_type);
 
-    _set_rsvg_affine (render, state->affine);
+    _set_rsvg_affine (render, &state->affine);
 
     pango_layout_get_extents (layout, &ink, NULL);
 
-    rsvg_bbox_init (&bbox, state->affine);
-    bbox.x = x + ink.x / (double)PANGO_SCALE;
-    bbox.y = y + ink.y / (double)PANGO_SCALE;
-    bbox.w = ink.width / (double)PANGO_SCALE;
-    bbox.h = ink.height / (double)PANGO_SCALE;
+    rsvg_bbox_init (&bbox, &state->affine);
+    if (PANGO_GRAVITY_IS_VERTICAL (gravity)) {
+        bbox.rect.x = x + (ink.x - ink.height) / (double)PANGO_SCALE;
+        bbox.rect.y = y + ink.y / (double)PANGO_SCALE;
+        bbox.rect.width = ink.height / (double)PANGO_SCALE;
+        bbox.rect.height = ink.width / (double)PANGO_SCALE;
+    } else {
+        bbox.rect.x = x + ink.x / (double)PANGO_SCALE;
+        bbox.rect.y = y + ink.y / (double)PANGO_SCALE;
+        bbox.rect.width = ink.width / (double)PANGO_SCALE;
+        bbox.rect.height = ink.height / (double)PANGO_SCALE;
+    }
     bbox.virgin = 0;
 
+    rotation = pango_gravity_to_rotation (gravity);
     if (state->fill) {
-    cairo_move_to (render->cr, x, y);
-    rsvg_bbox_insert (&render->bbox, &bbox);
+        cairo_save (render->cr);
+        cairo_move_to (render->cr, x, y);
+        rsvg_bbox_insert (&render->bbox, &bbox);
         _set_source_rsvg_paint_server (ctx,
                                        state->current_color,
                                        state->fill,
                                        state->fill_opacity,
-                                       bbox, rsvg_state_current (ctx)->current_color);
-
+                                       bbox, rsvg_current_state (ctx)->current_color);
+        if (rotation != 0.)
+            cairo_rotate (render->cr, -rotation);
         pango_cairo_show_layout (render->cr, layout);
+        cairo_restore (render->cr);
     }
 
     if (state->stroke) {
+        cairo_save (render->cr);
         cairo_move_to (render->cr, x, y);
         rsvg_bbox_insert (&render->bbox, &bbox);
-        pango_cairo_layout_path (render->cr, layout);
 
         _set_source_rsvg_paint_server (ctx,
                                        state->current_color,
                                        state->stroke,
                                        state->stroke_opacity,
-                                       bbox, rsvg_state_current (ctx)->current_color);
+                                       bbox, rsvg_current_state (ctx)->current_color);
 
-	cairo_set_line_width (render->cr, _rsvg_css_normalize_length (&state->stroke_width, ctx, 'h'));
+        if (rotation != 0.)
+            cairo_rotate (render->cr, -rotation);
+        pango_cairo_layout_path (render->cr, layout);
+
+        cairo_set_line_width (render->cr, _rsvg_css_normalize_length (&state->stroke_width, ctx, 'h'));
+        cairo_set_miter_limit (render->cr, state->miter_limit);
+        cairo_set_line_cap (render->cr, (cairo_line_cap_t) state->cap);
+        cairo_set_line_join (render->cr, (cairo_line_join_t) state->join);
+        cairo_set_dash (render->cr, state->dash.dash, state->dash.n_dash,
+                        _rsvg_css_normalize_length (&state->dash.offset, ctx, 'o'));
         cairo_stroke (render->cr);
+        cairo_restore (render->cr);
     }
 }
 
 void
-rsvg_cairo_render_path (RsvgDrawingCtx * ctx, const RsvgBpathDef * bpath_def)
+rsvg_cairo_render_path (RsvgDrawingCtx * ctx, const cairo_path_t *path)
 {
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
-    RsvgState *state = rsvg_state_current (ctx);
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+    RsvgState *state = rsvg_current_state (ctx);
     cairo_t *cr;
-    RsvgBpath *bpath;
-    int i;
-    int need_tmpbuf = 0;
     RsvgBbox bbox;
+    double backup_tolerance;
 
-    if (state->fill == NULL && state->stroke == NULL)
-        return;
-
-    need_tmpbuf = ((state->fill != NULL) && (state->stroke != NULL) && state->opacity != 0xff)
-        || state->clip_path_ref || state->mask || state->filter
-        || (state->comp_op != RSVG_COMP_OP_SRC_OVER);
-
-    if (need_tmpbuf)
-        rsvg_cairo_push_discrete_layer (ctx);
+    rsvg_cairo_push_discrete_layer (ctx);
 
     cr = render->cr;
 
-	_rsvg_cairo_set_shape_antialias (cr, state->shape_rendering_type);
+    cairo_set_antialias (cr, state->shape_rendering_type);
 
-    _set_rsvg_affine (render, state->affine);
+    _set_rsvg_affine (render, &state->affine);
 
     cairo_set_line_width (cr, _rsvg_css_normalize_length (&state->stroke_width, ctx, 'h'));
     cairo_set_miter_limit (cr, state->miter_limit);
@@ -552,66 +476,60 @@ rsvg_cairo_render_path (RsvgDrawingCtx * ctx, const RsvgBpathDef * bpath_def)
     cairo_set_dash (cr, state->dash.dash, state->dash.n_dash,
                     _rsvg_css_normalize_length (&state->dash.offset, ctx, 'o'));
 
-    for (i = 0; i < bpath_def->n_bpath; i++) {
-        bpath = &bpath_def->bpath[i];
-
-        switch (bpath->code) {
-        case RSVG_MOVETO:
-            cairo_close_path (cr);
-            /* fall-through */
-        case RSVG_MOVETO_OPEN:
-            cairo_move_to (cr, bpath->x3, bpath->y3);
-            break;
-        case RSVG_CURVETO:
-            cairo_curve_to (cr, bpath->x1, bpath->y1, bpath->x2, bpath->y2, bpath->x3, bpath->y3);
-            break;
-        case RSVG_LINETO:
-            cairo_line_to (cr, bpath->x3, bpath->y3);
-            break;
-        case RSVG_END:
-            break;
-        }
-    }
-
-    rsvg_bbox_init (&bbox, state->affine);
-
-    if (state->fill != NULL) {
+    cairo_append_path (cr, path);
+
+    rsvg_bbox_init (&bbox, &state->affine);
+
+    backup_tolerance = cairo_get_tolerance (cr);
+    cairo_set_tolerance (cr, 1.0);
+    /* dropping the precision of cairo's bezier subdivision, yielding 2x
+       _rendering_ time speedups, are these rather expensive operations
+       really needed here? */
+
+    /* Bounding box for fill
+     *
+     * Unlike the case for stroke, for fills we always compute the bounding box.
+     * In GNOME we have SVGs for symbolic icons where each icon has a bounding
+     * rectangle with no fill and no stroke, and inside it there are the actual
+     * paths for the icon's shape.  We need to be able to compute the bounding
+     * rectangle's extents, even when it has no fill nor stroke.
+     */
+    {
         RsvgBbox fb;
-        rsvg_bbox_init (&fb, state->affine);
-        cairo_fill_extents (cr, &fb.x, &fb.y, &fb.w, &fb.h);
-        fb.w -= fb.x;
-        fb.h -= fb.y;
+        rsvg_bbox_init (&fb, &state->affine);
+        cairo_fill_extents (cr, &fb.rect.x, &fb.rect.y, &fb.rect.width, &fb.rect.height);
+        fb.rect.width -= fb.rect.x;
+        fb.rect.height -= fb.rect.y;
         fb.virgin = 0;
         rsvg_bbox_insert (&bbox, &fb);
     }
+
+    /* Bounding box for stroke */
     if (state->stroke != NULL) {
         RsvgBbox sb;
-        rsvg_bbox_init (&sb, state->affine);
-        cairo_stroke_extents (cr, &sb.x, &sb.y, &sb.w, &sb.h);
-        sb.w -= sb.x;
-        sb.h -= sb.y;
+        rsvg_bbox_init (&sb, &state->affine);
+        cairo_stroke_extents (cr, &sb.rect.x, &sb.rect.y, &sb.rect.width, &sb.rect.height);
+        sb.rect.width -= sb.rect.x;
+        sb.rect.height -= sb.rect.y;
         sb.virgin = 0;
         rsvg_bbox_insert (&bbox, &sb);
     }
 
+    cairo_set_tolerance (cr, backup_tolerance);
+
     rsvg_bbox_insert (&render->bbox, &bbox);
 
     if (state->fill != NULL) {
         int opacity;
-        if (state->fill_rule == FILL_RULE_EVENODD)
-            cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
-        else                    /* state->fill_rule == FILL_RULE_NONZERO */
-            cairo_set_fill_rule (cr, CAIRO_FILL_RULE_WINDING);
 
-        if (!need_tmpbuf)
-            opacity = (state->fill_opacity * state->opacity) / 255;
-        else
-            opacity = state->fill_opacity;
+        cairo_set_fill_rule (cr, state->fill_rule);
+
+        opacity = state->fill_opacity;
 
         _set_source_rsvg_paint_server (ctx,
                                        state->current_color,
                                        state->fill,
-                                       opacity, bbox, rsvg_state_current (ctx)->current_color);
+                                       opacity, bbox, rsvg_current_state (ctx)->current_color);
 
         if (state->stroke != NULL)
             cairo_fill_preserve (cr);
@@ -621,143 +539,79 @@ rsvg_cairo_render_path (RsvgDrawingCtx * ctx, const RsvgBpathDef * bpath_def)
 
     if (state->stroke != NULL) {
         int opacity;
-        if (!need_tmpbuf)
-            opacity = (state->stroke_opacity * state->opacity) / 255;
-        else
-            opacity = state->stroke_opacity;
+        opacity = state->stroke_opacity;
 
         _set_source_rsvg_paint_server (ctx,
                                        state->current_color,
                                        state->stroke,
-                                       opacity, bbox, rsvg_state_current (ctx)->current_color);
+                                       opacity, bbox, rsvg_current_state (ctx)->current_color);
 
         cairo_stroke (cr);
     }
 
-    if (need_tmpbuf)
-        rsvg_cairo_pop_discrete_layer (ctx);
+    cairo_new_path (cr); /* clear the path in case stroke == fill == NULL; otherwise we leave it around from computing the bounding box */
+
+    rsvg_cairo_pop_discrete_layer (ctx);
 }
 
 void
-rsvg_cairo_render_image (RsvgDrawingCtx * ctx, const GdkPixbuf * pixbuf,
-                         double pixbuf_x, double pixbuf_y, double w, double h)
+rsvg_cairo_render_surface (RsvgDrawingCtx *ctx, 
+                           cairo_surface_t *surface,
+                           double src_x, 
+                           double src_y, 
+                           double w, 
+                           double h)
 {
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
-    RsvgState *state = rsvg_state_current (ctx);
-
-    gint width = gdk_pixbuf_get_width (pixbuf);
-    gint height = gdk_pixbuf_get_height (pixbuf);
-    double dwidth = width, dheight = height;
-    guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
-    int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-    int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
-    guchar *cairo_pixels;
-    cairo_format_t format;
-    cairo_surface_t *surface;
-    static const cairo_user_data_key_t key;
-    int j;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+    RsvgState *state = rsvg_current_state (ctx);
+
+    int width, height;
+    double dwidth, dheight;
     RsvgBbox bbox;
 
-    if (pixbuf == NULL)
+    if (surface == NULL)
         return;
 
-    rsvg_bbox_init (&bbox, state->affine);
-    bbox.x = pixbuf_x;
-    bbox.y = pixbuf_y;
-    bbox.w = w;
-    bbox.h = h;
-    bbox.virgin = 0;
-
-    _set_rsvg_affine (render, state->affine);
-    cairo_scale (render->cr, w / dwidth, h / dheight);
-    pixbuf_x *= dwidth / w;
-    pixbuf_y *= dheight / h;
-
-    if (n_channels == 3)
-        format = CAIRO_FORMAT_RGB24;
-    else
-        format = CAIRO_FORMAT_ARGB32;
-
-    cairo_pixels = g_try_malloc (4 * width * height);
-	if (!cairo_pixels)
-		return;
-
-    surface = cairo_image_surface_create_for_data ((unsigned char *) cairo_pixels,
-                                                   format, width, height, 4 * width);
-    cairo_surface_set_user_data (surface, &key, cairo_pixels, (cairo_destroy_func_t) g_free);
-
-    for (j = height; j; j--) {
-        guchar *p = gdk_pixels;
-        guchar *q = cairo_pixels;
-
-        if (n_channels == 3) {
-            guchar *end = p + 3 * width;
-
-            while (p < end) {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-                q[0] = p[2];
-                q[1] = p[1];
-                q[2] = p[0];
-#else
-                q[1] = p[0];
-                q[2] = p[1];
-                q[3] = p[2];
-#endif
-                p += 3;
-                q += 4;
-            }
-        } else {
-            guchar *end = p + 4 * width;
-            guint t1, t2, t3;
-
-#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
+    g_return_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
 
-            while (p < end) {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-                MULT (q[0], p[2], p[3], t1);
-                MULT (q[1], p[1], p[3], t2);
-                MULT (q[2], p[0], p[3], t3);
-                q[3] = p[3];
-#else
-                q[0] = p[3];
-                MULT (q[1], p[0], p[3], t1);
-                MULT (q[2], p[1], p[3], t2);
-                MULT (q[3], p[2], p[3], t3);
-#endif
-
-                p += 4;
-                q += 4;
-            }
+    dwidth = width = cairo_image_surface_get_width (surface);
+    dheight = height = cairo_image_surface_get_height (surface);
+    if (width == 0 || height == 0)
+        return;
 
-#undef MULT
-        }
+    rsvg_bbox_init (&bbox, &state->affine);
+    bbox.rect.x = src_x;
+    bbox.rect.y = src_y;
+    bbox.rect.width = w;
+    bbox.rect.height = h;
+    bbox.virgin = 0;
 
-        gdk_pixels += gdk_rowstride;
-        cairo_pixels += 4 * width;
-    }
+    _set_rsvg_affine (render, &state->affine);
+    cairo_scale (render->cr, w / dwidth, h / dheight);
+    src_x *= dwidth / w;
+    src_y *= dheight / h;
 
-    _rsvg_cairo_set_operator (render->cr, state->comp_op);
+    cairo_set_operator (render->cr, state->comp_op);
 
 #if 1
-    cairo_set_source_surface (render->cr, surface, pixbuf_x, pixbuf_y);
+    cairo_set_source_surface (render->cr, surface, src_x, src_y);
 #else
-	{
-		cairo_pattern_t *pattern;
-		cairo_matrix_t matrix;
+    {
+        cairo_pattern_t *pattern;
+        cairo_matrix_t matrix;
 
-		pattern = cairo_pattern_create_for_surface (surface);
-		cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+        pattern = cairo_pattern_create_for_surface (surface);
+        cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
 
-		cairo_matrix_init_translate (&matrix, -pixbuf_x, -pixbuf_y);
-		cairo_pattern_set_matrix (pattern, &matrix);
+        cairo_matrix_init_translate (&matrix, -src_x, -src_y);
+        cairo_pattern_set_matrix (pattern, &matrix);
 
-		cairo_set_source (render->cr, pattern);
-		cairo_pattern_destroy (pattern);
-	}
+        cairo_set_source (render->cr, pattern);
+        cairo_pattern_destroy (pattern);
+    }
 #endif
 
     cairo_paint (render->cr);
-    cairo_surface_destroy (surface);
 
     rsvg_bbox_insert (&render->bbox, &bbox);
 }
@@ -765,17 +619,26 @@ rsvg_cairo_render_image (RsvgDrawingCtx * ctx, const GdkPixbuf * pixbuf,
 static void
 rsvg_cairo_generate_mask (cairo_t * cr, RsvgMask * self, RsvgDrawingCtx * ctx, RsvgBbox * bbox)
 {
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
     cairo_surface_t *surface;
     cairo_t *mask_cr, *save_cr;
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
-    RsvgState *state = rsvg_state_current (ctx);
+    RsvgState *state = rsvg_current_state (ctx);
     guint8 *pixels;
     guint32 width = render->width, height = render->height;
     guint32 rowstride = width * 4, row, i;
-    double affinesave[6];
+    cairo_matrix_t affinesave;
     double sx, sy, sw, sh;
     gboolean nest = cr != render->initial_cr;
 
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return;
+    }
+
+    pixels = cairo_image_surface_get_data (surface);
+    rowstride = cairo_image_surface_get_stride (surface);
+
     if (self->maskunits == objectBoundingBox)
         _rsvg_push_view_box (ctx, 1, 1);
 
@@ -787,33 +650,31 @@ rsvg_cairo_generate_mask (cairo_t * cr, RsvgMask * self, RsvgDrawingCtx * ctx, R
     if (self->maskunits == objectBoundingBox)
         _rsvg_pop_view_box (ctx);
 
-    pixels = g_new0 (guint8, height * rowstride);
-    surface = cairo_image_surface_create_for_data (pixels,
-                                                   CAIRO_FORMAT_ARGB32, width, height, rowstride);
-
     mask_cr = cairo_create (surface);
     save_cr = render->cr;
     render->cr = mask_cr;
 
     if (self->maskunits == objectBoundingBox)
         rsvg_cairo_add_clipping_rect (ctx,
-                                      sx * bbox->w + bbox->x,
-                                      sy * bbox->h + bbox->y, sw * bbox->w, sh * bbox->h);
+                                      sx * bbox->rect.width + bbox->rect.x,
+                                      sy * bbox->rect.height + bbox->rect.y,
+                                      sw * bbox->rect.width,
+                                      sh * bbox->rect.height);
     else
         rsvg_cairo_add_clipping_rect (ctx, sx, sy, sw, sh);
 
     /* Horribly dirty hack to have the bbox premultiplied to everything */
     if (self->contentunits == objectBoundingBox) {
-        double bbtransform[6];
-        bbtransform[0] = bbox->w;
-        bbtransform[1] = 0.;
-        bbtransform[2] = 0.;
-        bbtransform[3] = bbox->h;
-        bbtransform[4] = bbox->x;
-        bbtransform[5] = bbox->y;
-        for (i = 0; i < 6; i++)
-            affinesave[i] = self->super.state->affine[i];
-        _rsvg_affine_multiply (self->super.state->affine, bbtransform, self->super.state->affine);
+        cairo_matrix_t bbtransform;
+        cairo_matrix_init (&bbtransform,
+                           bbox->rect.width,
+                           0,
+                           0,
+                           bbox->rect.height,
+                           bbox->rect.x,
+                           bbox->rect.y);
+        affinesave = self->super.state->affine;
+        cairo_matrix_multiply (&self->super.state->affine, &bbtransform, &self->super.state->affine);
         _rsvg_push_view_box (ctx, 1, 1);
     }
 
@@ -823,8 +684,7 @@ rsvg_cairo_generate_mask (cairo_t * cr, RsvgMask * self, RsvgDrawingCtx * ctx, R
 
     if (self->contentunits == objectBoundingBox) {
         _rsvg_pop_view_box (ctx);
-        for (i = 0; i < 6; i++)
-            self->super.state->affine[i] = affinesave[i];
+        self->super.state->affine = affinesave;
     }
 
     render->cr = save_cr;
@@ -846,63 +706,66 @@ rsvg_cairo_generate_mask (cairo_t * cr, RsvgMask * self, RsvgDrawingCtx * ctx, R
                         nest ? 0 : render->offset_x,
                         nest ? 0 : render->offset_y);
     cairo_surface_destroy (surface);
-    g_free (pixels);
-}
-
-static void
-rsvg_cairo_push_early_clips (RsvgDrawingCtx * ctx)
-{
-    cairo_save (((RsvgCairoRender *) ctx->render)->cr);
-    if (rsvg_state_current (ctx)->clip_path_ref)
-        if (((RsvgClipPath *) rsvg_state_current (ctx)->clip_path_ref)->units == userSpaceOnUse)
-            rsvg_cairo_clip (ctx, rsvg_state_current (ctx)->clip_path_ref, NULL);
-
 }
 
 static void
 rsvg_cairo_push_render_stack (RsvgDrawingCtx * ctx)
 {
-    /* XXX: Untested, probably needs help wrt filters */
-
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
     cairo_surface_t *surface;
     cairo_t *child_cr;
     RsvgBbox *bbox;
-    RsvgState *state = rsvg_state_current (ctx);
+    RsvgState *state = rsvg_current_state (ctx);
     gboolean lateclip = FALSE;
 
-    if (rsvg_state_current (ctx)->clip_path_ref)
-        if (((RsvgClipPath *) rsvg_state_current (ctx)->clip_path_ref)->units == objectBoundingBox)
-            lateclip = TRUE;
+    if (rsvg_current_state (ctx)->clip_path) {
+        RsvgNode *node;
+        node = rsvg_acquire_node (ctx, rsvg_current_state (ctx)->clip_path);
+        if (node && RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_CLIP_PATH) {
+            RsvgClipPath *clip_path = (RsvgClipPath *) node;
+
+            switch (clip_path->units) {
+            case userSpaceOnUse:
+                rsvg_cairo_clip (ctx, clip_path, NULL);
+                break;
+            case objectBoundingBox:
+                lateclip = TRUE;
+                break;
+
+            default:
+                g_assert_not_reached ();
+                break;
+            }
+
+        }
+        
+        rsvg_release_node (ctx, node);
+    }
 
     if (state->opacity == 0xFF
-        && !state->filter && !state->mask && !lateclip && (state->comp_op == RSVG_COMP_OP_SRC_OVER)
+        && !state->filter && !state->mask && !lateclip && (state->comp_op == CAIRO_OPERATOR_OVER)
         && (state->enable_background == RSVG_ENABLE_BACKGROUND_ACCUMULATE))
         return;
-    if (!state->filter)
+
+    if (!state->filter) {
         surface = cairo_surface_create_similar (cairo_get_target (render->cr),
                                                 CAIRO_CONTENT_COLOR_ALPHA,
                                                 render->width, render->height);
-    else {
-        guchar *pixels;
-        int rowstride = render->width * 4;
-        pixels = g_new0 (guint8, render->width * render->height * 4);
-
-        surface = cairo_image_surface_create_for_data (pixels,
-                                                       CAIRO_FORMAT_ARGB32,
-                                                       render->width, render->height, rowstride);
-        render->pixbuf_stack =
-            g_list_prepend (render->pixbuf_stack,
-                            gdk_pixbuf_new_from_data (pixels,
-                                                      GDK_COLORSPACE_RGB,
-                                                      TRUE,
-                                                      8,
-                                                      render->width,
-                                                      render->height,
-                                                      rowstride,
-                                                      (GdkPixbufDestroyNotify) rsvg_pixmap_destroy,
-                                                      NULL));
+    } else {
+        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 
+                                              render->width, render->height);
+
+        /* The surface reference is owned by the child_cr created below and put on the cr_stack! */
+        render->surfaces_stack = g_list_prepend (render->surfaces_stack, surface);
+    }
+
+#if 0
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return;
     }
+#endif
+
     child_cr = cairo_create (surface);
     cairo_surface_destroy (surface);
 
@@ -912,91 +775,61 @@ rsvg_cairo_push_render_stack (RsvgDrawingCtx * ctx)
     bbox = g_new (RsvgBbox, 1);
     *bbox = render->bbox;
     render->bb_stack = g_list_prepend (render->bb_stack, bbox);
-    rsvg_bbox_init (&render->bbox, state->affine);
+    rsvg_bbox_init (&render->bbox, &state->affine);
 }
 
 void
 rsvg_cairo_push_discrete_layer (RsvgDrawingCtx * ctx)
 {
-    rsvg_cairo_push_early_clips (ctx);
-    rsvg_cairo_push_render_stack (ctx);
-}
-
-static GdkPixbuf *
-rsvg_compile_bg (RsvgDrawingCtx * ctx)
-{
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
-    cairo_t *cr;
-    cairo_surface_t *surface;
-    GList *i;
-    unsigned char *pixels = g_new0 (guint8, render->width * render->height * 4);
-    int rowstride = render->width * 4;
-
-    GdkPixbuf *output = gdk_pixbuf_new_from_data (pixels,
-                                                  GDK_COLORSPACE_RGB, TRUE, 8,
-                                                  render->width, render->height,
-                                                  rowstride,
-                                                  (GdkPixbufDestroyNotify) rsvg_pixmap_destroy,
-                                                  NULL);
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
 
-    surface = cairo_image_surface_create_for_data (pixels,
-                                                   CAIRO_FORMAT_ARGB32,
-                                                   render->width, render->height, rowstride);
-
-    cr = cairo_create (surface);
-    cairo_surface_destroy (surface);
-
-    for (i = g_list_last (render->cr_stack); i != NULL; i = g_list_previous (i)) {
-        cairo_t *draw = i->data;
-        gboolean nest = draw != render->initial_cr;
-        cairo_set_source_surface (cr, cairo_get_target (draw),
-                                  nest ? 0 : -render->offset_x,
-                                  nest ? 0 : -render->offset_y);
-        cairo_paint (cr);
-    }
-
-    cairo_destroy (cr);
-    return output;
+    cairo_save (render->cr);
+    rsvg_cairo_push_render_stack (ctx);
 }
 
 static void
 rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
 {
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
     cairo_t *child_cr = render->cr;
-    gboolean lateclip = FALSE;
-    GdkPixbuf *output = NULL;
+    RsvgClipPath *lateclip = NULL;
     cairo_surface_t *surface = NULL;
-    RsvgState *state = rsvg_state_current (ctx);
-    gboolean nest;
-
-    if (rsvg_state_current (ctx)->clip_path_ref)
-        if (((RsvgClipPath *) rsvg_state_current (ctx)->clip_path_ref)->units == objectBoundingBox)
-            lateclip = TRUE;
+    RsvgState *state = rsvg_current_state (ctx);
+    gboolean nest, needs_destroy = FALSE;
+
+    if (rsvg_current_state (ctx)->clip_path) {
+        RsvgNode *node;
+        node = rsvg_acquire_node (ctx, rsvg_current_state (ctx)->clip_path);
+        if (node && RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_CLIP_PATH
+            && ((RsvgClipPath *) node)->units == objectBoundingBox)
+            lateclip = (RsvgClipPath *) node;
+        else
+            rsvg_release_node (ctx, node);
+    }
 
     if (state->opacity == 0xFF
-        && !state->filter && !state->mask && !lateclip && (state->comp_op == RSVG_COMP_OP_SRC_OVER)
+        && !state->filter && !state->mask && !lateclip && (state->comp_op == CAIRO_OPERATOR_OVER)
         && (state->enable_background == RSVG_ENABLE_BACKGROUND_ACCUMULATE))
         return;
 
-    if (state->filter) {
-        GdkPixbuf *pixbuf = render->pixbuf_stack->data;
-        GdkPixbuf *bg = rsvg_compile_bg (ctx);
+    surface = cairo_get_target (child_cr);
 
-        render->pixbuf_stack = g_list_remove (render->pixbuf_stack, pixbuf);
+    if (state->filter) {
+        RsvgNode *filter;
+        cairo_surface_t *output;
 
+        filter = rsvg_acquire_node (ctx, state->filter);
+        output = render->surfaces_stack->data;
+        render->surfaces_stack = g_list_delete_link (render->surfaces_stack, render->surfaces_stack);
 
-        output = rsvg_filter_render (state->filter, pixbuf, bg, ctx, &render->bbox, "2103");
-        g_object_unref (G_OBJECT (pixbuf));
-        g_object_unref (G_OBJECT (bg));
+        if (filter && RSVG_NODE_TYPE (filter) == RSVG_NODE_TYPE_FILTER) {
+            needs_destroy = TRUE;
+            surface = rsvg_filter_render ((RsvgFilter *) filter, output, ctx, &render->bbox, "2103");
+            /* Don't destroy the output surface, it's owned by child_cr */
+        }
 
-        surface = cairo_image_surface_create_for_data (gdk_pixbuf_get_pixels (output),
-                                                       CAIRO_FORMAT_ARGB32,
-                                                       gdk_pixbuf_get_width (output),
-                                                       gdk_pixbuf_get_height (output),
-                                                       gdk_pixbuf_get_rowstride (output));
-    } else
-        surface = cairo_get_target (child_cr);
+        rsvg_release_node (ctx, filter);
+    }
 
     render->cr = (cairo_t *) render->cr_stack->data;
     render->cr_stack = g_list_delete_link (render->cr_stack, render->cr_stack);
@@ -1007,19 +840,26 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
                               nest ? 0 : render->offset_x,
                               nest ? 0 : render->offset_y);
 
-    if (lateclip)
-        rsvg_cairo_clip (ctx, rsvg_state_current (ctx)->clip_path_ref, &render->bbox);
+    if (lateclip) {
+        rsvg_cairo_clip (ctx, lateclip, &render->bbox);
+        rsvg_release_node (ctx, (RsvgNode *) lateclip);
+    }
 
-    _rsvg_cairo_set_operator (render->cr, state->comp_op);
+    cairo_set_operator (render->cr, state->comp_op);
 
     if (state->mask) {
-        rsvg_cairo_generate_mask (render->cr, state->mask, ctx, &render->bbox);
+        RsvgNode *mask;
+
+        mask = rsvg_acquire_node (ctx, state->mask);
+        if (mask && RSVG_NODE_TYPE (mask) == RSVG_NODE_TYPE_MASK)
+          rsvg_cairo_generate_mask (render->cr, (RsvgMask *) mask, ctx, &render->bbox);
+        rsvg_release_node (ctx, mask);
     } else if (state->opacity != 0xFF)
         cairo_paint_with_alpha (render->cr, (double) state->opacity / 255.0);
     else
         cairo_paint (render->cr);
-    cairo_destroy (child_cr);
 
+    cairo_destroy (child_cr);
 
     rsvg_bbox_insert ((RsvgBbox *) render->bb_stack->data, &render->bbox);
 
@@ -1028,8 +868,7 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
     g_free (render->bb_stack->data);
     render->bb_stack = g_list_delete_link (render->bb_stack, render->bb_stack);
 
-    if (state->filter) {
-        g_object_unref (G_OBJECT (output));
+    if (needs_destroy) {
         cairo_surface_destroy (surface);
     }
 }
@@ -1037,43 +876,43 @@ rsvg_cairo_pop_render_stack (RsvgDrawingCtx * ctx)
 void
 rsvg_cairo_pop_discrete_layer (RsvgDrawingCtx * ctx)
 {
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+
     rsvg_cairo_pop_render_stack (ctx);
-    cairo_restore (((RsvgCairoRender *) ctx->render)->cr);
+    cairo_restore (render->cr);
 }
 
 void
 rsvg_cairo_add_clipping_rect (RsvgDrawingCtx * ctx, double x, double y, double w, double h)
 {
-    RsvgCairoRender *render = (RsvgCairoRender *) ctx->render;
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
     cairo_t *cr = render->cr;
 
-    _set_rsvg_affine (render, rsvg_state_current (ctx)->affine);
+    _set_rsvg_affine (render, &rsvg_current_state (ctx)->affine);
 
     cairo_rectangle (cr, x, y, w, h);
     cairo_clip (cr);
 }
 
-GdkPixbuf *
-rsvg_cairo_get_image_of_node (RsvgDrawingCtx * ctx,
-                              RsvgNode * drawable, double width, double height)
+cairo_surface_t *
+rsvg_cairo_get_surface_of_node (RsvgDrawingCtx *ctx,
+                                RsvgNode *drawable, 
+                                double width, 
+                                double height)
 {
-    /* XXX: untested, should work */
-
-    GdkPixbuf *img = NULL;
     cairo_surface_t *surface;
     cairo_t *cr;
-    guint8 *pixels;
-    int rowstride;
 
     RsvgCairoRender *save_render = (RsvgCairoRender *) ctx->render;
     RsvgCairoRender *render;
 
-    rowstride = width * 4;
-    pixels = g_new0 (guint8, width * height * 4);
-    surface = cairo_image_surface_create_for_data (pixels,
-                                                   CAIRO_FORMAT_ARGB32, width, height, rowstride);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
+
     cr = cairo_create (surface);
-    cairo_surface_destroy (surface);
 
     render = rsvg_cairo_render_new (cr, width, height);
     ctx->render = (RsvgRender *) render;
@@ -1082,24 +921,221 @@ rsvg_cairo_get_image_of_node (RsvgDrawingCtx * ctx,
     rsvg_node_draw (drawable, ctx, 0);
     rsvg_state_pop (ctx);
 
-	/* no colorspace conversion necessary. this is just a convenient
-	   holder of ARGB data with a width, height, & stride */
-    img = gdk_pixbuf_new_from_data (pixels,
-                                    GDK_COLORSPACE_RGB,
-                                    TRUE,
-                                    8,
-                                    width,
-                                    height,
-                                    rowstride, (GdkPixbufDestroyNotify) rsvg_pixmap_destroy, NULL);
-
     cairo_destroy (cr);
 
     rsvg_render_free (ctx->render);
     ctx->render = (RsvgRender *) save_render;
 
-    return img;
+    return surface;
 }
 
+cairo_surface_t *
+rsvg_cairo_surface_from_pixbuf (const GdkPixbuf *pixbuf)
+{
+    gint width, height, gdk_rowstride, n_channels, cairo_rowstride;
+    guchar *gdk_pixels, *cairo_pixels;
+    cairo_format_t format;
+    cairo_surface_t *surface;
+    int j;
+
+    if (pixbuf == NULL)
+        return NULL;
+
+    width = gdk_pixbuf_get_width (pixbuf);
+    height = gdk_pixbuf_get_height (pixbuf);
+    gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
+    gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+    n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+    if (n_channels == 3)
+        format = CAIRO_FORMAT_RGB24;
+    else
+        format = CAIRO_FORMAT_ARGB32;
+
+    surface = cairo_image_surface_create (format, width, height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
+
+    cairo_pixels = cairo_image_surface_get_data (surface);
+    cairo_rowstride = cairo_image_surface_get_stride (surface);
+
+    if (n_channels == 3) {
+        for (j = height; j; j--) {
+            guchar *p = gdk_pixels;
+            guchar *q = cairo_pixels;
+            guchar *end = p + 3 * width;
+
+            while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                q[0] = p[2];
+                q[1] = p[1];
+                q[2] = p[0];
+#else
+                q[1] = p[0];
+                q[2] = p[1];
+                q[3] = p[2];
+#endif
+                p += 3;
+                q += 4;
+            }
+
+            gdk_pixels += gdk_rowstride;
+            cairo_pixels += cairo_rowstride;
+        }
+    } else {
+        for (j = height; j; j--) {
+            guchar *p = gdk_pixels;
+            guchar *q = cairo_pixels;
+            guchar *end = p + 4 * width;
+            guint t1, t2, t3;
+
+#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
+
+            while (p < end) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                MULT (q[0], p[2], p[3], t1);
+                MULT (q[1], p[1], p[3], t2);
+                MULT (q[2], p[0], p[3], t3);
+                q[3] = p[3];
+#else
+                q[0] = p[3];
+                MULT (q[1], p[0], p[3], t1);
+                MULT (q[2], p[1], p[3], t2);
+                MULT (q[3], p[2], p[3], t3);
+#endif
+
+                p += 4;
+                q += 4;
+            }
+
+#undef MULT
+            gdk_pixels += gdk_rowstride;
+            cairo_pixels += cairo_rowstride;
+        }
+    }
+
+    cairo_surface_mark_dirty (surface);
+    return surface;
+}
+
+/* Copied from gtk+/gdk/gdkpixbuf-drawable.c, LGPL 2+.
+ *
+ * Copyright (C) 1999 Michael Zucchi
+ *
+ * Authors: Michael Zucchi <zucchi@zedzone.mmc.com.au>
+ *          Cody Russell <bratsche@dfw.net>
+ *          Federico Mena-Quintero <federico@gimp.org>
+ */
+
+static void
+convert_alpha (guchar *dest_data,
+               int     dest_stride,
+               guchar *src_data,
+               int     src_stride,
+               int     src_x,
+               int     src_y,
+               int     width,
+               int     height)
+{
+    int x, y;
+
+    src_data += src_stride * src_y + src_x * 4;
+
+    for (y = 0; y < height; y++) {
+        guint32 *src = (guint32 *) src_data;
+
+        for (x = 0; x < width; x++) {
+          guint alpha = src[x] >> 24;
+
+          if (alpha == 0) {
+              dest_data[x * 4 + 0] = 0;
+              dest_data[x * 4 + 1] = 0;
+              dest_data[x * 4 + 2] = 0;
+          } else {
+              dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+              dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
+              dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
+          }
+          dest_data[x * 4 + 3] = alpha;
+      }
+
+      src_data += src_stride;
+      dest_data += dest_stride;
+    }
+}
+
+static void
+convert_no_alpha (guchar *dest_data,
+                  int     dest_stride,
+                  guchar *src_data,
+                  int     src_stride,
+                  int     src_x,
+                  int     src_y,
+                  int     width,
+                  int     height)
+{
+    int x, y;
+
+    src_data += src_stride * src_y + src_x * 4;
+
+    for (y = 0; y < height; y++) {
+        guint32 *src = (guint32 *) src_data;
+
+        for (x = 0; x < width; x++) {
+            dest_data[x * 3 + 0] = src[x] >> 16;
+            dest_data[x * 3 + 1] = src[x] >>  8;
+            dest_data[x * 3 + 2] = src[x];
+        }
+
+        src_data += src_stride;
+        dest_data += dest_stride;
+    }
+}
+
+GdkPixbuf *
+rsvg_cairo_surface_to_pixbuf (cairo_surface_t *surface)
+{
+    cairo_content_t content;
+    GdkPixbuf *dest;
+    int width, height;
+
+    /* General sanity checks */
+    g_assert (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE);
+
+    width = cairo_image_surface_get_width (surface);
+    height = cairo_image_surface_get_height (surface);
+    if (width == 0 || height == 0)
+        return NULL;
+
+    content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
+    dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+                          !!(content & CAIRO_CONTENT_ALPHA),
+                          8,
+                          width, height);
+
+    if (gdk_pixbuf_get_has_alpha (dest))
+      convert_alpha (gdk_pixbuf_get_pixels (dest),
+                    gdk_pixbuf_get_rowstride (dest),
+                    cairo_image_surface_get_data (surface),
+                    cairo_image_surface_get_stride (surface),
+                    0, 0,
+                    width, height);
+    else
+      convert_no_alpha (gdk_pixbuf_get_pixels (dest),
+                        gdk_pixbuf_get_rowstride (dest),
+                        cairo_image_surface_get_data (surface),
+                        cairo_image_surface_get_stride (surface),
+                        0, 0,
+                        width, height);
+
+    return dest;
+}
+
+/* This is unused but still exists for ABI compat. See bug #666868. */
+void rsvg_cairo_to_pixbuf (guint8 * pixels, int rowstride, int height);
+
 void
 rsvg_cairo_to_pixbuf (guint8 * pixels, int rowstride, int height)
 {
@@ -1127,29 +1163,3 @@ rsvg_cairo_to_pixbuf (guint8 * pixels, int rowstride, int height)
         }
     }
 }
-
-void
-rsvg_pixbuf_to_cairo (guint8 * pixels, int rowstride, int height)
-{
-    int row;
-    /* un-premultiply data */
-    for (row = 0; row < height; row++) {
-        guint8 *row_data = (pixels + (row * rowstride));
-        int i;
-
-        for (i = 0; i < rowstride; i += 4) {
-            guint32 *b = (guint32 *) & row_data[i];
-            guint8 pixel[4];
-            int alpha;
-
-            memcpy (&pixel, b, sizeof (guint32));
-            alpha = pixel[3];
-            if (alpha == 0)
-                *b = 0;
-            else
-                *b = alpha << 24 |
-                    (int) pixel[0] * alpha / 255 << 16 |
-                    (int) pixel[1] * alpha / 255 << 8 | (int) pixel[2] * alpha / 255;
-        }
-    }
-}
diff --git a/rsvg-cairo-draw.h b/rsvg-cairo-draw.h
index 7d354aa..01556b7 100644
--- a/rsvg-cairo-draw.h
+++ b/rsvg-cairo-draw.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-shapes.c: Draw shapes with libart
 
@@ -31,23 +32,27 @@
 
 G_BEGIN_DECLS 
 
-PangoContext	*rsvg_cairo_create_pango_context    (RsvgDrawingCtx *ctx);
-void		 rsvg_cairo_render_pango_layout	    (RsvgDrawingCtx *ctx, PangoLayout *layout, 
-						     double x, double y);
-void		 rsvg_cairo_render_path		    (RsvgDrawingCtx *ctx, 
-						     const RsvgBpathDef * path);
-void		 rsvg_cairo_render_image	    (RsvgDrawingCtx *ctx, const GdkPixbuf * img, 
-						     double x, double y, double w, double h);
-void		 rsvg_cairo_push_discrete_layer	    (RsvgDrawingCtx *ctx);
-void		 rsvg_cairo_pop_discrete_layer	    (RsvgDrawingCtx *ctx);
-void		 rsvg_cairo_add_clipping_rect	    (RsvgDrawingCtx *ctx,
-						     double x, double y, double width, double height);
-
-GdkPixbuf	*rsvg_cairo_get_image_of_node	    (RsvgDrawingCtx *ctx, RsvgNode *drawable, 
-						     double width, double height);
-
-void		 rsvg_cairo_to_pixbuf		    (guint8 * pixels, int rowstride, int height);
-void		 rsvg_pixbuf_to_cairo		    (guint8 * pixels, int rowstride, int height);
+G_GNUC_INTERNAL
+PangoContext    *rsvg_cairo_create_pango_context    (RsvgDrawingCtx *ctx);
+G_GNUC_INTERNAL
+void         rsvg_cairo_render_pango_layout	    (RsvgDrawingCtx *ctx, PangoLayout *layout, 
+                                                 double x, double y);
+G_GNUC_INTERNAL
+void         rsvg_cairo_render_path             (RsvgDrawingCtx *ctx, 
+                                                 const cairo_path_t *path);
+G_GNUC_INTERNAL
+void         rsvg_cairo_render_surface          (RsvgDrawingCtx *ctx, cairo_surface_t *surface,
+                                                 double x, double y, double w, double h);
+G_GNUC_INTERNAL
+void         rsvg_cairo_push_discrete_layer	    (RsvgDrawingCtx *ctx);
+G_GNUC_INTERNAL
+void         rsvg_cairo_pop_discrete_layer      (RsvgDrawingCtx *ctx);
+G_GNUC_INTERNAL
+void         rsvg_cairo_add_clipping_rect       (RsvgDrawingCtx *ctx,
+                                                 double x, double y, double width, double height);
+G_GNUC_INTERNAL
+cairo_surface_t*rsvg_cairo_get_surface_of_node  (RsvgDrawingCtx *ctx, RsvgNode *drawable, 
+                                                 double width, double height);
 
 G_END_DECLS
 
diff --git a/rsvg-cairo-render.c b/rsvg-cairo-render.c
index b7f2c8e..4285fe8 100644
--- a/rsvg-cairo-render.c
+++ b/rsvg-cairo-render.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-cairo-render.c: The cairo backend plugin
 
@@ -31,6 +32,7 @@
 #include <string.h>
 
 #include "rsvg.h"
+#include "rsvg-private.h"
 #include "rsvg-cairo.h"
 #include "rsvg-cairo-draw.h"
 #include "rsvg-cairo-render.h"
@@ -40,7 +42,7 @@
 static void
 rsvg_cairo_render_free (RsvgRender * self)
 {
-    RsvgCairoRender *me = (RsvgCairoRender *) self;
+    RsvgCairoRender *me = RSVG_CAIRO_RENDER (self);
 
     /* TODO */
 
@@ -52,15 +54,16 @@ rsvg_cairo_render_new (cairo_t * cr, double width, double height)
 {
     RsvgCairoRender *cairo_render = g_new0 (RsvgCairoRender, 1);
 
+    cairo_render->super.type = RSVG_RENDER_TYPE_CAIRO;
     cairo_render->super.free = rsvg_cairo_render_free;
     cairo_render->super.create_pango_context = rsvg_cairo_create_pango_context;
     cairo_render->super.render_pango_layout = rsvg_cairo_render_pango_layout;
-    cairo_render->super.render_image = rsvg_cairo_render_image;
+    cairo_render->super.render_surface = rsvg_cairo_render_surface;
     cairo_render->super.render_path = rsvg_cairo_render_path;
     cairo_render->super.pop_discrete_layer = rsvg_cairo_pop_discrete_layer;
     cairo_render->super.push_discrete_layer = rsvg_cairo_push_discrete_layer;
     cairo_render->super.add_clipping_rect = rsvg_cairo_add_clipping_rect;
-    cairo_render->super.get_image_of_node = rsvg_cairo_get_image_of_node;
+    cairo_render->super.get_surface_of_node = rsvg_cairo_get_surface_of_node;
     cairo_render->width = width;
     cairo_render->height = height;
     cairo_render->offset_x = 0;
@@ -69,7 +72,7 @@ rsvg_cairo_render_new (cairo_t * cr, double width, double height)
     cairo_render->cr = cr;
     cairo_render->cr_stack = NULL;
     cairo_render->bb_stack = NULL;
-    cairo_render->pixbuf_stack = NULL;
+    cairo_render->surfaces_stack = NULL;
 
     return cairo_render;
 }
@@ -114,8 +117,8 @@ rsvg_cairo_new_drawing_ctx (cairo_t * cr, RsvgHandle * handle)
     RsvgDrawingCtx *draw;
     RsvgCairoRender *render;
     RsvgState *state;
-    cairo_matrix_t cairo_transform;
-    double affine[6], bbx0, bby0, bbx1, bby1;
+    cairo_matrix_t affine;
+    double bbx0, bby0, bbx1, bby1;
 
     rsvg_handle_get_dimensions (handle, &data);
     if (data.width == 0 || data.height == 0)
@@ -123,12 +126,12 @@ rsvg_cairo_new_drawing_ctx (cairo_t * cr, RsvgHandle * handle)
 
     draw = g_new (RsvgDrawingCtx, 1);
 
-    cairo_get_matrix (cr, &cairo_transform);
+    cairo_get_matrix (cr, &affine);
 
     /* find bounding box of image as transformed by the current cairo context
      * The size of this bounding box determines the size of the intermediate
      * surfaces allocated during drawing. */
-    rsvg_cairo_transformed_image_bounding_box (&cairo_transform,
+    rsvg_cairo_transformed_image_bounding_box (&affine,
                                                data.width, data.height,
                                                &bbx0, &bby0, &bbx1, &bby1);
 
@@ -144,57 +147,47 @@ rsvg_cairo_new_drawing_ctx (cairo_t * cr, RsvgHandle * handle)
     draw->state = NULL;
 
     draw->defs = handle->priv->defs;
-    draw->base_uri = g_strdup (handle->priv->base_uri);
     draw->dpi_x = handle->priv->dpi_x;
     draw->dpi_y = handle->priv->dpi_y;
-    draw->vb.w = data.em;
-    draw->vb.h = data.ex;
+    draw->vb.rect.width = data.em;
+    draw->vb.rect.height = data.ex;
     draw->pango_context = NULL;
     draw->drawsub_stack = NULL;
-    draw->ptrs = NULL;
+    draw->acquired_nodes = NULL;
 
     rsvg_state_push (draw);
-    state = rsvg_state_current (draw);
+    state = rsvg_current_state (draw);
 
     /* apply cairo transformation to our affine transform */
-    affine[0] = cairo_transform.xx;
-    affine[1] = cairo_transform.yx;
-    affine[2] = cairo_transform.xy;
-    affine[3] = cairo_transform.yy;
-    affine[4] = cairo_transform.x0;
-    affine[5] = cairo_transform.y0;
-    _rsvg_affine_multiply (state->affine, affine, state->affine);
+    cairo_matrix_multiply (&state->affine, &affine, &state->affine);
 
     /* scale according to size set by size_func callback */
-    affine[0] = data.width / data.em;
-    affine[1] = 0;
-    affine[2] = 0;
-    affine[3] = data.height / data.ex;
-    affine[4] = 0;
-    affine[5] = 0;
-    _rsvg_affine_multiply (state->affine, affine, state->affine);
+    cairo_matrix_init_scale (&affine, data.width / data.em, data.height / data.ex);
+    cairo_matrix_multiply (&state->affine, &affine, &state->affine);
 
     /* adjust transform so that the corner of the bounding box above is
      * at (0,0) - we compensate for this in _set_rsvg_affine() in
      * rsvg-cairo-render.c and a few other places */
-    state->affine[4] -= render->offset_x;
-    state->affine[5] -= render->offset_y;
+    state->affine.x0 -= render->offset_x;
+    state->affine.y0 -= render->offset_y;
 
-    rsvg_bbox_init (&((RsvgCairoRender *) draw->render)->bbox, state->affine);
+    rsvg_bbox_init (&((RsvgCairoRender *) draw->render)->bbox, &state->affine);
 
     return draw;
 }
 
 /**
- * rsvg_handle_render_cairo_sub
- * @handle: A RsvgHandle
+ * rsvg_handle_render_cairo_sub:
+ * @handle: A #RsvgHandle
  * @cr: A Cairo renderer
- * @id: An element's id within the SVG, or %NULL to render the whole SVG. For
- * example, if you have a layer called "layer1" that you wish to render, pass 
- * "##layer1" as the id.
+ * @id: (nullable): An element's id within the SVG, or %NULL to render
+ *   the whole SVG. For example, if you have a layer called "layer1"
+ *   that you wish to render, pass "##layer1" as the id.
  *
  * Draws a subset of a SVG to a Cairo surface
  *
+ * Returns: %TRUE if drawing succeeded.
+ *
  * Since: 2.14
  */
 gboolean
@@ -211,10 +204,10 @@ rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id)
     if (id && *id)
         drawsub = rsvg_defs_lookup (handle->priv->defs, id);
 
-	if (drawsub == NULL && id != NULL) {
-		/* todo: there's no way to signal that @id doesn't exist */
-		return FALSE;
-	}
+    if (drawsub == NULL && id != NULL) {
+        /* todo: there's no way to signal that @id doesn't exist */
+        return FALSE;
+    }
 
     draw = rsvg_cairo_new_drawing_ctx (cr, handle);
     if (!draw)
@@ -234,16 +227,17 @@ rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id)
     rsvg_state_pop (draw);
     rsvg_drawing_ctx_free (draw);
 
-	return TRUE;
+    return TRUE;
 }
 
 /**
- * rsvg_handle_render_cairo
- * @handle: A RsvgHandle
+ * rsvg_handle_render_cairo:
+ * @handle: A #RsvgHandle
  * @cr: A Cairo renderer
  *
  * Draws a SVG to a Cairo surface
  *
+ * Returns: %TRUE if drawing succeeded.
  * Since: 2.14
  */
 gboolean
diff --git a/rsvg-cairo-render.h b/rsvg-cairo-render.h
index e49fca2..d9bfd57 100644
--- a/rsvg-cairo-render.h
+++ b/rsvg-cairo-render.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-cairo-render.h: The cairo backend plugin
 
@@ -46,12 +47,17 @@ struct _RsvgCairoRender {
 
     RsvgBbox bbox;
     GList *bb_stack;
-    GList *pixbuf_stack;
+    GList *surfaces_stack;
 };
 
+#define RSVG_CAIRO_RENDER(render) (_RSVG_RENDER_CIC ((render), RSVG_RENDER_TYPE_CAIRO, RsvgCairoRender))
+
+G_GNUC_INTERNAL
 RsvgCairoRender *rsvg_cairo_render_new		(cairo_t * cr, double width, double height);
-void		 rsvg_cairo_render_rsvg_handle	(cairo_t * cr, RsvgHandle * handle);
-RsvgDrawingCtx *rsvg_cairo_new_drawing_ctx (cairo_t * cr, RsvgHandle * handle);
+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);
 
 G_END_DECLS
 
diff --git a/rsvg-cairo.h b/rsvg-cairo.h
index 4aa8c4b..d9ebfb2 100644
--- a/rsvg-cairo.h
+++ b/rsvg-cairo.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg-cairo.h: SAX-based renderer for SVG files using cairo
  
@@ -22,6 +23,10 @@
    Author: Carl Worth <cworth@cworth.org>
 */
 
+#if !defined (__RSVG_RSVG_H_INSIDE__) && !defined (RSVG_COMPILATION)
+#warning "Including <librsvg/rsvg-cairo.h> directly is deprecated."
+#endif
+
 #ifndef RSVG_CAIRO_H
 #define RSVG_CAIRO_H
 
@@ -29,8 +34,8 @@
 
 G_BEGIN_DECLS 
 
-gboolean	rsvg_handle_render_cairo     (RsvgHandle * handle, cairo_t * cr);
-gboolean	rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id);
+gboolean    rsvg_handle_render_cairo     (RsvgHandle * handle, cairo_t * cr);
+gboolean    rsvg_handle_render_cairo_sub (RsvgHandle * handle, cairo_t * cr, const char *id);
 
 G_END_DECLS
 
diff --git a/rsvg-compat.h b/rsvg-compat.h
new file mode 100644
index 0000000..897e4c2
--- /dev/null
+++ b/rsvg-compat.h
@@ -0,0 +1,13 @@
+/* rsvg-compat.h: miscellaneous compatibility functions to deal with deprecations in the platform */
+
+#ifndef RSVG_COMPAT_H
+#define RSVG_COMPAT_H
+
+/* g_type_init() was deprecated in glib 2.36.0 */
+#if !GLIB_CHECK_VERSION (2, 36, 0)
+#  define RSVG_G_TYPE_INIT g_type_init ()
+#else
+#  define RSVG_G_TYPE_INIT {}
+#endif
+
+#endif /* RSVG_COMPAT_H */
diff --git a/rsvg-cond.c b/rsvg-cond.c
index 1b3655f..b5c02a4 100644
--- a/rsvg-cond.c
+++ b/rsvg-cond.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-cond.c: Handle SVG conditionals
 
diff --git a/rsvg-convert.1 b/rsvg-convert.1
new file mode 100644
index 0000000..6af32fd
--- /dev/null
+++ b/rsvg-convert.1
@@ -0,0 +1,55 @@
+.TH rsvg-convert 1
+.SH NAME
+rsvg-convert \- turn SVG files into raster images.
+.SH SYNOPSIS
+.B rsvg-convert
+[options] in.svg > out.png
+.I ""
+.SH DESCRIPTION
+Converts SVG images into PNG raster images, PDF, PS, or SVG vector images, or dumps of Cairo XML or recording surfaces.
+.SS OPTIONS
+.TP
+.I "\-d \-\-dpi-x number"
+Set the X resolution of the image in pixels per inch. RSVG's current default is 90dpi
+.TP
+.I "\-p \-\-dpi-y number"
+Set the Y resolution of the image in pixels per inch. RSVG's current default is 90dpi
+.TP
+.I "\-x \-\-x\-zoom number"
+X Zoom factor, as a percentage. If unspecified, 1.0 is used as the default.
+.TP
+.I "\-y \-\-y\-zoom number"
+Y Zoom factor, as a percentage. If unspecified, 1.0 is used as the default.
+.TP
+.I "\-z \-\-\-zoom number"
+Zoom factor, as a percentage. If unspecified, 1.0 is used as the default.
+.TP
+.I "\-w \-\-width integer"
+Specify how wide you wish the image to be. If unspecified, the natural width of the image is used as the default.
+.TP
+.I "\-h \-\-height integer"
+Specify how tall you wish the image to be. If unspecified, the natural height of the image is used as the default.
+.TP
+.I "\-f \-\-format [png, pdf, ps, svg, xml, recording]"
+Specify the output format you wish the image to be saved in. If unspecified, PNG is used as the default.
+.TP
+.I "\-o \-\-output filename"
+Specify the output filename. If unspecified, outputs to stdout.
+.TP
+.I "\-a \-\-keep-aspect-ratio"
+Specify that the aspect ratio is to be preserved. If unspecified, aspect ratio will not be preserved.
+.TP
+.I "\-b \-\-background-color [black, white, #abccee, #aaa...]"
+Specify the background color. If unspecified, none is used as the default.
+.TP
+.I "\-\-base-uri uri"
+Specify the base URI for SVG files. If unspecified, none is used as the default.
+.TP
+.I "\-v \-\-version"
+Display what version of rsvg this is.
+.SH MORE INFORMATION
+http://librsvg.sourceforge.net/
+http://www.w3.org/TR/SVG11/
+http://www.gnome.org/
+.SH "AUTHORS"
+Dom Lachowicz (cinamod@hotmail.com), Caleb Moore (c.moore@student.unsw.edu.au), and a host of others.
diff --git a/rsvg-convert.c b/rsvg-convert.c
index 115d825..468e824 100644
--- a/rsvg-convert.c
+++ b/rsvg-convert.c
@@ -1,4 +1,6 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
+/*
 
    rsvg-convert.c: Command line utility for exercising rsvg with cairo.
  
@@ -32,11 +34,25 @@
 #include <stdlib.h>
 #include <string.h>
 #include <locale.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#ifdef G_OS_UNIX
+#include <gio/gunixinputstream.h>
+#endif
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <gio/gwin32inputstream.h>
+#endif
 
 #include "rsvg-css.h"
 #include "rsvg.h"
-#include "rsvg-cairo.h"
-#include "rsvg-private.h"
+#include "rsvg-compat.h"
+#include "rsvg-size-callback.h"
 
 #ifdef CAIRO_HAS_PS_SURFACE
 #include <cairo-ps.h>
@@ -50,69 +66,47 @@
 #include <cairo-svg.h>
 #endif
 
+#ifdef CAIRO_HAS_XML_SURFACE
+#include <cairo-xml.h>
+#endif
+
 static void
 display_error (GError * err)
 {
     if (err) {
-        g_print ("%s\n", err->message);
+        g_printerr ("%s\n", err->message);
         g_error_free (err);
     }
 }
 
-static RsvgHandle *
-rsvg_handle_new_from_stdio_file (FILE * f, GError ** error)
-{
-    RsvgHandle *handle;
-    gchar *current_dir;
-    gchar *base_uri;
-
-    handle = rsvg_handle_new ();
-
-    while (!feof (f)) {
-        guchar buffer[4096];
-        gsize length = fread (buffer, 1, sizeof (buffer), f);
-
-        if (length > 0) {
-            if (!rsvg_handle_write (handle, buffer, length, error)) {
-                g_object_unref (G_OBJECT (handle));
-                return NULL;
-            }
-        } else if (ferror (f)) {
-            g_object_unref (G_OBJECT (handle));
-            return NULL;
-        }
-    }
-
-    if (!rsvg_handle_close (handle, error)) {
-        g_object_unref (G_OBJECT (handle));
-        return NULL;
-    }
-
-    current_dir = g_get_current_dir ();
-    base_uri = g_build_filename (current_dir, "file.svg", NULL);
-    rsvg_handle_set_base_uri (handle, base_uri);
-    g_free (base_uri);
-    g_free (current_dir);
-
-    return handle;
-}
-
-static void
-rsvg_cairo_size_callback (int *width, int *height, gpointer data)
-{
-    RsvgDimensionData *dimensions = data;
-    *width = dimensions->width;
-    *height = dimensions->height;
-}
-
 static cairo_status_t
 rsvg_cairo_write_func (void *closure, const unsigned char *data, unsigned int length)
 {
     if (fwrite (data, 1, length, (FILE *) closure) == length)
-		return CAIRO_STATUS_SUCCESS;
-	return CAIRO_STATUS_WRITE_ERROR;
+        return CAIRO_STATUS_SUCCESS;
+    return CAIRO_STATUS_WRITE_ERROR;
 }
 
+static char *
+get_lookup_id_from_command_line (const char *lookup_id)
+{
+    char *export_lookup_id;
+
+    if (lookup_id == NULL)
+        export_lookup_id = NULL;
+    else {
+        /* rsvg_handle_has_sub() and rsvg_defs_lookup() expect ids to have a
+         * '#' prepended to them, so they can lookup ids in externs like
+         * "subfile.svg#subid".  For the user's convenience, we include this
+         * '#' automatically; we only support specifying ids from the
+         * toplevel, and don't expect users to lookup things in externs.
+         */
+        export_lookup_id = g_strdup_printf ("#%s", lookup_id);
+    }
+
+    return export_lookup_id;
+}
+ 
 int
 main (int argc, char **argv)
 {
@@ -127,21 +121,32 @@ main (int argc, char **argv)
     int bVersion = 0;
     char *format = NULL;
     char *output = NULL;
+    char *export_id = NULL;
     int keep_aspect_ratio = FALSE;
-    guint32 background_color = NULL;
+    guint32 background_color = 0;
     char *background_color_str = NULL;
-    char *base_uri = NULL;
     gboolean using_stdin = FALSE;
+    gboolean unlimited = FALSE;
+    gboolean keep_image_data = FALSE;
+    gboolean no_keep_image_data = FALSE;
     GError *error = NULL;
 
     int i;
     char **args = NULL;
     gint n_args = 0;
-    RsvgHandle *rsvg;
+    RsvgHandle *rsvg = NULL;
     cairo_surface_t *surface = NULL;
     cairo_t *cr = NULL;
+    RsvgHandleFlags flags = RSVG_HANDLE_FLAGS_NONE;
     RsvgDimensionData dimensions;
     FILE *output_file = stdout;
+    char *export_lookup_id;
+    double unscaled_width, unscaled_height;
+    int scaled_width, scaled_height;
+
+#ifdef G_OS_WIN32
+    HANDLE handle;
+#endif
 
     GOptionEntry options_table[] = {
         {"dpi-x", 'd', 0, G_OPTION_ARG_DOUBLE, &dpi_x,
@@ -159,28 +164,33 @@ main (int argc, char **argv)
         {"height", 'h', 0, G_OPTION_ARG_INT, &height,
          N_("height [optional; defaults to the SVG's height]"), N_("<int>")},
         {"format", 'f', 0, G_OPTION_ARG_STRING, &format,
-         N_("save format [optional; defaults to 'png']"), N_("[png, pdf, ps, svg]")},
+         N_("save format [optional; defaults to 'png']"), N_("[png, pdf, ps, eps, svg, xml, recording]")},
         {"output", 'o', 0, G_OPTION_ARG_STRING, &output,
          N_("output filename [optional; defaults to stdout]"), NULL},
+        {"export-id", 'i', 0, G_OPTION_ARG_STRING, &export_id,
+         N_("SVG id of object to export [optional; defaults to exporting all objects]"), N_("<object id>")},
         {"keep-aspect-ratio", 'a', 0, G_OPTION_ARG_NONE, &keep_aspect_ratio,
          N_("whether to preserve the aspect ratio [optional; defaults to FALSE]"), NULL},
         {"background-color", 'b', 0, G_OPTION_ARG_STRING, &background_color_str,
          N_("set the background color [optional; defaults to None]"), N_("[black, white, #abccee, #aaa...]")},
+        {"unlimited", 'u', 0, G_OPTION_ARG_NONE, &unlimited, N_("Allow huge SVG files"), NULL},
+        {"keep-image-data", 0, 0, G_OPTION_ARG_NONE, &keep_image_data, N_("Keep image data"), NULL},
+        {"no-keep-image-data", 0, 0, G_OPTION_ARG_NONE, &no_keep_image_data, N_("Don't keep image data"), NULL},
         {"version", 'v', 0, G_OPTION_ARG_NONE, &bVersion, N_("show version information"), NULL},
-        {"base-uri", 'b', 0, G_OPTION_ARG_STRING, &base_uri, N_("base uri"), NULL},
         {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("[FILE...]")},
         {NULL}
     };
 
-	/* Set the locale so that UTF-8 filenames work */
-	setlocale(LC_ALL, "");
+    /* Set the locale so that UTF-8 filenames work */
+    setlocale(LC_ALL, "");
 
-	g_thread_init(NULL);
+    RSVG_G_TYPE_INIT;
 
     g_option_context = g_option_context_new (_("- SVG Converter"));
     g_option_context_add_main_entries (g_option_context, options_table, NULL);
     g_option_context_set_help_enabled (g_option_context, TRUE);
     if (!g_option_context_parse (g_option_context, &argc, &argv, &error)) {
+        g_option_context_free (g_option_context);
         display_error (error);
         exit (1);
     }
@@ -195,9 +205,12 @@ main (int argc, char **argv)
     if (output != NULL) {
         output_file = fopen (output, "wb");
         if (!output_file) {
-            fprintf (stderr, _("Error saving to file: %s\n"), output);
+            g_printerr (_("Error saving to file: %s\n"), output);
+            g_free (output);
             exit (1);
         }
+
+        g_free (output);
     }
 
     if (args)
@@ -207,50 +220,97 @@ main (int argc, char **argv)
     if (n_args == 0) {
         n_args = 1;
         using_stdin = TRUE;
-    } else if (n_args > 1 && (!format || !(!strcmp (format, "ps") || !strcmp (format, "pdf")))) {
-        fprintf (stderr, _("Multiple SVG files are only allowed for PDF and PS output.\n"));
+    } else if (n_args > 1 && (!format || !(!strcmp (format, "ps") || !strcmp (format, "eps") || !strcmp (format, "pdf")))) {
+        g_printerr (_("Multiple SVG files are only allowed for PDF and (E)PS output.\n"));
         exit (1);
     }
 
+    if (format != NULL &&
+        (g_str_equal (format, "ps") || g_str_equal (format, "eps") || g_str_equal (format, "pdf")) &&
+        !no_keep_image_data)
+        keep_image_data = TRUE;
+
     if (zoom != 1.0)
         x_zoom = y_zoom = zoom;
 
-    rsvg_init ();
     rsvg_set_default_dpi_x_y (dpi_x, dpi_y);
 
+    if (unlimited)
+        flags |= RSVG_HANDLE_FLAG_UNLIMITED;
+
+    if (keep_image_data)
+        flags |= RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA;
+
     for (i = 0; i < n_args; i++) {
+        GFile *file;
+        GInputStream *stream;
+
+        if (using_stdin) {
+            file = NULL;
+#ifdef _WIN32
+            handle = GetStdHandle (STD_INPUT_HANDLE);
+
+            if (handle == INVALID_HANDLE_VALUE) {
+              gchar *emsg = g_win32_error_message (GetLastError());
+              g_printerr ( _("Unable to acquire HANDLE for STDIN: %s\n"), emsg);
+              g_free (emsg);
+              exit (1);
+            }
+            stream = g_win32_input_stream_new (handle, FALSE);
+#else
+            stream = g_unix_input_stream_new (STDIN_FILENO, FALSE);
+#endif
+        } else {
+            file = g_file_new_for_commandline_arg (args[i]);
+            stream = (GInputStream *) g_file_read (file, NULL, &error);
 
-        if (using_stdin)
-            rsvg = rsvg_handle_new_from_stdio_file (stdin, &error);
-        else
-            rsvg = rsvg_handle_new_from_file (args[i], &error);
+            if (stream == NULL)
+                goto done;
+        }
 
-        if (!rsvg) {
-            fprintf (stderr, _("Error reading SVG:"));
+        rsvg = rsvg_handle_new_from_stream_sync (stream, file, flags, NULL, &error);
+
+    done:
+        if (stream) {
+            g_object_unref (stream);
+            stream = NULL;
+        }
+
+        if (file) {
+            g_object_unref (file);
+            file = NULL;
+        }
+
+        if (error != NULL) {
+            g_printerr (_("Error reading SVG:"));
             display_error (error);
-            fprintf (stderr, "\n");
+            g_printerr ("\n");
             exit (1);
         }
 
-        if (base_uri)
-            rsvg_handle_set_base_uri (rsvg, base_uri);
-
-        /* in the case of multi-page output, all subsequent SVGs are scaled to the first's size */
-        rsvg_handle_set_size_callback (rsvg, rsvg_cairo_size_callback, &dimensions, NULL);
+        export_lookup_id = get_lookup_id_from_command_line (export_id);
+        if (export_lookup_id != NULL
+            && !rsvg_handle_has_sub (rsvg, export_lookup_id)) {
+            g_printerr (_("File %s does not have an object with id \"%s\"\n"), args[i], export_id);
+            exit (1);
+        }
 
         if (i == 0) {
             struct RsvgSizeCallbackData size_data;
 
-            rsvg_handle_get_dimensions (rsvg, &dimensions);
+            if (!rsvg_handle_get_dimensions_sub (rsvg, &dimensions, export_lookup_id))
+                g_printerr ("Could not get dimensions for file %s\n", args[i]);
+
+            unscaled_width = dimensions.width;
+            unscaled_height = dimensions.height;
+
             /* if both are unspecified, assume user wants to zoom the image in at least 1 dimension */
             if (width == -1 && height == -1) {
                 size_data.type = RSVG_SIZE_ZOOM;
                 size_data.x_zoom = x_zoom;
                 size_data.y_zoom = y_zoom;
                 size_data.keep_aspect_ratio = keep_aspect_ratio;
-            }
-            /* if both are unspecified, assume user wants to resize image in at least 1 dimension */
-            else if (x_zoom == 1.0 && y_zoom == 1.0) {
+            } else if (x_zoom == 1.0 && y_zoom == 1.0) {
                 /* if one parameter is unspecified, assume user wants to keep the aspect ratio */
                 if (width == -1 || height == -1) {
                     size_data.type = RSVG_SIZE_WH_MAX;
@@ -273,28 +333,45 @@ main (int argc, char **argv)
                 size_data.keep_aspect_ratio = keep_aspect_ratio;
             }
 
-            _rsvg_size_callback (&dimensions.width, &dimensions.height, &size_data);
+            scaled_width = dimensions.width;
+            scaled_height = dimensions.height;
+            _rsvg_size_callback (&scaled_width, &scaled_height, &size_data);
 
             if (!format || !strcmp (format, "png"))
                 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                                      dimensions.width, dimensions.height);
+                                                      scaled_width, scaled_height);
 #ifdef CAIRO_HAS_PDF_SURFACE
             else if (!strcmp (format, "pdf"))
                 surface = cairo_pdf_surface_create_for_stream (rsvg_cairo_write_func, output_file,
-                                                               dimensions.width, dimensions.height);
+                                                               scaled_width, scaled_height);
 #endif
 #ifdef CAIRO_HAS_PS_SURFACE
-            else if (!strcmp (format, "ps"))
+            else if (!strcmp (format, "ps") || !strcmp (format, "eps")){
                 surface = cairo_ps_surface_create_for_stream (rsvg_cairo_write_func, output_file,
-                                                              dimensions.width, dimensions.height);
+                                                              scaled_width, scaled_height);
+                if(!strcmp (format, "eps"))
+                    cairo_ps_surface_set_eps(surface, TRUE);
+            }
 #endif
 #ifdef CAIRO_HAS_SVG_SURFACE
             else if (!strcmp (format, "svg"))
                 surface = cairo_svg_surface_create_for_stream (rsvg_cairo_write_func, output_file,
-                                                               dimensions.width, dimensions.height);
+                                                               scaled_width, scaled_height);
+#endif
+#ifdef CAIRO_HAS_XML_SURFACE
+            else if (!strcmp (format, "xml")) {
+                cairo_device_t *device = cairo_xml_create_for_stream (rsvg_cairo_write_func, output_file);
+                surface = cairo_xml_surface_create (device, CAIRO_CONTENT_COLOR_ALPHA,
+                                                    scaled_width, scaled_height);
+                cairo_device_destroy (device);
+            }
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE (1, 10, 0)
+            else if (!strcmp (format, "recording"))
+                surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR_ALPHA, NULL);
+#endif
 #endif
             else {
-                fprintf (stderr, _("Unknown output format."));
+                g_printerr (_("Unknown output format."));
                 exit (1);
             }
 
@@ -310,26 +387,57 @@ main (int argc, char **argv)
                 ((background_color >> 16) & 0xff) / 255.0, 
                 ((background_color >> 8) & 0xff) / 255.0, 
                 ((background_color >> 0) & 0xff) / 255.0);
-            cairo_rectangle (cr, 0, 0, dimensions.width, dimensions.height);
+            cairo_rectangle (cr, 0, 0, scaled_width, scaled_height);
             cairo_fill (cr);
         }
 
-        rsvg_handle_render_cairo (rsvg, cr);
+        if (export_lookup_id) {
+            RsvgPositionData pos;
+
+            if (!rsvg_handle_get_position_sub (rsvg, &pos, export_lookup_id)) {
+                g_printerr (_("File %s does not have an object with id \"%s\"\n"), args[i], export_id);
+                exit (1);
+            }
+
+            /* Move the whole thing to 0, 0 so the object to export is at the origin */
+            cairo_translate (cr, -pos.x, -pos.y);
+        }
+
+        cairo_scale (cr,
+                     scaled_width / unscaled_width,
+                     scaled_height / unscaled_height);
+        rsvg_handle_render_cairo_sub (rsvg, cr, export_lookup_id);
+
+        g_free (export_lookup_id);
 
         if (!format || !strcmp (format, "png"))
             cairo_surface_write_to_png_stream (surface, rsvg_cairo_write_func, output_file);
-        else
+#if CAIRO_HAS_XML_SURFACE && CAIRO_VERSION >= CAIRO_VERSION_ENCODE (1, 10, 0)
+        else if (!strcmp (format, "recording")) {
+            cairo_device_t *device = cairo_xml_create_for_stream (rsvg_cairo_write_func, output_file);
+            cairo_xml_for_recording_surface (device, surface);
+            cairo_device_destroy (device);
+        }
+#endif
+        else if (!strcmp (format, "xml"))
+          ;
+        else if (!strcmp (format, "svg") || !strcmp (format, "pdf") || !strcmp (format, "ps") || !strcmp (format, "eps"))
             cairo_show_page (cr);
+        else
+          g_assert_not_reached ();
 
-        g_object_unref (G_OBJECT (rsvg));
+        g_object_unref (rsvg);
     }
 
     cairo_destroy (cr);
+
     cairo_surface_destroy (surface);
 
     fclose (output_file);
 
-    rsvg_term ();
+    g_strfreev (args);
+
+    rsvg_cleanup ();
 
     return 0;
 }
diff --git a/rsvg-css.c b/rsvg-css.c
index e988ce5..5b359a2 100644
--- a/rsvg-css.c
+++ b/rsvg-css.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg-css.c: Parse CSS basic data types.
  
@@ -38,6 +39,10 @@
 #include <errno.h>
 #include <math.h>
 
+#include <libxml/parser.h>
+
+#include <libcroco/libcroco.h>
+
 #define POINTS_PER_INCH (72.0)
 #define CM_PER_INCH     (2.54)
 #define MM_PER_INCH     (25.4)
@@ -47,7 +52,7 @@
 #define UNSETINHERIT() G_STMT_START {if (inherit != NULL) *inherit = FALSE;} G_STMT_END
 
 /**
- * rsvg_css_parse_vbox
+ * rsvg_css_parse_vbox:
  * @vbox: The CSS viewBox
  * @x : The X output
  * @y: The Y output
@@ -64,8 +69,8 @@ rsvg_css_parse_vbox (const char *vbox)
     guint list_len;
     vb.active = FALSE;
 
-    vb.x = vb.y = 0;
-    vb.w = vb.h = 0;
+    vb.rect.x = vb.rect.y = 0;
+    vb.rect.width = vb.rect.height = 0;
 
     list = rsvg_css_parse_number_list (vbox, &list_len);
 
@@ -75,10 +80,10 @@ rsvg_css_parse_vbox (const char *vbox)
         g_free (list);
         return vb;
     } else {
-        vb.x = list[0];
-        vb.y = list[1];
-        vb.w = list[2];
-        vb.h = list[3];
+        vb.rect.x = list[0];
+        vb.rect.y = list[1];
+        vb.rect.width = list[2];
+        vb.rect.height = list[3];
         vb.active = TRUE;
 
         g_free (list);
@@ -93,8 +98,8 @@ typedef enum _RelativeSize {
 } RelativeSize;
 
 static double
-rsvg_css_parse_raw_length (const char *str, gint * in,
-                           gint * percent, gint * em, gint * ex, RelativeSize * relative_size)
+rsvg_css_parse_raw_length (const char *str, gboolean * in,
+                           gboolean * percent, gboolean * em, gboolean * ex, RelativeSize * relative_size)
 {
     double length = 0.0;
     char *p = NULL;
@@ -177,7 +182,7 @@ RsvgLength
 _rsvg_css_parse_length (const char *str)
 {
     RsvgLength out;
-    gint percent, em, ex, in;
+    gboolean percent, em, ex, in;
     RelativeSize relative_size = RELATIVE_SIZE_NORMAL;
     percent = em = ex = in = FALSE;
 
@@ -199,6 +204,31 @@ _rsvg_css_parse_length (const char *str)
     return out;
 }
 
+/* Recursive evaluation of all parent elements regarding absolute font size */
+double
+_rsvg_css_normalize_font_size (RsvgState * state, RsvgDrawingCtx * ctx)
+{
+    RsvgState *parent;
+
+    switch (state->font_size.factor) {
+    case 'p':
+    case 'm':
+    case 'x':
+        parent = rsvg_state_parent (state);
+        if (parent) {
+            double parent_size;
+            parent_size = _rsvg_css_normalize_font_size (parent, ctx);
+            return state->font_size.length * parent_size;
+        }
+        break;
+    default:
+        return _rsvg_css_normalize_length (&state->font_size, ctx, 'v');
+        break;
+    }
+
+    return 12.;
+}
+
 double
 _rsvg_css_normalize_length (const RsvgLength * in, RsvgDrawingCtx * ctx, char dir)
 {
@@ -206,14 +236,14 @@ _rsvg_css_normalize_length (const RsvgLength * in, RsvgDrawingCtx * ctx, char di
         return in->length;
     else if (in->factor == 'p') {
         if (dir == 'h')
-            return in->length * ctx->vb.w;
+            return in->length * ctx->vb.rect.width;
         if (dir == 'v')
-            return in->length * ctx->vb.h;
+            return in->length * ctx->vb.rect.height;
         if (dir == 'o')
-            return in->length * rsvg_viewport_percentage (ctx->vb.w, ctx->vb.h);
+            return in->length * rsvg_viewport_percentage (ctx->vb.rect.width,
+                                                          ctx->vb.rect.height);
     } else if (in->factor == 'm' || in->factor == 'x') {
-        double font = _rsvg_css_hand_normalize_length (&rsvg_state_current (ctx)->font_size,
-                                                       ctx->dpi_y, ctx->vb.h, 1);
+        double font = _rsvg_css_normalize_font_size (rsvg_current_state (ctx), ctx);
         if (in->factor == 'm')
             return in->length * font;
         else
@@ -234,6 +264,27 @@ _rsvg_css_normalize_length (const RsvgLength * in, RsvgDrawingCtx * ctx, char di
     return 0;
 }
 
+/* Recursive evaluation of all parent elements regarding basline-shift */
+double
+_rsvg_css_accumulate_baseline_shift (RsvgState * state, RsvgDrawingCtx * ctx)
+{
+    RsvgState *parent;
+    double shift = 0.;
+
+    parent = rsvg_state_parent (state);
+    if (parent) {
+        if (state->has_baseline_shift) {
+            double parent_font_size;
+            parent_font_size = _rsvg_css_normalize_font_size (parent, ctx); /* font size from here */
+            shift = parent_font_size * state->baseline_shift;
+        }
+        shift += _rsvg_css_accumulate_baseline_shift (parent, ctx); /* baseline-shift for parent element */
+    }
+
+    return shift;
+}
+
+
 double
 _rsvg_css_hand_normalize_length (const RsvgLength * in, gdouble pixels_per_inch,
                                  gdouble width_or_height, gdouble font_size)
@@ -252,98 +303,36 @@ _rsvg_css_hand_normalize_length (const RsvgLength * in, gdouble pixels_per_inch,
     return 0;
 }
 
-double
-_rsvg_css_hand_normalize_length_sub (const RsvgLength * in, gdouble length, 
-				     gdouble pixels_per_inch, gdouble width_or_height, 
-				     gdouble font_size)
-{
-    if (in->factor == '\0')
-        return length;
-    else if (in->factor == 'p')
-        return length * width_or_height;
-    else if (in->factor == 'm')
-        return length * font_size;
-    else if (in->factor == 'x')
-        return length * font_size / 2.;
-    else if (in->factor == 'i')
-        return length * pixels_per_inch;
-
-    return 0;
-}
-
-gboolean
-rsvg_css_param_match (const char *str, const char *param_name)
-{
-    int i;
-
-    for (i = 0; str[i] != '\0' && param_name[i] != '\0'; i++)
-        if (param_name[i] != str[i])
-            return FALSE;
-    return str[i] == ':' && param_name[i] == '\0';
-}
-
-int
-rsvg_css_param_arg_offset (const char *str)
-{
-    int i;
-    int found;
-    found = -1;
-    i = 0;
-    while (str[i] != '\0') {
-        for (; str[i] != '\0' && str[i] != ':'; i++);
-        if (str[i] != '\0')
-            i++;
-        for (; str[i] == ' '; i++);
-        if (str[i] != '\0')
-            found = i;
-    }
-    if (found == -1)
-        found = i;
-    return found;
-}
-
 static gint
-rsvg_css_clip_rgb_percent (gdouble in_percent)
+rsvg_css_clip_rgb_percent (const char *s, double max)
 {
-    /* spec says to clip these values */
-    if (in_percent > 100.)
-        return 255;
-    else if (in_percent <= 0.)
-        return 0;
-    return (gint) floor (255. * in_percent / 100. + 0.5);
-}
-
-static gint
-rsvg_css_clip_rgb (gint rgb)
-{
-    /* spec says to clip these values */
-    if (rgb > 255)
-        return 255;
-    else if (rgb < 0)
-        return 0;
-    return rgb;
-}
-
-typedef struct {
-    const char *const name;
-    guint rgb;
-} ColorPair;
+    double value;
+    char *end;
 
-/* compare function callback for bsearch */
-static int
-rsvg_css_color_compare (const void *a, const void *b)
-{
-    const char *needle = (const char *) a;
-    const ColorPair *haystack = (const ColorPair *) b;
+    value = g_ascii_strtod (s, &end);
 
-    return g_ascii_strcasecmp (needle, haystack->name);
+    if (*end == '%') {
+        value = CLAMP (value, 0, 100) / 100.0;
+    }
+    else {
+        value = CLAMP (value, 0, max) / max;
+    }
+    
+    return (gint) floor (value * 255 + 0.5);
 }
 
 /* pack 3 [0,255] ints into one 32 bit one */
-#define PACK_RGB(r,g,b) (((r) << 16) | ((g) << 8) | (b))
+#define PACK_RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+#define PACK_RGB(r,g,b) PACK_RGBA(r, g, b, 255)
 
 /**
+ * rsvg_css_parse_color:
+ * @str: string to parse
+ * @inherit: whether to inherit
+ *
  * Parse a CSS2 color specifier, return RGB value
+ *
+ * Returns: and RGB value
  */
 guint32
 rsvg_css_parse_color (const char *str, gboolean * inherit)
@@ -371,224 +360,83 @@ rsvg_css_parse_color (const char *str, gboolean * inherit)
             val = ((val & 0xf00) << 8) | ((val & 0x0f0) << 4) | (val & 0x00f);
             val |= val << 4;
         }
-    }
-    /* i want to use g_str_has_prefix but it isn't in my gstrfuncs.h?? */
-    else if (strstr (str, "rgb") != NULL) {
-        gint r, g, b;
-        r = g = b = 0;
-
-        if (strstr (str, "%") != 0) {
-            guint i, nb_toks;
-            char **toks;
 
-            /* assume rgb (9%, 100%, 23%) */
-            for (i = 0; str[i] != '('; i++);
-
-            i++;
+        val |= 0xff000000; /* opaque */
+    }
+    else if (g_str_has_prefix (str, "rgb")) {
+        gint r, g, b, a;
+        gboolean has_alpha;
+        guint nb_toks;
+        char **toks;
 
-            toks = rsvg_css_parse_list (str + i, &nb_toks);
+        r = g = b = 0;
+        a = 255;
 
-            if (toks) {
-                if (nb_toks == 3) {
-                    r = rsvg_css_clip_rgb_percent (g_ascii_strtod (toks[0], NULL));
-                    g = rsvg_css_clip_rgb_percent (g_ascii_strtod (toks[1], NULL));
-                    b = rsvg_css_clip_rgb_percent (g_ascii_strtod (toks[2], NULL));
-                }
+        if (str[3] == 'a') {
+            /* "rgba" */
+            has_alpha = TRUE;
+            str += 4;
+        }
+        else {
+            /* "rgb" */
+            has_alpha = FALSE;
+            str += 3;
+        }
 
-                g_strfreev (toks);
+        str = strchr (str, '(');
+        if (str == NULL)
+          return val;
+
+        toks = rsvg_css_parse_list (str + 1, &nb_toks);
+
+        if (toks) {
+            if (nb_toks == (has_alpha ? 4 : 3)) {
+                r = rsvg_css_clip_rgb_percent (toks[0], 255.0);
+                g = rsvg_css_clip_rgb_percent (toks[1], 255.0);
+                b = rsvg_css_clip_rgb_percent (toks[2], 255.0);
+                if (has_alpha)
+                    a = rsvg_css_clip_rgb_percent (toks[3], 1.0);
+                else
+                    a = 255;
             }
-        } else {
-            /* assume "rgb (r, g, b)" */
-            if (3 == sscanf (str, " rgb ( %d , %d , %d ) ", &r, &g, &b)) {
-                r = rsvg_css_clip_rgb (r);
-                g = rsvg_css_clip_rgb (g);
-                b = rsvg_css_clip_rgb (b);
-            } else
-                r = g = b = 0;
+
+            g_strfreev (toks);
         }
 
-        val = PACK_RGB (r, g, b);
+        val = PACK_RGBA (r, g, b, a);
     } else if (!strcmp (str, "inherit"))
         UNSETINHERIT ();
     else {
-        static const ColorPair color_list[] = {
-            {"aliceblue", PACK_RGB (240, 248, 255)},
-            {"antiquewhite", PACK_RGB (250, 235, 215)},
-            {"aqua", PACK_RGB (0, 255, 255)},
-            {"aquamarine", PACK_RGB (127, 255, 212)},
-            {"azure", PACK_RGB (240, 255, 255)},
-            {"beige", PACK_RGB (245, 245, 220)},
-            {"bisque", PACK_RGB (255, 228, 196)},
-            {"black", PACK_RGB (0, 0, 0)},
-            {"blanchedalmond", PACK_RGB (255, 235, 205)},
-            {"blue", PACK_RGB (0, 0, 255)},
-            {"blueviolet", PACK_RGB (138, 43, 226)},
-            {"brown", PACK_RGB (165, 42, 42)},
-            {"burlywood", PACK_RGB (222, 184, 135)},
-            {"cadetblue", PACK_RGB (95, 158, 160)},
-            {"chartreuse", PACK_RGB (127, 255, 0)},
-            {"chocolate", PACK_RGB (210, 105, 30)},
-            {"coral", PACK_RGB (255, 127, 80)},
-            {"cornflowerblue", PACK_RGB (100, 149, 237)},
-            {"cornsilk", PACK_RGB (255, 248, 220)},
-            {"crimson", PACK_RGB (220, 20, 60)},
-            {"cyan", PACK_RGB (0, 255, 255)},
-            {"darkblue", PACK_RGB (0, 0, 139)},
-            {"darkcyan", PACK_RGB (0, 139, 139)},
-            {"darkgoldenrod", PACK_RGB (184, 132, 11)},
-            {"darkgray", PACK_RGB (169, 169, 169)},
-            {"darkgreen", PACK_RGB (0, 100, 0)},
-            {"darkgrey", PACK_RGB (169, 169, 169)},
-            {"darkkhaki", PACK_RGB (189, 183, 107)},
-            {"darkmagenta", PACK_RGB (139, 0, 139)},
-            {"darkolivegreen", PACK_RGB (85, 107, 47)},
-            {"darkorange", PACK_RGB (255, 140, 0)},
-            {"darkorchid", PACK_RGB (153, 50, 204)},
-            {"darkred", PACK_RGB (139, 0, 0)},
-            {"darksalmon", PACK_RGB (233, 150, 122)},
-            {"darkseagreen", PACK_RGB (143, 188, 143)},
-            {"darkslateblue", PACK_RGB (72, 61, 139)},
-            {"darkslategray", PACK_RGB (47, 79, 79)},
-            {"darkslategrey", PACK_RGB (47, 79, 79)},
-            {"darkturquoise", PACK_RGB (0, 206, 209)},
-            {"darkviolet", PACK_RGB (148, 0, 211)},
-            {"deeppink", PACK_RGB (255, 20, 147)},
-            {"deepskyblue", PACK_RGB (0, 191, 255)},
-            {"dimgray", PACK_RGB (105, 105, 105)},
-            {"dimgrey", PACK_RGB (105, 105, 105)},
-            {"dodgerblue", PACK_RGB (30, 144, 255)},
-            {"firebrick", PACK_RGB (178, 34, 34)},
-            {"floralwhite", PACK_RGB (255, 255, 240)},
-            {"forestgreen", PACK_RGB (34, 139, 34)},
-            {"fuchsia", PACK_RGB (255, 0, 255)},
-            {"gainsboro", PACK_RGB (220, 220, 220)},
-            {"ghostwhite", PACK_RGB (248, 248, 255)},
-            {"gold", PACK_RGB (255, 215, 0)},
-            {"goldenrod", PACK_RGB (218, 165, 32)},
-            {"gray", PACK_RGB (128, 128, 128)},
-            {"green", PACK_RGB (0, 128, 0)},
-            {"greenyellow", PACK_RGB (173, 255, 47)},
-            {"grey", PACK_RGB (128, 128, 128)},
-            {"honeydew", PACK_RGB (240, 255, 240)},
-            {"hotpink", PACK_RGB (255, 105, 180)},
-            {"indianred", PACK_RGB (205, 92, 92)},
-            {"indigo", PACK_RGB (75, 0, 130)},
-            {"ivory", PACK_RGB (255, 255, 240)},
-            {"khaki", PACK_RGB (240, 230, 140)},
-            {"lavender", PACK_RGB (230, 230, 250)},
-            {"lavenderblush", PACK_RGB (255, 240, 245)},
-            {"lawngreen", PACK_RGB (124, 252, 0)},
-            {"lemonchiffon", PACK_RGB (255, 250, 205)},
-            {"lightblue", PACK_RGB (173, 216, 230)},
-            {"lightcoral", PACK_RGB (240, 128, 128)},
-            {"lightcyan", PACK_RGB (224, 255, 255)},
-            {"lightgoldenrodyellow", PACK_RGB (250, 250, 210)},
-            {"lightgray", PACK_RGB (211, 211, 211)},
-            {"lightgreen", PACK_RGB (144, 238, 144)},
-            {"lightgrey", PACK_RGB (211, 211, 211)},
-            {"lightpink", PACK_RGB (255, 182, 193)},
-            {"lightsalmon", PACK_RGB (255, 160, 122)},
-            {"lightseagreen", PACK_RGB (32, 178, 170)},
-            {"lightskyblue", PACK_RGB (135, 206, 250)},
-            {"lightslategray", PACK_RGB (119, 136, 153)},
-            {"lightslategrey", PACK_RGB (119, 136, 153)},
-            {"lightsteelblue", PACK_RGB (176, 196, 222)},
-            {"lightyellow", PACK_RGB (255, 255, 224)},
-            {"lime", PACK_RGB (0, 255, 0)},
-            {"limegreen", PACK_RGB (50, 205, 50)},
-            {"linen", PACK_RGB (250, 240, 230)},
-            {"magenta", PACK_RGB (255, 0, 255)},
-            {"maroon", PACK_RGB (128, 0, 0)},
-            {"mediumaquamarine", PACK_RGB (102, 205, 170)},
-            {"mediumblue", PACK_RGB (0, 0, 205)},
-            {"mediumorchid", PACK_RGB (186, 85, 211)},
-            {"mediumpurple", PACK_RGB (147, 112, 219)},
-            {"mediumseagreen", PACK_RGB (60, 179, 113)},
-            {"mediumslateblue", PACK_RGB (123, 104, 238)},
-            {"mediumspringgreen", PACK_RGB (0, 250, 154)},
-            {"mediumturquoise", PACK_RGB (72, 209, 204)},
-            {"mediumvioletred", PACK_RGB (199, 21, 133)},
-            {"midnightblue", PACK_RGB (25, 25, 112)},
-            {"mintcream", PACK_RGB (245, 255, 250)},
-            {"mistyrose", PACK_RGB (255, 228, 225)},
-            {"moccasin", PACK_RGB (255, 228, 181)},
-            {"navajowhite", PACK_RGB (255, 222, 173)},
-            {"navy", PACK_RGB (0, 0, 128)},
-            {"oldlace", PACK_RGB (253, 245, 230)},
-            {"olive", PACK_RGB (128, 128, 0)},
-            {"olivedrab", PACK_RGB (107, 142, 35)},
-            {"orange", PACK_RGB (255, 165, 0)},
-            {"orangered", PACK_RGB (255, 69, 0)},
-            {"orchid", PACK_RGB (218, 112, 214)},
-            {"palegoldenrod", PACK_RGB (238, 232, 170)},
-            {"palegreen", PACK_RGB (152, 251, 152)},
-            {"paleturquoise", PACK_RGB (175, 238, 238)},
-            {"palevioletred", PACK_RGB (219, 112, 147)},
-            {"papayawhip", PACK_RGB (255, 239, 213)},
-            {"peachpuff", PACK_RGB (255, 218, 185)},
-            {"peru", PACK_RGB (205, 133, 63)},
-            {"pink", PACK_RGB (255, 192, 203)},
-            {"plum", PACK_RGB (221, 160, 203)},
-            {"powderblue", PACK_RGB (176, 224, 230)},
-            {"purple", PACK_RGB (128, 0, 128)},
-            {"red", PACK_RGB (255, 0, 0)},
-            {"rosybrown", PACK_RGB (188, 143, 143)},
-            {"royalblue", PACK_RGB (65, 105, 225)},
-            {"saddlebrown", PACK_RGB (139, 69, 19)},
-            {"salmon", PACK_RGB (250, 128, 114)},
-            {"sandybrown", PACK_RGB (244, 164, 96)},
-            {"seagreen", PACK_RGB (46, 139, 87)},
-            {"seashell", PACK_RGB (255, 245, 238)},
-            {"sienna", PACK_RGB (160, 82, 45)},
-            {"silver", PACK_RGB (192, 192, 192)},
-            {"skyblue", PACK_RGB (135, 206, 235)},
-            {"slateblue", PACK_RGB (106, 90, 205)},
-            {"slategray", PACK_RGB (119, 128, 144)},
-            {"slategrey", PACK_RGB (119, 128, 144)},
-            {"snow", PACK_RGB (255, 255, 250)},
-            {"springgreen", PACK_RGB (0, 255, 127)},
-            {"steelblue", PACK_RGB (70, 130, 180)},
-            {"tan", PACK_RGB (210, 180, 140)},
-            {"teal", PACK_RGB (0, 128, 128)},
-            {"thistle", PACK_RGB (216, 191, 216)},
-            {"tomato", PACK_RGB (255, 99, 71)},
-            {"turquoise", PACK_RGB (64, 224, 208)},
-            {"violet", PACK_RGB (238, 130, 238)},
-            {"wheat", PACK_RGB (245, 222, 179)},
-            {"white", PACK_RGB (255, 255, 255)},
-            {"whitesmoke", PACK_RGB (245, 245, 245)},
-            {"yellow", PACK_RGB (255, 255, 0)},
-            {"yellowgreen", PACK_RGB (154, 205, 50)}
-        };
-
-        ColorPair *result = bsearch (str, color_list,
-                                     sizeof (color_list) / sizeof (color_list[0]),
-                                     sizeof (ColorPair),
-                                     rsvg_css_color_compare);
-
-        /* default to black on failed lookup */
-        if (result == NULL) {
+        CRRgb rgb;
+
+        if (cr_rgb_set_from_name (&rgb, (const guchar *) str) == CR_OK) {
+            val = PACK_RGB (rgb.red, rgb.green, rgb.blue);
+        } else {
+            /* default to opaque black on failed lookup */
             UNSETINHERIT ();
-            val = 0;
-        } else
-            val = result->rgb;
+            val = PACK_RGB (0, 0, 0);
+        }
     }
 
     return val;
 }
 
 #undef PACK_RGB
+#undef PACK_RGBA
 
 guint
 rsvg_css_parse_opacity (const char *str)
 {
-    char *end_ptr;
+    char *end_ptr = NULL;
     double opacity;
 
     opacity = g_ascii_strtod (str, &end_ptr);
 
-    if (end_ptr && end_ptr[0] == '%')
-        opacity *= 0.01;
+    if (((opacity == -HUGE_VAL || opacity == HUGE_VAL) && (ERANGE == errno)) ||
+        *end_ptr != '\0')
+        opacity = 1.;
+
+    opacity = CLAMP (opacity, 0., 1.);
 
     return (guint) floor (opacity * 255. + 0.5);
 }
@@ -693,7 +541,9 @@ rsvg_css_parse_font_style (const char *str, gboolean * inherit)
             return PANGO_STYLE_OBLIQUE;
         if (!strcmp (str, "italic"))
             return PANGO_STYLE_ITALIC;
-        else if (!strcmp (str, "inherit")) {
+        if (!strcmp (str, "normal"))
+            return PANGO_STYLE_NORMAL;
+        if (!strcmp (str, "inherit")) {
             UNSETINHERIT ();
             return PANGO_STYLE_NORMAL;
         }
@@ -988,321 +838,64 @@ rsvg_css_parse_overflow (const char *str, gboolean * inherit)
     return 0;
 }
 
-/***********************************************************************/
-/***********************************************************************/
-
-/* 
-   Code largely based on xmltok_impl.c from Expat
-
-   Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
-   and Clark Cooper
-   Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
-   
-   Permission is hereby granted, free of charge, to any person obtaining
-   a copy of this software and associated documentation files (the
-   "Software"), to deal in the Software without restriction, including
-   without limitation the rights to use, copy, modify, merge, publish,
-   distribute, sublicense, and/or sell copies of the Software, and to
-   permit persons to whom the Software is furnished to do so, subject to
-   the following conditions:
-   
-   The above copyright notice and this permission notice shall be included
-   in all copies or substantial portions of the Software.
-   
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-typedef struct {
-    const char *name;
-    const char *valuePtr;
-    const char *valueEnd;
-    char normalized;
-} ATTRIBUTE;
-
-#define PTRCALL
-#define PTRFASTCALL
-#define PREFIX(x) x
-typedef void ENCODING;
-
-#define BYTE_TO_ASCII(enc, p) (*(p))
-
-/* minimum bytes per character */
-#define MINBPC(enc) 1
-
-#define BYTE_TYPE(enc, p) utf8_byte_type_table[(int)(*(p))]
-
-#define ASCII_SPACE 0x20
-
-enum {
-    BT_NONXML,
-    BT_MALFORM,
-    BT_LT,
-    BT_AMP,
-    BT_RSQB,
-    BT_LEAD2,
-    BT_LEAD3,
-    BT_LEAD4,
-    BT_TRAIL,
-    BT_CR,
-    BT_LF,
-    BT_GT,
-    BT_QUOT,
-    BT_APOS,
-    BT_EQUALS,
-    BT_QUEST,
-    BT_EXCL,
-    BT_SOL,
-    BT_SEMI,
-    BT_NUM,
-    BT_LSQB,
-    BT_S,
-    BT_NMSTRT,
-    BT_COLON,
-    BT_HEX,
-    BT_DIGIT,
-    BT_NAME,
-    BT_MINUS,
-    BT_OTHER,                   /* known not to be a name or name start character */
-    BT_NONASCII,                /* might be a name or name start character */
-    BT_PERCNT,
-    BT_LPAR,
-    BT_RPAR,
-    BT_AST,
-    BT_PLUS,
-    BT_COMMA,
-    BT_VERBAR
-};
-
-static const char utf8_byte_type_table[] = {
-    /* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
-    /* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
-    /* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
-    /* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
-    /* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
-    /* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
-    /* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
-    /* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
-    /* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
-    /* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
-    /* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
-    /* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
-    /* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
-    /* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
-    /* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
-    /* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
-    /* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
-    /* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
-    /* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
-    /* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
-    /* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
-    /* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
-    /* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
-    /* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
-    /* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
-    /* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
-    /* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
-    /* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM
-};
-
-/* This must only be called for a well-formed start-tag or empty
-   element tag.  Returns the number of attributes.  Pointers to the
-   first attsMax attributes are stored in atts.
-*/
-
-static int PTRCALL
-PREFIX (getAtts) (const ENCODING * enc, const char *ptr, int attsMax, ATTRIBUTE * atts) {
-    enum { other, inName, inValue } state = inName;
-    int nAtts = 0;
-    int open = 0;               /* defined when state == inValue;
-                                   initialization just to shut up compilers */
-
-    for (ptr += MINBPC (enc);; ptr += MINBPC (enc)) {
-        switch (BYTE_TYPE (enc, ptr)) {
-
-#define START_NAME \
-      if (state == other) { \
-        if (nAtts < attsMax) { \
-          atts[nAtts].name = ptr; \
-          atts[nAtts].normalized = 1; \
-        } \
-        state = inName; \
-      }
-
-#define LEAD_CASE(n) \
-    case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break;
-            LEAD_CASE (2) LEAD_CASE (3) LEAD_CASE (4)
-#undef LEAD_CASE
-        case BT_NONASCII:
-        case BT_NMSTRT:
-        case BT_HEX:
-            START_NAME break;
-#undef START_NAME
-        case BT_QUOT:
-            if (state != inValue) {
-                if (nAtts < attsMax)
-                    atts[nAtts].valuePtr = ptr + MINBPC (enc);
-                state = inValue;
-                open = BT_QUOT;
-            } else if (open == BT_QUOT) {
-                state = other;
-                if (nAtts < attsMax)
-                    atts[nAtts].valueEnd = ptr;
-                nAtts++;
-            }
-            break;
-        case BT_APOS:
-            if (state != inValue) {
-                if (nAtts < attsMax)
-                    atts[nAtts].valuePtr = ptr + MINBPC (enc);
-                state = inValue;
-                open = BT_APOS;
-            } else if (open == BT_APOS) {
-                state = other;
-                if (nAtts < attsMax)
-                    atts[nAtts].valueEnd = ptr;
-                nAtts++;
-            }
-            break;
-        case BT_AMP:
-            if (nAtts < attsMax)
-                atts[nAtts].normalized = 0;
-            break;
-        case BT_S:
-            if (state == inName)
-                state = other;
-            else if (state == inValue
-                     && nAtts < attsMax
-                     && atts[nAtts].normalized
-                     && (ptr == atts[nAtts].valuePtr
-                         || BYTE_TO_ASCII (enc, ptr) != ASCII_SPACE
-                         || BYTE_TO_ASCII (enc, ptr + MINBPC (enc)) == ASCII_SPACE
-                         || BYTE_TYPE (enc, ptr + MINBPC (enc)) == open))
-                atts[nAtts].normalized = 0;
-            break;
-        case BT_CR:
-        case BT_LF:
-            /* This case ensures that the first attribute name is counted
-               Apart from that we could just change state on the quote. */
-            if (state == inName)
-                state = other;
-            else if (state == inValue && nAtts < attsMax)
-                atts[nAtts].normalized = 0;
-            break;
-        case BT_GT:
-        case BT_SOL:
-            if (state != inValue)
-                return nAtts;
-            break;
-        default:
-            break;
-        }
-    }
-    /* not reached */
-}
-
-static int PTRFASTCALL PREFIX (nameLength) (const ENCODING * enc, const char *ptr) {
-    const char *start = ptr;
-    for (;;) {
-        switch (BYTE_TYPE (enc, ptr)) {
-#define LEAD_CASE(n) \
-    case BT_LEAD ## n: ptr += n; break;
-            LEAD_CASE (2) LEAD_CASE (3) LEAD_CASE (4)
-#undef LEAD_CASE
-        case BT_NONASCII:
-        case BT_NMSTRT:
-#ifdef XML_NS
-        case BT_COLON:
-#endif
-        case BT_HEX:
-        case BT_DIGIT:
-        case BT_NAME:
-        case BT_MINUS:
-            ptr += MINBPC (enc);
-            break;
-        default:
-            return (int) (ptr - start);
-        }
-    }
+static void
+rsvg_xml_noerror (void *data, xmlErrorPtr error)
+{
 }
 
+/* This is quite hacky and not entirely correct, but apparently 
+ * libxml2 has NO support for parsing pseudo attributes as defined 
+ * by the xml-styleheet spec.
+ */
 char **
 rsvg_css_parse_xml_attribute_string (const char *attribute_string)
 {
-    int i, nb_atts;
-    ATTRIBUTE *attributes;
-    int attrSize = 16;
-    ENCODING *enc = NULL;
-    char **atts;
-    char *_attribute_string;
-
-    _attribute_string = g_strdup_printf ("<tag %s />\n", attribute_string);
-    attributes = g_new (ATTRIBUTE, attrSize);
-
-    nb_atts = getAtts (enc, _attribute_string, attrSize, attributes);
-    if (nb_atts > attrSize) {
-        attrSize = nb_atts;
-        g_free (attributes);
-
-        attributes = g_new (ATTRIBUTE, attrSize);
-        nb_atts = getAtts (enc, _attribute_string, attrSize, attributes);
+    xmlSAXHandler handler;
+    xmlParserCtxtPtr parser;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    xmlAttrPtr attr;
+    char *tag;
+    GPtrArray *attributes;
+    char **retval = NULL;
+
+    tag = g_strdup_printf ("<rsvg-hack %s />\n", attribute_string);
+
+    memset (&handler, 0, sizeof (handler));
+    xmlSAX2InitDefaultSAXHandler (&handler, 0);
+    handler.serror = rsvg_xml_noerror;
+    parser = xmlCreatePushParserCtxt (&handler, NULL, tag, strlen (tag) + 1, NULL);
+    parser->options |= XML_PARSE_NONET;
+
+    if (xmlParseDocument (parser) != 0)
+        goto done;
+
+    if ((doc = parser->myDoc) == NULL ||
+        (node = doc->children) == NULL ||
+        strcmp ((const char *) node->name, "rsvg-hack") != 0 ||
+        node->next != NULL ||
+        node->properties == NULL)
+          goto done;
+
+    attributes = g_ptr_array_new ();
+    for (attr = node->properties; attr; attr = attr->next) {
+        xmlNodePtr content = attr->children;
+
+        g_ptr_array_add (attributes, g_strdup ((char *) attr->name));
+        if (content)
+          g_ptr_array_add (attributes, g_strdup ((char *) content->content));
+        else
+          g_ptr_array_add (attributes, g_strdup (""));
     }
 
-    atts = g_new0 (char *, ((2 * nb_atts) + 1));
-    for (i = 0; i < nb_atts; i++) {
-        atts[(2 * i)] = g_strdup (attributes[i].name);
-        atts[(2 * i)][nameLength (enc, attributes[i].name)] = '\0';
-        atts[(2 * i) + 1] = g_strdup (attributes[i].valuePtr);
-        atts[(2 * i) + 1][attributes[i].valueEnd - attributes[i].valuePtr] = '\0';
-    }
+    g_ptr_array_add (attributes, NULL);
+    retval = (char **) g_ptr_array_free (attributes, FALSE);
 
-    g_free (attributes);
-    g_free (_attribute_string);
+  done:
+    if (parser->myDoc)
+      xmlFreeDoc (parser->myDoc);
+    xmlFreeParserCtxt (parser);
+    g_free (tag);
 
-    return atts;
+    return retval;
 }
diff --git a/rsvg-css.h b/rsvg-css.h
index 6fa2561..6abeba9 100644
--- a/rsvg-css.h
+++ b/rsvg-css.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-css.h : CSS utility functions
 
@@ -26,8 +27,11 @@
 #define RSVG_CSS_H
 
 #include <glib.h>
+
+#ifdef RSVG_COMPILATION
 #include <pango/pango.h>
 #include "rsvg-private.h"
+#endif
 
 G_BEGIN_DECLS
 
@@ -43,31 +47,45 @@ G_BEGIN_DECLS
 #define RSVG_ASPECT_RATIO_XMAX_YMAX (1 << 8)
 #define RSVG_ASPECT_RATIO_SLICE (1 << 31)
 
-int	    rsvg_css_parse_aspect_ratio	    (const char *str);
-
-gboolean    rsvg_css_param_match	    (const char *str, const char *param_name);
-int	    rsvg_css_param_arg_offset	    (const char *str);
-
-guint32	    rsvg_css_parse_color	    (const char *str, gboolean * inherit);
-guint	    rsvg_css_parse_opacity	    (const char *str);
-double	    rsvg_css_parse_angle	    (const char *str);
-double	    rsvg_css_parse_frequency	    (const char *str);
-double	    rsvg_css_parse_time		    (const char *str);
+/* This one is semi-public for mis-use in rsvg-convert */
+guint32	    rsvg_css_parse_color        (const char *str, gboolean * inherit);
 
-PangoStyle	 rsvg_css_parse_font_style	(const char *str, gboolean * inherit);
-PangoVariant	 rsvg_css_parse_font_variant	(const char *str, gboolean * inherit);
-PangoWeight	 rsvg_css_parse_font_weight	(const char *str, gboolean * inherit);
-PangoStretch	 rsvg_css_parse_font_stretch	(const char *str, gboolean * inherit);
-const char	*rsvg_css_parse_font_family	(const char *str, gboolean * inherit);
+#ifdef RSVG_COMPILATION
 
-RsvgViewBox	  rsvg_css_parse_vbox			(const char *vbox);
-void		  rsvg_css_parse_number_optional_number	(const char *str, double *x, double *y);
-gchar		**rsvg_css_parse_list			(const char *in_str, guint * out_list_len);
-gdouble		 *rsvg_css_parse_number_list		(const char *in_str, guint * out_list_len);
-
-gboolean	  rsvg_css_parse_overflow		(const char *str, gboolean * inherit);
-
-char		**rsvg_css_parse_xml_attribute_string	(const char *attribute_string);
+G_GNUC_INTERNAL
+int	    rsvg_css_parse_aspect_ratio	    (const char *str);
+G_GNUC_INTERNAL
+guint       rsvg_css_parse_opacity	    (const char *str);
+G_GNUC_INTERNAL
+double      rsvg_css_parse_angle        (const char *str);
+G_GNUC_INTERNAL
+double      rsvg_css_parse_frequency    (const char *str);
+G_GNUC_INTERNAL
+double      rsvg_css_parse_time         (const char *str);
+G_GNUC_INTERNAL
+PangoStyle   rsvg_css_parse_font_style      (const char *str, gboolean * inherit);
+G_GNUC_INTERNAL
+PangoVariant rsvg_css_parse_font_variant    (const char *str, gboolean * inherit);
+G_GNUC_INTERNAL
+PangoWeight	 rsvg_css_parse_font_weight	    (const char *str, gboolean * inherit);
+G_GNUC_INTERNAL
+PangoStretch rsvg_css_parse_font_stretch    (const char *str, gboolean * inherit);
+G_GNUC_INTERNAL
+const char  *rsvg_css_parse_font_family	    (const char *str, gboolean * inherit);
+G_GNUC_INTERNAL
+RsvgViewBox	  rsvg_css_parse_vbox           (const char *vbox);
+G_GNUC_INTERNAL
+void          rsvg_css_parse_number_optional_number	(const char *str, double *x, double *y);
+G_GNUC_INTERNAL
+gchar       **rsvg_css_parse_list           (const char *in_str, guint * out_list_len);
+G_GNUC_INTERNAL
+gdouble	     *rsvg_css_parse_number_list    (const char *in_str, guint * out_list_len);
+G_GNUC_INTERNAL
+gboolean      rsvg_css_parse_overflow       (const char *str, gboolean * inherit);
+G_GNUC_INTERNAL
+char        **rsvg_css_parse_xml_attribute_string   (const char *attribute_string);
+
+#endif /* RSVG_COMPILATION */
 
 G_END_DECLS
 
diff --git a/rsvg-defs.c b/rsvg-defs.c
index 581917e..d2c6a06 100644
--- a/rsvg-defs.c
+++ b/rsvg-defs.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg-defs.c: Manage SVG defs and references.
  
@@ -26,7 +27,7 @@
 #include "rsvg-private.h"
 #include "rsvg-defs.h"
 #include "rsvg-styles.h"
-#include "rsvg-image.h"
+#include "rsvg-io.h"
 
 #include <glib.h>
 
@@ -34,19 +35,11 @@ struct _RsvgDefs {
     GHashTable *hash;
     GPtrArray *unnamed;
     GHashTable *externs;
-    gchar *base_uri;
-    GSList *toresolve;
-};
-
-typedef struct _RsvgResolutionPending RsvgResolutionPending;
-
-struct _RsvgResolutionPending {
-    RsvgNode **tochange;
-    char *name;
+    RsvgHandle *ctx;
 };
 
 RsvgDefs *
-rsvg_defs_new (void)
+rsvg_defs_new (RsvgHandle *handle)
 {
     RsvgDefs *result = g_new (RsvgDefs, 1);
 
@@ -54,42 +47,38 @@ rsvg_defs_new (void)
     result->externs =
         g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref);
     result->unnamed = g_ptr_array_new ();
-    result->base_uri = NULL;
-    result->toresolve = NULL;
+    result->ctx = handle; /* no need to take a ref here */
 
     return result;
 }
 
-void
-rsvg_defs_set_base_uri (RsvgDefs * self, gchar * base_uri)
-{
-    self->base_uri = base_uri;
-}
-
 static int
 rsvg_defs_load_extern (const RsvgDefs * defs, const char *name)
 {
     RsvgHandle *handle;
     gchar *filename, *base_uri;
-    GByteArray *chars;
+    char *data;
+    gsize data_len;
+    gboolean rv;
 
-    filename = rsvg_get_file_path (name, defs->base_uri);
+    filename = _rsvg_io_get_file_path (name, rsvg_handle_get_base_uri (defs->ctx));
 
-    chars = _rsvg_acquire_xlink_href_resource (name, defs->base_uri, NULL);
+    data = _rsvg_handle_acquire_data (defs->ctx, name, NULL, &data_len, NULL);
 
-    if (chars) {
+    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);
 
-        if (rsvg_handle_write (handle, chars->data, chars->len, NULL) &&
-            rsvg_handle_close (handle, NULL)) {
+        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);
         }
 
-        g_byte_array_free (chars, TRUE);
+        g_free (data);
     }
 
     g_free (filename);
@@ -108,7 +97,7 @@ rsvg_defs_extern_lookup (const RsvgDefs * defs, const char *filename, const char
     }
 
     if (file != NULL)
-        return (RsvgNode *) g_hash_table_lookup (file->priv->defs->hash, name);
+        return g_hash_table_lookup (file->priv->defs->hash, name);
     else
         return NULL;
 }
@@ -122,7 +111,7 @@ rsvg_defs_lookup (const RsvgDefs * defs, const char *name)
         return NULL;
     }
     if (hashpos == name) {
-        return (RsvgNode *) g_hash_table_lookup (defs->hash, name + 1);
+        return g_hash_table_lookup (defs->hash, name + 1);
     } else {
         gchar **splitbits;
         RsvgNode *toreturn;
@@ -146,6 +135,9 @@ rsvg_defs_set (RsvgDefs * defs, const char *name, RsvgNode * val)
 void
 rsvg_defs_register_name (RsvgDefs * defs, const char *name, RsvgNode * val)
 {
+    if (g_hash_table_lookup (defs->hash, name))
+        return;
+
     g_hash_table_insert (defs->hash, g_strdup (name), val);
 }
 
@@ -172,26 +164,3 @@ rsvg_defs_free (RsvgDefs * defs)
     g_free (defs);
 }
 
-void
-rsvg_defs_add_resolver (RsvgDefs * defs, RsvgNode ** tochange, const gchar * name)
-{
-    RsvgResolutionPending *data;
-    data = g_new (RsvgResolutionPending, 1);
-    data->tochange = tochange;
-    data->name = g_strdup (name);
-    defs->toresolve = g_slist_prepend (defs->toresolve, data);
-}
-
-void
-rsvg_defs_resolve_all (RsvgDefs * defs)
-{
-    while (defs->toresolve) {
-        RsvgResolutionPending *data;
-        data = defs->toresolve->data;
-        *(data->tochange) = rsvg_defs_lookup (defs, data->name);
-        g_free (data->name);
-        g_free (data);
-        defs->toresolve = g_slist_delete_link (defs->toresolve, defs->toresolve);
-
-    }
-}
diff --git a/rsvg-defs.h b/rsvg-defs.h
index cb9b53e..3d6802f 100644
--- a/rsvg-defs.h
+++ b/rsvg-defs.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-defs.h : SVG defs utilities
 
@@ -30,17 +31,22 @@
 
 #include <glib.h>
 
+#include "rsvg.h"
+
 G_BEGIN_DECLS 
 
-RsvgDefs    *rsvg_defs_new		(void);
+G_GNUC_INTERNAL
+RsvgDefs    *rsvg_defs_new		(RsvgHandle *handle);
+/* for some reason this one's public... */
 RsvgNode    *rsvg_defs_lookup		(const RsvgDefs * defs, const char *name);
+G_GNUC_INTERNAL
 void	     rsvg_defs_set		(RsvgDefs * defs, const char *name, RsvgNode * val);
+G_GNUC_INTERNAL
 void	     rsvg_defs_free		(RsvgDefs * defs);
-void	     rsvg_defs_set_base_uri	(RsvgDefs * self, gchar * base_uri);
-void	     rsvg_defs_add_resolver	(RsvgDefs * defs, RsvgNode ** tochange, const gchar * name);
-void	     rsvg_defs_resolve_all	(RsvgDefs * defs);
+G_GNUC_INTERNAL
 void	     rsvg_defs_register_name	(RsvgDefs * defs, const char *name, RsvgNode * val);
-void	     rsvg_defs_register_memory	(RsvgDefs * defs, RsvgNode * val);
+G_GNUC_INTERNAL
+void	     rsvg_defs_register_memory  (RsvgDefs * defs, RsvgNode * val);
 
 G_END_DECLS
 #endif
diff --git a/rsvg-dimensions.c b/rsvg-dimensions.c
deleted file mode 100644
index f8f0283..0000000
--- a/rsvg-dimensions.c
+++ /dev/null
@@ -1,133 +0,0 @@
-
-/*
- * License: Public Domain.
- * Author: Robert Staudinger <robsta@gnome.org>.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <rsvg.h>
-
-static void
-show_help (GOptionContext *context)
-{
-	char *help;
-	help = g_option_context_get_help (context, TRUE, NULL);
-	perror (help);
-	g_free (help), help = NULL;
-}
-
-int
-main (int	  argc,
-      char	**argv)
-{
-	GOptionContext		 *context;
-	char const		 *fragment;
-	char const	 	**filenames;
-	char const		 *file;
-	RsvgHandle		 *handle;
-	RsvgDimensionData	  dimensions;
-	RsvgPositionData	  position;
-	GError			 *error;
-	int			  exit_code;
-	int                       i;
-
-	GOptionEntry options[] = {
-		{ "fragment", 'f', 0, G_OPTION_ARG_STRING, &fragment, "The SVG fragment to address.", "<string>" },
-		{ G_OPTION_REMAINING, 0, G_OPTION_FLAG_FILENAME, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, "[FILE...]" },
-		{ NULL }
-	};
-
-	rsvg_init ();
-
-	context = NULL;
-	fragment = NULL;
-	filenames = NULL;
-	handle = NULL;
-	error = NULL;
-
-	context = g_option_context_new ("- SVG measuring tool.");
-	g_option_context_add_main_entries (context, options, NULL);
-
-	/* No args? */
-	if (argc < 2) {
-		show_help (context);
-		exit_code = EXIT_SUCCESS;
-		goto bail;
-	}
-
-	error = NULL;
-	g_option_context_parse (context, &argc, &argv, &error);
-	if (error) {
-		show_help (context);
-		g_warning ("%s", error->message);
-		exit_code = EXIT_FAILURE;
-		goto bail;
-	}
-
-	/* Invalid / missing args? */
-	if (filenames == NULL) {
-		show_help (context);
-		exit_code = EXIT_FAILURE;
-		goto bail;
-	}
-
-	g_option_context_free (context), context = NULL;
-
-	for (i = 0; NULL != (file = filenames[i]); i++) {
-
-		error = NULL;
-		handle = rsvg_handle_new_from_file (file, &error);
-		if (error) {
-			g_warning ("%s", error->message);
-			exit_code = EXIT_FAILURE;
-			goto bail;
-		}
-
-		if (fragment && handle) {
-			gboolean have_fragment = FALSE;
-			have_fragment |= rsvg_handle_get_dimensions_sub (handle,
-						&dimensions, fragment);
-			have_fragment |= rsvg_handle_get_position_sub (handle,
-						&position, fragment);
-			if (!have_fragment) {
-				g_warning ("%s: fragment `'%s' not found.",
-						file, fragment);
-				exit_code = EXIT_FAILURE;
-				goto bail;
-			}
-
-			printf ("%s, fragment `%s': x=%d, y=%d, %dx%d, em=%f, ex=%f\n",
-					file, fragment,
-					position.x, position.y,
-					dimensions.width, dimensions.height,
-					dimensions.em, dimensions.ex);
-
-		} else if (handle) {
-			rsvg_handle_get_dimensions (handle, &dimensions);
-			printf ("%s: %dx%d, em=%f, ex=%f\n", file,
-					dimensions.width, dimensions.height,
-					dimensions.em, dimensions.ex);
-		} else {
-			g_warning ("Could not open file `%s'", file);
-			exit_code = EXIT_FAILURE;
-			goto bail;
-		}
-
-		g_object_unref (G_OBJECT (handle)), handle = NULL;
-	}
-
-	exit_code = EXIT_SUCCESS;
-
-bail:
-	if (handle)
-		g_object_unref (G_OBJECT (handle)), handle = NULL;
-	if (context)
-		g_option_context_free (context), context = NULL;
-	if (error)
-		g_error_free (error), error = NULL;
-	rsvg_term ();
-	return exit_code;
-}
-
diff --git a/rsvg-file-util.c b/rsvg-file-util.c
index 3adde39..e5f058d 100644
--- a/rsvg-file-util.c
+++ b/rsvg-file-util.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-file-util.c: SAX-based renderer for SVG files into a GdkPixbuf.
 
@@ -23,99 +24,32 @@
    Author: Raph Levien <raph@artofcode.com>
 */
 
+/**
+ * SECTION: rsvg-pixbuf
+ * @short_description: How to render SVGs into GdkPixbufs, for easy use in GTK+
+ *  applications
+ *
+ * GdkPixbuf is a library for image loading and manipulation. It is part of the
+ * cross-platform GTK+ widget toolkit.
+ */
+
 #include "config.h"
 #include "rsvg.h"
 #include "rsvg-private.h"
+#include "rsvg-io.h"
+#include "rsvg-size-callback.h"
 
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <math.h>
 
 #define SVG_BUFFER_SIZE (1024 * 8)
 
-void
-_rsvg_size_callback (int *width, int *height, gpointer data)
-{
-    struct RsvgSizeCallbackData *real_data = (struct RsvgSizeCallbackData *) data;
-    double zoomx, zoomy, zoom;
-
-    int in_width, in_height;
-
-    in_width = *width;
-    in_height = *height;
-
-    switch (real_data->type) {
-    case RSVG_SIZE_ZOOM:
-        if (*width < 0 || *height < 0)
-            return;
-
-        *width = floor (real_data->x_zoom * *width + 0.5);
-        *height = floor (real_data->y_zoom * *height + 0.5);
-        break;
-
-    case RSVG_SIZE_ZOOM_MAX:
-        if (*width < 0 || *height < 0)
-            return;
-
-        *width = floor (real_data->x_zoom * *width + 0.5);
-        *height = floor (real_data->y_zoom * *height + 0.5);
-
-        if (*width > real_data->width || *height > real_data->height) {
-            zoomx = (double) real_data->width / *width;
-            zoomy = (double) real_data->height / *height;
-            zoom = MIN (zoomx, zoomy);
-
-            *width = floor (zoom * *width + 0.5);
-            *height = floor (zoom * *height + 0.5);
-        }
-        break;
-
-    case RSVG_SIZE_WH_MAX:
-        if (*width < 0 || *height < 0)
-            return;
-
-        zoomx = (double) real_data->width / *width;
-        zoomy = (double) real_data->height / *height;
-        if (zoomx < 0)
-            zoom = zoomy;
-        else if (zoomy < 0)
-            zoom = zoomx;
-        else
-            zoom = MIN (zoomx, zoomy);
-
-        *width = floor (zoom * *width + 0.5);
-        *height = floor (zoom * *height + 0.5);
-        break;
-
-    case RSVG_SIZE_WH:
-
-        if (real_data->width != -1)
-            *width = real_data->width;
-        if (real_data->height != -1)
-            *height = real_data->height;
-        break;
-
-    default:
-        g_assert_not_reached ();
-    }
-
-    if (real_data->keep_aspect_ratio) {
-        int out_min = MIN (*width, *height);
-
-        if (out_min == *width) {
-            *height = in_height * ((double) *width / (double) in_width);
-        } else {
-            *width = in_width * ((double) *height / (double) in_height);
-        }
-    }
-}
-
 /* private */
 GdkPixbuf *
 rsvg_pixbuf_from_data_with_size_data (const guchar * buff,
                                       size_t len,
-                                      struct RsvgSizeCallbackData *data,
+                                      /* RsvgSizeCallbackData */ gpointer data,
                                       const char *base_uri, GError ** error)
 {
     RsvgHandle *handle;
@@ -132,25 +66,28 @@ rsvg_pixbuf_from_data_with_size_data (const guchar * buff,
     rsvg_handle_set_base_uri (handle, base_uri);
 
     if (!rsvg_handle_write (handle, buff, len, error)) {
-        g_object_unref (G_OBJECT (handle));
+        (void) rsvg_handle_close (handle, NULL);
+        g_object_unref (handle);
         return NULL;
     }
 
     if (!rsvg_handle_close (handle, error)) {
-        g_object_unref (G_OBJECT (handle));
+        g_object_unref (handle);
         return NULL;
     }
 
     retval = rsvg_handle_get_pixbuf (handle);
-    g_object_unref (G_OBJECT (handle));
+    g_object_unref (handle);
 
     return retval;
 }
 
 static GdkPixbuf *
-rsvg_pixbuf_from_stdio_file_with_size_data (GByteArray * f,
-                                            struct RsvgSizeCallbackData *data,
-                                            gchar * base_uri, GError ** error)
+rsvg_pixbuf_from_stdio_file_with_size_data (const char *data,
+                                            gsize data_len,
+                                            struct RsvgSizeCallbackData *cb_data,
+                                            gchar * base_uri, 
+                                            GError ** error)
 {
     RsvgHandle *handle;
     GdkPixbuf *retval;
@@ -162,38 +99,42 @@ rsvg_pixbuf_from_stdio_file_with_size_data (GByteArray * f,
         return NULL;
     }
 
-    rsvg_handle_set_size_callback (handle, _rsvg_size_callback, data, NULL);
+    rsvg_handle_set_size_callback (handle, _rsvg_size_callback, cb_data, NULL);
     rsvg_handle_set_base_uri (handle, base_uri);
 
-    if (!rsvg_handle_write (handle, f->data, f->len, error)) {
-        g_object_unref (G_OBJECT (handle));
+    if (!rsvg_handle_write (handle, (guchar *) data, data_len, error)) {
+        (void) rsvg_handle_close (handle, NULL);
+        g_object_unref (handle);
         return NULL;
     }
 
     if (!rsvg_handle_close (handle, error)) {
-        g_object_unref (G_OBJECT (handle));
+        g_object_unref (handle);
         return NULL;
     }
 
     retval = rsvg_handle_get_pixbuf (handle);
-    g_object_unref (G_OBJECT (handle));
+    g_object_unref (handle);
 
     return retval;
 }
 
 static GdkPixbuf *
 rsvg_pixbuf_from_file_with_size_data (const gchar * file_name,
-                                      struct RsvgSizeCallbackData *data, GError ** error)
+                                      struct RsvgSizeCallbackData *cb_data, 
+                                      GError ** error)
 {
     GdkPixbuf *pixbuf;
-    GByteArray *f;
+    char *data;
+    gsize data_len;
     GString *base_uri = g_string_new (file_name);
 
-    f = _rsvg_acquire_xlink_href_resource (file_name, base_uri->str, error);
+    data = _rsvg_io_acquire_data (file_name, base_uri->str, NULL, &data_len, NULL, error);
 
-    if (f) {
-        pixbuf = rsvg_pixbuf_from_stdio_file_with_size_data (f, data, base_uri->str, error);
-        g_byte_array_free (f, TRUE);
+    if (data) {
+        pixbuf = rsvg_pixbuf_from_stdio_file_with_size_data (data, data_len,
+                                                             cb_data, base_uri->str, error);
+        g_free (data);
     } else {
         pixbuf = NULL;
     }
diff --git a/rsvg-filter.c b/rsvg-filter.c
index 705f14c..81cce75 100644
--- a/rsvg-filter.c
+++ b/rsvg-filter.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg-filter.c: Provides filters
  
@@ -29,6 +30,8 @@
 #include "rsvg-styles.h"
 #include "rsvg-image.h"
 #include "rsvg-css.h"
+#include "rsvg-cairo-render.h"
+
 #include <string.h>
 
 #include <math.h>
@@ -40,12 +43,8 @@
 typedef struct _RsvgFilterPrimitiveOutput RsvgFilterPrimitiveOutput;
 
 struct _RsvgFilterPrimitiveOutput {
-    GdkPixbuf *result;
+    cairo_surface_t *surface;
     RsvgIRect bounds;
-    gboolean Rused;
-    gboolean Gused;
-    gboolean Bused;
-    gboolean Aused;
 };
 
 typedef struct _RsvgFilterContext RsvgFilterContext;
@@ -54,17 +53,45 @@ struct _RsvgFilterContext {
     gint width, height;
     RsvgFilter *filter;
     GHashTable *results;
-    GdkPixbuf *source;
-    GdkPixbuf *bg;
+    cairo_surface_t *source_surface;
+    cairo_surface_t *bg_surface;
     RsvgFilterPrimitiveOutput lastresult;
-    double affine[6];
-    double paffine[6];
+    cairo_matrix_t affine;
+    cairo_matrix_t paffine;
     int channelmap[4];
     RsvgDrawingCtx *ctx;
 };
 
 typedef struct _RsvgFilterPrimitive RsvgFilterPrimitive;
 
+/* We don't have real subclassing here.  If you derive something from
+ * RsvgFilterPrimitive, and don't need any special code to free your
+ * RsvgFilterPrimitiveFoo structure, you can just set
+ *
+ *    foo_filter->super.super.free = rsvg_filter_primitive_free;
+ *
+ * for the destructor.  Otherwise, create a custom destructor like this:
+ *
+ *    foo_filter->super.super.free = &rsvg_filter_primitive_foo_free;
+ *
+ *    ...
+ *
+ *    static void
+ *    rsvg_filter_primitive_foo_free (RsvgNode *self)
+ *    {
+ *        RsvgFilterPrimitiveFoo *foo;
+ *
+ *        foo = (RsvgFilterPrimitiveFoo *) self;
+ *        g_free (foo->my_custom_stuff);
+ *        g_free (foo->more_custom_stuff);
+ *        ... etc ...
+ *
+ *        rsvg_filter_primitive_free (self);
+ *    }
+ *
+ * That last call to rsvg_filter_primitive_free() will free the base RsvgFilterPrimitive's own fields,
+ * and your whole structure itself, via _rsvg_node_free().
+ */
 struct _RsvgFilterPrimitive {
     RsvgNode super;
     RsvgLength x, y, width, height;
@@ -78,6 +105,18 @@ struct _RsvgFilterPrimitive {
 /*************************************************************/
 
 static void
+rsvg_filter_primitive_free (RsvgNode *self)
+{
+    RsvgFilterPrimitive *primitive;
+
+    primitive = (RsvgFilterPrimitive *) self;
+    g_string_free (primitive->in, TRUE);
+    g_string_free (primitive->result, TRUE);
+
+    _rsvg_node_free (self);
+}
+
+static void
 rsvg_filter_primitive_render (RsvgFilterPrimitive * self, RsvgFilterContext * ctx)
 {
     self->render (self, ctx);
@@ -87,18 +126,18 @@ static RsvgIRect
 rsvg_filter_primitive_get_bounds (RsvgFilterPrimitive * self, RsvgFilterContext * ctx)
 {
     RsvgBbox box, otherbox;
-    double affine[6];
+    cairo_matrix_t affine;
 
-    _rsvg_affine_identity (affine);
-    rsvg_bbox_init (&box, affine);
-    rsvg_bbox_init (&otherbox, ctx->affine);
+    cairo_matrix_init_identity (&affine);
+    rsvg_bbox_init (&box, &affine);
+    rsvg_bbox_init (&otherbox, &ctx->affine);
     otherbox.virgin = 0;
     if (ctx->filter->filterunits == objectBoundingBox)
         _rsvg_push_view_box (ctx->ctx, 1., 1.);
-    otherbox.x = _rsvg_css_normalize_length (&ctx->filter->x, ctx->ctx, 'h');
-    otherbox.y = _rsvg_css_normalize_length (&ctx->filter->y, ctx->ctx, 'v');
-    otherbox.w = _rsvg_css_normalize_length (&ctx->filter->width, ctx->ctx, 'h');
-    otherbox.h = _rsvg_css_normalize_length (&ctx->filter->height, ctx->ctx, 'v');
+    otherbox.rect.x = _rsvg_css_normalize_length (&ctx->filter->x, ctx->ctx, 'h');
+    otherbox.rect.y = _rsvg_css_normalize_length (&ctx->filter->y, ctx->ctx, 'v');
+    otherbox.rect.width = _rsvg_css_normalize_length (&ctx->filter->width, ctx->ctx, 'h');
+    otherbox.rect.height = _rsvg_css_normalize_length (&ctx->filter->height, ctx->ctx, 'v');
     if (ctx->filter->filterunits == objectBoundingBox)
         _rsvg_pop_view_box (ctx->ctx);
 
@@ -108,68 +147,69 @@ rsvg_filter_primitive_get_bounds (RsvgFilterPrimitive * self, RsvgFilterContext
         if (self->x.factor != 'n' || self->y.factor != 'n' ||
             self->width.factor != 'n' || self->height.factor != 'n') {
 
-            rsvg_bbox_init (&otherbox, ctx->paffine);
+            rsvg_bbox_init (&otherbox, &ctx->paffine);
             otherbox.virgin = 0;
             if (ctx->filter->primitiveunits == objectBoundingBox)
                 _rsvg_push_view_box (ctx->ctx, 1., 1.);
             if (self->x.factor != 'n')
-                otherbox.x = _rsvg_css_normalize_length (&self->x, ctx->ctx, 'h');
+                otherbox.rect.x = _rsvg_css_normalize_length (&self->x, ctx->ctx, 'h');
             else
-                otherbox.x = 0;
+                otherbox.rect.x = 0;
             if (self->y.factor != 'n')
-                otherbox.y = _rsvg_css_normalize_length (&self->y, ctx->ctx, 'v');
+                otherbox.rect.y = _rsvg_css_normalize_length (&self->y, ctx->ctx, 'v');
             else
-                otherbox.y = 0;
+                otherbox.rect.y = 0;
             if (self->width.factor != 'n')
-                otherbox.w = _rsvg_css_normalize_length (&self->width, ctx->ctx, 'h');
+                otherbox.rect.width = _rsvg_css_normalize_length (&self->width, ctx->ctx, 'h');
             else
-                otherbox.w = ctx->ctx->vb.w;
+                otherbox.rect.width = ctx->ctx->vb.rect.width;
             if (self->height.factor != 'n')
-                otherbox.h = _rsvg_css_normalize_length (&self->height, ctx->ctx, 'v');
+                otherbox.rect.height = _rsvg_css_normalize_length (&self->height, ctx->ctx, 'v');
             else
-                otherbox.h = ctx->ctx->vb.h;
+                otherbox.rect.height = ctx->ctx->vb.rect.height;
             if (ctx->filter->primitiveunits == objectBoundingBox)
                 _rsvg_pop_view_box (ctx->ctx);
             rsvg_bbox_clip (&box, &otherbox);
         }
 
-    rsvg_bbox_init (&otherbox, affine);
+    rsvg_bbox_init (&otherbox, &affine);
     otherbox.virgin = 0;
-    otherbox.x = 0;
-    otherbox.y = 0;
-    otherbox.w = ctx->width;
-    otherbox.h = ctx->height;
+    otherbox.rect.x = 0;
+    otherbox.rect.y = 0;
+    otherbox.rect.width = ctx->width;
+    otherbox.rect.height = ctx->height;
     rsvg_bbox_clip (&box, &otherbox);
     {
-        RsvgIRect output = { box.x, box.y,
-            box.x + box.w,
-            box.y + box.h
+        RsvgIRect output = { box.rect.x, box.rect.y,
+            box.rect.x + box.rect.width,
+            box.rect.y + box.rect.height
         };
         return output;
     }
 }
 
-GdkPixbuf *
-_rsvg_pixbuf_new_cleared (GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample,
-                          int width, int height)
+static cairo_surface_t *
+_rsvg_image_surface_new (int width, int height)
 {
-    GdkPixbuf *pb;
-    guchar *data;
+    cairo_surface_t *surface;
 
-    pb = gdk_pixbuf_new (colorspace, has_alpha, bits_per_sample, width, height);
-    data = gdk_pixbuf_get_pixels (pb);
-    memset (data, 0, width * height * 4);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+      cairo_surface_destroy (surface);
+      return NULL;
+    }
 
-    return pb;
+    return surface;
 }
 
 static guchar
-gdk_pixbuf_get_interp_pixel (guchar * src, gdouble ox, gdouble oy, guchar ch, RsvgIRect boundarys,
+get_interp_pixel (guchar * src, gdouble ox, gdouble oy, guchar ch, RsvgIRect boundarys,
                              guint rowstride)
 {
     double xmod, ymod;
     double dist1, dist2, dist3, dist4;
     double c, c1, c2, c3, c4;
+    double fox, foy, cox, coy;
 
     xmod = fmod (ox, 1.0);
     ymod = fmod (oy, 1.0);
@@ -179,29 +219,34 @@ gdk_pixbuf_get_interp_pixel (guchar * src, gdouble ox, gdouble oy, guchar ch, Rs
     dist3 = (xmod) * (ymod);
     dist4 = (1 - xmod) * (ymod);
 
-    if (floor (ox) <= boundarys.x0 || floor (ox) >= boundarys.x1 ||
-        floor (oy) <= boundarys.y0 || floor (oy) >= boundarys.y1)
+    fox = floor (ox);
+    foy = floor (oy);
+    cox = ceil (ox);
+    coy = ceil (oy);
+
+    if (fox <= boundarys.x0 || fox >= boundarys.x1 ||
+        foy <= boundarys.y0 || foy >= boundarys.y1)
         c1 = 0;
     else
-        c1 = src[(guint) floor (oy) * rowstride + (guint) floor (ox) * 4 + ch];
+        c1 = src[(guint) foy * rowstride + (guint) fox * 4 + ch];
 
-    if (ceil (ox) <= boundarys.x0 || ceil (ox) >= boundarys.x1 ||
-        floor (oy) <= boundarys.y0 || floor (oy) >= boundarys.y1)
+    if (cox <= boundarys.x0 || cox >= boundarys.x1 ||
+        foy <= boundarys.y0 || foy >= boundarys.y1)
         c2 = 0;
     else
-        c2 = src[(guint) floor (oy) * rowstride + (guint) ceil (ox) * 4 + ch];
+        c2 = src[(guint) foy * rowstride + (guint) cox * 4 + ch];
 
-    if (ceil (ox) <= boundarys.x0 || ceil (ox) >= boundarys.x1 ||
-        ceil (oy) <= boundarys.y0 || ceil (oy) >= boundarys.y1)
+    if (cox <= boundarys.x0 || cox >= boundarys.x1 ||
+        coy <= boundarys.y0 || coy >= boundarys.y1)
         c3 = 0;
     else
-        c3 = src[(guint) ceil (oy) * rowstride + (guint) ceil (ox) * 4 + ch];
+        c3 = src[(guint) coy * rowstride + (guint) cox * 4 + ch];
 
-    if (floor (ox) <= boundarys.x0 || floor (ox) >= boundarys.x1 ||
-        ceil (oy) <= boundarys.y0 || ceil (oy) >= boundarys.y1)
+    if (fox <= boundarys.x0 || fox >= boundarys.x1 ||
+        coy <= boundarys.y0 || coy >= boundarys.y1)
         c4 = 0;
     else
-        c4 = src[(guint) ceil (oy) * rowstride + (guint) floor (ox) * 4 + ch];
+        c4 = src[(guint) coy * rowstride + (guint) fox * 4 + ch];
 
     c = (c1 * dist1 + c2 * dist2 + c3 * dist3 + c4 * dist4) / (dist1 + dist2 + dist3 + dist4);
 
@@ -209,116 +254,110 @@ gdk_pixbuf_get_interp_pixel (guchar * src, gdouble ox, gdouble oy, guchar ch, Rs
 }
 
 static void
-rsvg_filter_fix_coordinate_system (RsvgFilterContext * ctx, RsvgState * state, RsvgBbox bbox)
+rsvg_filter_fix_coordinate_system (RsvgFilterContext * ctx, RsvgState * state, RsvgBbox *bbox)
 {
     int x, y, height, width;
-    int i;
 
-    x = bbox.x;
-    y = bbox.y;
-    width = bbox.w;
-    height = bbox.h;
+    x = bbox->rect.x;
+    y = bbox->rect.y;
+    width = bbox->rect.width;
+    height = bbox->rect.height;
 
-    ctx->width = gdk_pixbuf_get_width (ctx->source);
-    ctx->height = gdk_pixbuf_get_height (ctx->source);
+    ctx->width = cairo_image_surface_get_width (ctx->source_surface);
+    ctx->height = cairo_image_surface_get_height (ctx->source_surface);
 
-    for (i = 0; i < 6; i++)
-        ctx->affine[i] = state->affine[i];
+    ctx->affine = state->affine;
     if (ctx->filter->filterunits == objectBoundingBox) {
-        double affine[6] = { width, 0, 0, height, x, y };
-        _rsvg_affine_multiply (ctx->affine, affine, ctx->affine);
+        cairo_matrix_t affine;
+        cairo_matrix_init (&affine, width, 0, 0, height, x, y);
+        cairo_matrix_multiply (&ctx->affine, &affine, &ctx->affine);
     }
-    for (i = 0; i < 6; i++)
-        ctx->paffine[i] = state->affine[i];
+    ctx->paffine = state->affine;
     if (ctx->filter->primitiveunits == objectBoundingBox) {
-        double affine[6] = { width, 0, 0, height, x, y };
-        _rsvg_affine_multiply (ctx->paffine, affine, ctx->paffine);
+        cairo_matrix_t affine;
+        cairo_matrix_init (&affine, width, 0, 0, height, x, y);
+        cairo_matrix_multiply (&ctx->paffine, &affine, &ctx->paffine);
     }
 }
 
-void
-rsvg_alpha_blt (GdkPixbuf * src, gint srcx, gint srcy, gint srcwidth,
-                gint srcheight, GdkPixbuf * dst, gint dstx, gint dsty)
+static gboolean
+rectangle_intersect (gint ax, gint ay, gint awidth, gint aheight,
+                     gint bx, gint by, gint bwidth, gint bheight,
+                     gint *rx, gint *ry, gint *rwidth, gint *rheight)
 {
-    gint rightx;
-    gint bottomy;
-    gint dstwidth;
-    gint dstheight;
+    gint rx1, ry1, rx2, ry2;
+
+    rx1 = MAX (ax, bx);
+    ry1 = MAX (ay, by);
+    rx2 = MIN (ax + awidth, bx + bwidth);
+    ry2 = MIN (ay + aheight, by + bheight);
+
+    if (rx2 > rx1 && ry2 > ry1) {
+        *rx = rx1;
+        *ry = ry1;
+        *rwidth = rx2 - rx1;
+        *rheight = ry2 - ry1;
 
-    gint srcoffsetx;
-    gint srcoffsety;
-    gint dstoffsetx;
-    gint dstoffsety;
+        return TRUE;
+    } else {
+        *rx = *ry = *rwidth = *rheight = 0;
+
+        return FALSE;
+    }
+}
 
+static void
+rsvg_alpha_blt (cairo_surface_t *src,
+                gint srcx,
+                gint srcy,
+                gint srcwidth,
+                gint srcheight,
+                cairo_surface_t *dst,
+                gint dstx,
+                gint dsty)
+{
+    gint src_surf_width, src_surf_height;
+    gint dst_surf_width, dst_surf_height;
+    gint src_clipped_x, src_clipped_y, src_clipped_width, src_clipped_height;
+    gint dst_clipped_x, dst_clipped_y, dst_clipped_width, dst_clipped_height;
     gint x, y, srcrowstride, dstrowstride, sx, sy, dx, dy;
     guchar *src_pixels, *dst_pixels;
 
-    dstheight = srcheight;
-    dstwidth = srcwidth;
-
-    rightx = srcx + srcwidth;
-    bottomy = srcy + srcheight;
-
-    if (rightx > gdk_pixbuf_get_width (src))
-        rightx = gdk_pixbuf_get_width (src);
-    if (bottomy > gdk_pixbuf_get_height (src))
-        bottomy = gdk_pixbuf_get_height (src);
-    srcwidth = rightx - srcx;
-    srcheight = bottomy - srcy;
-
-    rightx = dstx + dstwidth;
-    bottomy = dsty + dstheight;
-    if (rightx > gdk_pixbuf_get_width (dst))
-        rightx = gdk_pixbuf_get_width (dst);
-    if (bottomy > gdk_pixbuf_get_height (dst))
-        bottomy = gdk_pixbuf_get_height (dst);
-    dstwidth = rightx - dstx;
-    dstheight = bottomy - dsty;
-
-    if (dstwidth < srcwidth)
-        srcwidth = dstwidth;
-    if (dstheight < srcheight)
-        srcheight = dstheight;
-
-    if (srcx < 0)
-        srcoffsetx = 0 - srcx;
-    else
-        srcoffsetx = 0;
+    g_assert (cairo_image_surface_get_format (src) == CAIRO_FORMAT_ARGB32);
+    g_assert (cairo_image_surface_get_format (dst) == CAIRO_FORMAT_ARGB32);
 
-    if (srcy < 0)
-        srcoffsety = 0 - srcy;
-    else
-        srcoffsety = 0;
+    cairo_surface_flush (src);
 
-    if (dstx < 0)
-        dstoffsetx = 0 - dstx;
-    else
-        dstoffsetx = 0;
+    src_surf_width  = cairo_image_surface_get_width (src);
+    src_surf_height = cairo_image_surface_get_height (src);
 
-    if (dsty < 0)
-        dstoffsety = 0 - dsty;
-    else
-        dstoffsety = 0;
+    dst_surf_width  = cairo_image_surface_get_width (dst);
+    dst_surf_height = cairo_image_surface_get_height (dst);
+
+    if (!rectangle_intersect (0, 0, src_surf_width, src_surf_height,
+                              srcx, srcy, srcwidth, srcheight,
+                              &src_clipped_x, &src_clipped_y, &src_clipped_width, &src_clipped_height))
+        return; /* source rectangle is not in source surface */
 
-    if (dstoffsetx > srcoffsetx)
-        srcoffsetx = dstoffsetx;
-    if (dstoffsety > srcoffsety)
-        srcoffsety = dstoffsety;
+    if (!rectangle_intersect (0, 0, dst_surf_width, dst_surf_height,
+                              dstx, dsty, src_clipped_width, src_clipped_height,
+                              &dst_clipped_x, &dst_clipped_y, &dst_clipped_width, &dst_clipped_height))
+        return; /* dest rectangle is not in dest surface */
 
-    srcrowstride = gdk_pixbuf_get_rowstride (src);
-    dstrowstride = gdk_pixbuf_get_rowstride (dst);
+    srcrowstride = cairo_image_surface_get_stride (src);
+    dstrowstride = cairo_image_surface_get_stride (dst);
 
-    src_pixels = gdk_pixbuf_get_pixels (src);
-    dst_pixels = gdk_pixbuf_get_pixels (dst);
+    src_pixels = cairo_image_surface_get_data (src);
+    dst_pixels = cairo_image_surface_get_data (dst);
 
-    for (y = srcoffsety; y < srcheight; y++)
-        for (x = srcoffsetx; x < srcwidth; x++) {
+    for (y = 0; y < dst_clipped_height; y++)
+        for (x = 0; x < dst_clipped_width; x++) {
             guint a, c, ad, cd, ar, cr, i;
 
-            sx = x + srcx;
-            sy = y + srcy;
-            dx = x + dstx;
-            dy = y + dsty;
+            sx = x + src_clipped_x;
+            sy = y + src_clipped_y;
+            dx = x + dst_clipped_x;
+            dy = y + dst_clipped_y;
             a = src_pixels[4 * sx + sy * srcrowstride + 3];
 
             if (a) {
@@ -333,15 +372,18 @@ rsvg_alpha_blt (GdkPixbuf * src, gint srcx, gint srcy, gint srcwidth,
                 }
             }
         }
+
+    cairo_surface_mark_dirty (dst);
 }
 
-void
-rsvg_art_affine_image (const GdkPixbuf * img, GdkPixbuf * intermediate,
-                       double *affine, double w, double h)
+static gboolean
+rsvg_art_affine_image (cairo_surface_t *img, 
+                       cairo_surface_t *intermediate,
+                       cairo_matrix_t *affine, 
+                       double w, 
+                       double h)
 {
-    gdouble tmp_affine[6];
-    gdouble inv_affine[6];
-    gdouble raw_inv_affine[6];
+    cairo_matrix_t inv_affine, raw_inv_affine;
     gint intstride;
     gint basestride;
     gint basex, basey;
@@ -356,41 +398,43 @@ rsvg_art_affine_image (const GdkPixbuf * img, GdkPixbuf * intermediate,
     gint iwidth, iheight;
     gint width, height;
 
-    width = gdk_pixbuf_get_width (img);
-    height = gdk_pixbuf_get_height (img);
-    iwidth = gdk_pixbuf_get_width (intermediate);
-    iheight = gdk_pixbuf_get_height (intermediate);
+    g_assert (cairo_image_surface_get_format (intermediate) == CAIRO_FORMAT_ARGB32);
 
-    has_alpha = gdk_pixbuf_get_has_alpha (img);
+    cairo_surface_flush (img);
 
-    basestride = gdk_pixbuf_get_rowstride (img);
-    intstride = gdk_pixbuf_get_rowstride (intermediate);
-    basepix = gdk_pixbuf_get_pixels (img);
-    intpix = gdk_pixbuf_get_pixels (intermediate);
-    basebpp = has_alpha ? 4 : 3;
+    width = cairo_image_surface_get_width (img);
+    height = cairo_image_surface_get_height (img);
+    iwidth = cairo_image_surface_get_width (intermediate);
+    iheight = cairo_image_surface_get_height (intermediate);
 
-    _rsvg_affine_invert (raw_inv_affine, affine);
+    has_alpha = cairo_image_surface_get_format (img) == CAIRO_FORMAT_ARGB32;
 
-    /*scale to w and h */
-    tmp_affine[0] = (double) w;
-    tmp_affine[3] = (double) h;
-    tmp_affine[1] = tmp_affine[2] = tmp_affine[4] = tmp_affine[5] = 0;
-    _rsvg_affine_multiply (tmp_affine, tmp_affine, affine);
+    basestride = cairo_image_surface_get_stride (img);
+    intstride = cairo_image_surface_get_stride (intermediate);
+    basepix = cairo_image_surface_get_data (img);
+    intpix = cairo_image_surface_get_data (intermediate);
+    basebpp = has_alpha ? 4 : 3;
 
-    _rsvg_affine_invert (inv_affine, tmp_affine);
+    raw_inv_affine = *affine;
+    if (cairo_matrix_invert (&raw_inv_affine) != CAIRO_STATUS_SUCCESS)
+      return FALSE;
 
+    cairo_matrix_init_scale (&inv_affine, w, h);
+    cairo_matrix_multiply (&inv_affine, &inv_affine, affine);
+    if (cairo_matrix_invert (&inv_affine) != CAIRO_STATUS_SUCCESS)
+      return FALSE;
 
     /*apply the transformation */
     for (i = 0; i < iwidth; i++)
         for (j = 0; j < iheight; j++) {
-            fbasex = (inv_affine[0] * (double) i + inv_affine[2] * (double) j +
-                      inv_affine[4]) * (double) width;
-            fbasey = (inv_affine[1] * (double) i + inv_affine[3] * (double) j +
-                      inv_affine[5]) * (double) height;
+            fbasex = (inv_affine.xx * (double) i + inv_affine.xy * (double) j +
+                      inv_affine.x0) * (double) width;
+            fbasey = (inv_affine.yx * (double) i + inv_affine.yy * (double) j +
+                      inv_affine.y0) * (double) height;
             basex = floor (fbasex);
             basey = floor (fbasey);
-            rawx = raw_inv_affine[0] * i + raw_inv_affine[2] * j + raw_inv_affine[4];
-            rawy = raw_inv_affine[1] * i + raw_inv_affine[3] * j + raw_inv_affine[5];
+            rawx = raw_inv_affine.xx * i + raw_inv_affine.xy * j + raw_inv_affine.x0;
+            rawy = raw_inv_affine.yx * i + raw_inv_affine.yy * j + raw_inv_affine.y0;
             if (rawx < 0 || rawy < 0 || rawx >= w ||
                 rawy >= h || basex < 0 || basey < 0 || basex >= width || basey >= height) {
                 for (k = 0; k < 4; k++)
@@ -427,6 +471,12 @@ rsvg_art_affine_image (const GdkPixbuf * img, GdkPixbuf * intermediate,
             }
 
         }
+
+    /* Don't need cairo_surface_mark_dirty(intermediate) here since
+     * the only caller does further work and then calls that himself.
+     */
+
+    return TRUE;
 }
 
 static void
@@ -435,46 +485,59 @@ rsvg_filter_free_pair (gpointer value)
     RsvgFilterPrimitiveOutput *output;
 
     output = (RsvgFilterPrimitiveOutput *) value;
-    g_object_unref (G_OBJECT (output->result));
+    cairo_surface_destroy (output->surface);
     g_free (output);
 }
 
+static void
+rsvg_filter_context_free (RsvgFilterContext * ctx)
+{
+    if (!ctx)
+	return;
+
+    if (ctx->bg_surface)
+        cairo_surface_destroy (ctx->bg_surface);
+
+    g_free (ctx);
+}
+
 /**
- * rsvg_filter_render: Copy the source to the bg using a filter.
+ * rsvg_filter_render:
  * @self: a pointer to the filter to use
- * @source: a pointer to the source pixbuf
- * @bg: the background pixbuf
+ * @source: the a #cairo_surface_t of type %CAIRO_SURFACE_TYPE_IMAGE
  * @context: the context
  *
- * This function will create a context for itself, set up the coordinate systems
- * execute all its little primatives and then clean up its own mess
+ * Create a new surface applied the filter. This function will create
+ * a context for itself, set up the coordinate systems execute all its
+ * little primatives and then clean up its own mess.
+ * 
+ * Returns: (transfer full): a new #cairo_surface_t
  **/
-GdkPixbuf *
-rsvg_filter_render (RsvgFilter * self, GdkPixbuf * source,
-                    GdkPixbuf * bg, RsvgDrawingCtx * context, RsvgBbox * bounds, char *channelmap)
+cairo_surface_t *
+rsvg_filter_render (RsvgFilter *self,
+                    cairo_surface_t *source,
+                    RsvgDrawingCtx *context, 
+                    RsvgBbox *bounds, 
+                    char *channelmap)
 {
     RsvgFilterContext *ctx;
     RsvgFilterPrimitive *current;
     guint i;
-    GdkPixbuf *out;
+    cairo_surface_t *output;
 
+    g_return_val_if_fail (source != NULL, NULL);
+    g_return_val_if_fail (cairo_surface_get_type (source) == CAIRO_SURFACE_TYPE_IMAGE, NULL);
 
     ctx = g_new (RsvgFilterContext, 1);
     ctx->filter = self;
-    ctx->source = source;
-    ctx->bg = bg;
+    ctx->source_surface = source;
+    ctx->bg_surface = NULL;
     ctx->results = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, rsvg_filter_free_pair);
     ctx->ctx = context;
 
-    g_object_ref (G_OBJECT (source));
+    rsvg_filter_fix_coordinate_system (ctx, rsvg_current_state (context), bounds);
 
-    rsvg_filter_fix_coordinate_system (ctx, rsvg_state_current (context), *bounds);
-
-    ctx->lastresult.result = source;
-    ctx->lastresult.Rused = 1;
-    ctx->lastresult.Gused = 1;
-    ctx->lastresult.Bused = 1;
-    ctx->lastresult.Aused = 1;
+    ctx->lastresult.surface = cairo_surface_reference (source);
     ctx->lastresult.bounds = rsvg_filter_primitive_get_bounds (NULL, ctx);
 
     for (i = 0; i < 4; i++)
@@ -482,21 +545,21 @@ rsvg_filter_render (RsvgFilter * self, GdkPixbuf * source,
 
     for (i = 0; i < self->super.children->len; i++) {
         current = g_ptr_array_index (self->super.children, i);
-        if (!strncmp (current->super.type->str, "fe", 2))
+        if (RSVG_NODE_IS_FILTER_PRIMITIVE (&current->super))
             rsvg_filter_primitive_render (current, ctx);
     }
 
-    out = ctx->lastresult.result;
+    output = ctx->lastresult.surface;
 
     g_hash_table_destroy (ctx->results);
 
-    g_free (ctx);
+    rsvg_filter_context_free (ctx);
 
-    return out;
+    return output;
 }
 
 /**
- * rsvg_filter_store_result: Files a result into a context.
+ * rsvg_filter_store_result:
  * @name: The name of the result
  * @result: The pointer to the result
  * @ctx: the context that this was called in
@@ -509,67 +572,120 @@ rsvg_filter_store_output (GString * name, RsvgFilterPrimitiveOutput result, Rsvg
 {
     RsvgFilterPrimitiveOutput *store;
 
-    g_object_unref (G_OBJECT (ctx->lastresult.result));
+    cairo_surface_destroy (ctx->lastresult.surface);
 
     store = g_new (RsvgFilterPrimitiveOutput, 1);
     *store = result;
 
-    if (strcmp (name->str, "")) {
-        g_object_ref (G_OBJECT (result.result));        /* increments the references for the table */
+    if (name->str[0] != '\0') {
+        cairo_surface_reference (result.surface);        /* increments the references for the table */
         g_hash_table_insert (ctx->results, g_strdup (name->str), store);
     }
 
-    g_object_ref (G_OBJECT (result.result));    /* increments the references for the last result */
+    cairo_surface_reference (result.surface);    /* increments the references for the last result */
     ctx->lastresult = result;
 }
 
 static void
-rsvg_filter_store_result (GString * name, GdkPixbuf * result, RsvgFilterContext * ctx)
+rsvg_filter_store_result (GString * name,
+                          cairo_surface_t *surface,
+                          RsvgFilterContext * ctx)
 {
     RsvgFilterPrimitiveOutput output;
-    output.Rused = 1;
-    output.Gused = 1;
-    output.Bused = 1;
-    output.Aused = 1;
     output.bounds.x0 = 0;
     output.bounds.y0 = 0;
     output.bounds.x1 = ctx->width;
     output.bounds.y1 = ctx->height;
-    output.result = result;
+    output.surface = surface;
 
     rsvg_filter_store_output (name, output, ctx);
 }
 
-static GdkPixbuf *
-pixbuf_get_alpha (GdkPixbuf * pb, RsvgFilterContext * ctx)
+static cairo_surface_t *
+surface_get_alpha (cairo_surface_t *source,
+                   RsvgFilterContext * ctx)
 {
     guchar *data;
     guchar *pbdata;
-    GdkPixbuf *output;
-
     gsize i, pbsize;
+    cairo_surface_t *surface;
 
-    pbsize = gdk_pixbuf_get_width (pb) * gdk_pixbuf_get_height (pb);
+    if (source == NULL)
+        return NULL;
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8,
-                                       gdk_pixbuf_get_width (pb), gdk_pixbuf_get_height (pb));
+    cairo_surface_flush (source);
 
-    data = gdk_pixbuf_get_pixels (output);
-    pbdata = gdk_pixbuf_get_pixels (pb);
+    pbsize = cairo_image_surface_get_width (source) * 
+             cairo_image_surface_get_height (source);
+
+    surface = _rsvg_image_surface_new (cairo_image_surface_get_width (source),
+                                       cairo_image_surface_get_height (source));
+    if (surface == NULL)
+        return NULL;
 
+    data = cairo_image_surface_get_data (surface);
+    pbdata = cairo_image_surface_get_data (source);
+
+    /* FIXMEchpe: rewrite this into nested width, height loops */
     for (i = 0; i < pbsize; i++)
         data[i * 4 + ctx->channelmap[3]] = pbdata[i * 4 + ctx->channelmap[3]];
 
-    return output;
+    cairo_surface_mark_dirty (surface);
+    return surface;
 }
 
+static cairo_surface_t *
+rsvg_compile_bg (RsvgDrawingCtx * ctx)
+{
+    RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
+    cairo_surface_t *surface;
+    cairo_t *cr;
+    GList *i;
+
+    surface = _rsvg_image_surface_new (render->width, render->height);
+    if (surface == NULL)
+        return NULL;
+
+    cr = cairo_create (surface);
+
+    for (i = g_list_last (render->cr_stack); i != NULL; i = g_list_previous (i)) {
+        cairo_t *draw = i->data;
+        gboolean nest = draw != render->initial_cr;
+        cairo_set_source_surface (cr, cairo_get_target (draw),
+                                  nest ? 0 : -render->offset_x,
+                                  nest ? 0 : -render->offset_y);
+        cairo_paint (cr);
+    }
+
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+/**
+ * rsvg_filter_get_bg:
+ * 
+ * Returns: (transfer none) (nullable): a #cairo_surface_t, or %NULL
+ */
+static cairo_surface_t *
+rsvg_filter_get_bg (RsvgFilterContext * ctx)
+{
+    if (!ctx->bg_surface)
+        ctx->bg_surface = rsvg_compile_bg (ctx->ctx);
+
+    return ctx->bg_surface;
+}
+
+/* FIXMEchpe: proper return value and out param! */
 /**
- * rsvg_filter_get_in: Gets a pixbuf for a primative.
- * @name: The name of the pixbuf
+ * rsvg_filter_get_result:
+ * @name: The name of the surface
  * @ctx: the context that this was called in
  *
- * Returns a pointer to the result that the name refers to, a special
- * Pixbuf if the name is a special keyword or NULL if nothing was found
+ * Gets a surface for a primitive
+ *
+ * Returns: (nullable): a pointer to the result that the name refers to, a special
+ * surface if the name is a special keyword or %NULL if nothing was found
  **/
 static RsvgFilterPrimitiveOutput
 rsvg_filter_get_result (GString * name, RsvgFilterContext * ctx)
@@ -579,28 +695,22 @@ rsvg_filter_get_result (GString * name, RsvgFilterContext * ctx)
     output.bounds.x0 = output.bounds.x1 = output.bounds.y0 = output.bounds.y1 = 0;
 
     if (!strcmp (name->str, "SourceGraphic")) {
-        g_object_ref (G_OBJECT (ctx->source));
-        output.result = ctx->source;
-        output.Rused = output.Gused = output.Bused = output.Aused = 1;
+        output.surface = cairo_surface_reference (ctx->source_surface);
         return output;
     } else if (!strcmp (name->str, "BackgroundImage")) {
-        g_object_ref (G_OBJECT (ctx->bg));
-        output.result = ctx->bg;
-        output.Rused = output.Gused = output.Bused = output.Aused = 1;
+        output.surface = rsvg_filter_get_bg (ctx);
+        if (output.surface)
+            cairo_surface_reference (output.surface);
         return output;
-    } else if (!strcmp (name->str, "") || !strcmp (name->str, "none") || !name) {
-        g_object_ref (G_OBJECT (ctx->lastresult.result));
+    } else if (!strcmp (name->str, "") || !strcmp (name->str, "none")) {
         output = ctx->lastresult;
+        cairo_surface_reference (output.surface);
         return output;
     } else if (!strcmp (name->str, "SourceAlpha")) {
-        output.Rused = output.Gused = output.Bused = 0;
-        output.Aused = 1;
-        output.result = pixbuf_get_alpha (ctx->source, ctx);
+        output.surface = surface_get_alpha (ctx->source_surface, ctx);
         return output;
     } else if (!strcmp (name->str, "BackgroundAlpha")) {
-        output.Rused = output.Gused = output.Bused = 0;
-        output.Aused = 1;
-        output.result = pixbuf_get_alpha (ctx->bg, ctx);
+        output.surface = surface_get_alpha (rsvg_filter_get_bg (ctx), ctx);
         return output;
     }
 
@@ -608,47 +718,28 @@ rsvg_filter_get_result (GString * name, RsvgFilterContext * ctx)
 
     if (outputpointer != NULL) {
         output = *outputpointer;
-        g_object_ref (G_OBJECT (output.result));
+        cairo_surface_reference (output.surface);
         return output;
     }
 
-    g_warning (_("%s not found\n"), name->str);
+    /* g_warning (_("%s not found\n"), name->str); */
 
     output = ctx->lastresult;
-    g_object_ref (G_OBJECT (ctx->lastresult.result));
+    cairo_surface_reference (output.surface);
     return output;
 }
 
-
-static GdkPixbuf *
-rsvg_filter_get_in (GString * name, RsvgFilterContext * ctx)
-{
-    return rsvg_filter_get_result (name, ctx).result;
-}
-
 /**
- * rsvg_filter_parse: Looks up an allready created filter.
- * @defs: a pointer to the hash of definitions
- * @str: a string with the name of the filter to be looked up
- *
- * Returns a pointer to the filter that the name refers to, or NULL
- * if none was found
- **/
-RsvgFilter *
-rsvg_filter_parse (const RsvgDefs * defs, const char *str)
+ * 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)
 {
-    char *name;
-
-    name = rsvg_get_url_string (str);
-    if (name) {
-        RsvgNode *val;
-        val = rsvg_defs_lookup (defs, name);
-        g_free (name);
-
-        if (val && (!strcmp (val->type->str, "filter")))
-            return (RsvgFilter *) val;
-    }
-    return NULL;
+    return rsvg_filter_get_result (name, ctx).surface;
 }
 
 static void
@@ -686,7 +777,7 @@ rsvg_filter_set_args (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
 }
 
 /**
- * rsvg_new_filter: Creates a black filter
+ * rsvg_new_filter:
  *
  * Creates a blank filter and assigns default values to everything
  **/
@@ -696,7 +787,7 @@ rsvg_new_filter (void)
     RsvgFilter *filter;
 
     filter = g_new (RsvgFilter, 1);
-    _rsvg_node_init (&filter->super);
+    _rsvg_node_init (&filter->super, RSVG_NODE_TYPE_FILTER);
     filter->filterunits = objectBoundingBox;
     filter->primitiveunits = userSpaceOnUse;
     filter->x = _rsvg_css_parse_length ("-10%");
@@ -724,8 +815,12 @@ struct _RsvgFilterPrimitiveBlend {
 };
 
 static void
-rsvg_filter_blend (RsvgFilterPrimitiveBlendMode mode, GdkPixbuf * in, GdkPixbuf * in2,
-                   GdkPixbuf * output, RsvgIRect boundarys, int *channelmap)
+rsvg_filter_blend (RsvgFilterPrimitiveBlendMode mode, 
+                   cairo_surface_t *in, 
+                   cairo_surface_t *in2,
+                   cairo_surface_t* output, 
+                   RsvgIRect boundarys, 
+                   int *channelmap)
 {
     guchar i;
     gint x, y;
@@ -733,20 +828,24 @@ rsvg_filter_blend (RsvgFilterPrimitiveBlendMode mode, GdkPixbuf * in, GdkPixbuf
     guchar *in_pixels;
     guchar *in2_pixels;
     guchar *output_pixels;
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
-    rowstride = gdk_pixbuf_get_rowstride (in);
-    rowstride2 = gdk_pixbuf_get_rowstride (in2);
-    rowstrideo = gdk_pixbuf_get_rowstride (output);
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
-    in_pixels = gdk_pixbuf_get_pixels (in);
-    in2_pixels = gdk_pixbuf_get_pixels (in2);
+    cairo_surface_flush (in);
+    cairo_surface_flush (in2);
+
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
+    rowstride = cairo_image_surface_get_stride (in);
+    rowstride2 = cairo_image_surface_get_stride (in2);
+    rowstrideo = cairo_image_surface_get_stride (output);
+
+    output_pixels = cairo_image_surface_get_data (output);
+    in_pixels = cairo_image_surface_get_data (in);
+    in2_pixels = cairo_image_surface_get_data (in2);
 
     if (boundarys.x0 < 0)
         boundarys.x0 = 0;
     if (boundarys.y0 < 0)
-        boundarys.y1 = 0;
+        boundarys.y0 = 0;
     if (boundarys.x1 >= width)
         boundarys.x1 = width;
     if (boundarys.y1 >= height)
@@ -831,8 +930,9 @@ rsvg_filter_blend (RsvgFilterPrimitiveBlendMode mode, GdkPixbuf * in, GdkPixbuf
             }
             output_pixels[4 * x + y * rowstrideo + channelmap[3]] = qr * 255.0;
         }
-}
 
+    cairo_surface_mark_dirty (output);
+}
 
 static void
 rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self, RsvgFilterContext * ctx)
@@ -841,88 +941,46 @@ rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self, RsvgFilterContex
 
     RsvgFilterPrimitiveBlend *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
-    GdkPixbuf *in2;
+    cairo_surface_t *output, *in, *in2;
 
     upself = (RsvgFilterPrimitiveBlend *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
+    if (in == NULL)
+      return;
+
     in2 = rsvg_filter_get_in (upself->in2, ctx);
+    if (in2 == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    output =
-        _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, gdk_pixbuf_get_width (in),
-                                  gdk_pixbuf_get_height (in));
+    output = _rsvg_image_surface_new (cairo_image_surface_get_width (in),
+                                      cairo_image_surface_get_height (in));
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        cairo_surface_destroy (in2);
+        return;
+    }
 
     rsvg_filter_blend (upself->mode, in, in2, output, boundarys, ctx->channelmap);
 
     rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (in2));
-    g_object_unref (G_OBJECT (output));
-}
-
-void
-rsvg_filter_adobe_blend (gint modenum, GdkPixbuf * in, GdkPixbuf * bg, GdkPixbuf * output,
-                         RsvgIRect boundarys, RsvgDrawingCtx * ctx)
-{
-    int standardmap[4] = { 0, 1, 2, 3 };
-    RsvgFilterPrimitiveBlendMode mode;
-    mode = normal;
-
-    switch (modenum) {
-    case 0:
-        mode = normal;
-        break;
-    case 1:
-        mode = multiply;
-        break;
-    case 2:
-        mode = screen;
-        break;
-    case 3:
-        mode = darken;
-        break;
-    case 4:
-        mode = lighten;
-        break;
-    case 5:
-        mode = softlight;
-        break;
-    case 6:
-        mode = hardlight;
-        break;
-    case 7:
-        mode = colordodge;
-        break;
-    case 8:
-        mode = colorburn;
-        break;
-    case 9:
-        mode = overlay;
-        break;
-    case 10:
-        mode = exclusion;
-        break;
-    case 11:
-        mode = difference;
-        break;
-    }
-
-    rsvg_filter_blend (mode, in, bg, output, boundarys, standardmap);
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (in2);
+    cairo_surface_destroy (output);
 }
 
 static void
 rsvg_filter_primitive_blend_free (RsvgNode * self)
 {
-    RsvgFilterPrimitiveBlend *upself;
-    upself = (RsvgFilterPrimitiveBlend *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    g_string_free (upself->in2, TRUE);
-    _rsvg_node_free (self);
+    RsvgFilterPrimitiveBlend *blend;
+    blend = (RsvgFilterPrimitiveBlend *) self;
+    g_string_free (blend->in2, TRUE);
+
+    rsvg_filter_primitive_free (self);
 }
 
 static void
@@ -970,15 +1028,15 @@ rsvg_new_filter_primitive_blend (void)
 {
     RsvgFilterPrimitiveBlend *filter;
     filter = g_new (RsvgFilterPrimitiveBlend, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_BLEND);
     filter->mode = normal;
     filter->super.in = g_string_new ("none");
     filter->in2 = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
-    filter->super.render = &rsvg_filter_primitive_blend_render;
-    filter->super.super.free = &rsvg_filter_primitive_blend_free;
+    filter->super.render = rsvg_filter_primitive_blend_render;
+    filter->super.super.free = rsvg_filter_primitive_blend_free;
     filter->super.super.set_atts = rsvg_filter_primitive_blend_set_atts;
     return (RsvgNode *) filter;
 }
@@ -1014,8 +1072,7 @@ rsvg_filter_primitive_convolve_matrix_render (RsvgFilterPrimitive * self, RsvgFi
 
     RsvgFilterPrimitiveConvolveMatrix *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
 
     gint sx, sy, kx, ky;
     guchar sval;
@@ -1028,24 +1085,34 @@ rsvg_filter_primitive_convolve_matrix_render (RsvgFilterPrimitive * self, RsvgFi
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
+
+    cairo_surface_flush (in);
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    in_pixels = cairo_image_surface_get_data (in);
 
-    targetx = upself->targetx * ctx->paffine[0];
-    targety = upself->targety * ctx->paffine[3];
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
+
+    targetx = upself->targetx * ctx->paffine.xx;
+    targety = upself->targety * ctx->paffine.yy;
 
     if (upself->dx != 0 || upself->dy != 0) {
-        dx = upself->dx * ctx->paffine[0];
-        dy = upself->dy * ctx->paffine[3];
+        dx = upself->dx * ctx->paffine.xx;
+        dy = upself->dy * ctx->paffine.yy;
     } else
         dx = dy = 1;
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    rowstride = cairo_image_surface_get_stride (in);
+
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    output_pixels = cairo_image_surface_get_data (output);
 
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++) {
@@ -1109,22 +1176,24 @@ rsvg_filter_primitive_convolve_matrix_render (RsvgFilterPrimitive * self, RsvgFi
                     output_pixels[4 * x + y * rowstride + ctx->channelmap[3]] / 255;
             }
         }
+
+    cairo_surface_mark_dirty (output);
+
     rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
 rsvg_filter_primitive_convolve_matrix_free (RsvgNode * self)
 {
-    RsvgFilterPrimitiveConvolveMatrix *upself;
+    RsvgFilterPrimitiveConvolveMatrix *convolve;
 
-    upself = (RsvgFilterPrimitiveConvolveMatrix *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    g_free (upself->KernelMatrix);
-    _rsvg_node_free (self);
+    convolve = (RsvgFilterPrimitiveConvolveMatrix *) self;
+    g_free (convolve->KernelMatrix);
+
+    rsvg_filter_primitive_free (self);
 }
 
 static void
@@ -1175,9 +1244,8 @@ rsvg_filter_primitive_convolve_matrix_set_atts (RsvgNode * self,
         if ((value = rsvg_property_bag_lookup (atts, "order"))) {
             double tempx, tempy;
             rsvg_css_parse_number_optional_number (value, &tempx, &tempy);
-            filter->orderx = tempx;
-            filter->ordery = tempy;
-
+            filter->orderx = MAX (tempx, G_MAXINT);
+            filter->ordery = MAX (tempy, G_MAXINT);
         }
         if ((value = rsvg_property_bag_lookup (atts, "kernelUnitLength")))
             rsvg_css_parse_number_optional_number (value, &filter->dx, &filter->dy);
@@ -1197,6 +1265,9 @@ rsvg_filter_primitive_convolve_matrix_set_atts (RsvgNode * self,
             rsvg_defs_register_name (ctx->priv->defs, value, &filter->super.super);
     }
 
+    if ((gint64) listlen != (gint64) filter->orderx * filter->ordery)
+        filter->orderx = filter->ordery = 0;
+
     if (filter->divisor == 0) {
         for (j = 0; j < filter->orderx; j++)
             for (i = 0; i < filter->ordery; i++)
@@ -1206,9 +1277,6 @@ rsvg_filter_primitive_convolve_matrix_set_atts (RsvgNode * self,
     if (filter->divisor == 0)
         filter->divisor = 1;
 
-    if ((gint) listlen < filter->orderx * filter->ordery)
-        filter->orderx = filter->ordery = 0;
-
     if (!has_target_x) {
         filter->targetx = floor (filter->orderx / 2);
     }
@@ -1222,19 +1290,20 @@ rsvg_new_filter_primitive_convolve_matrix (void)
 {
     RsvgFilterPrimitiveConvolveMatrix *filter;
     filter = g_new (RsvgFilterPrimitiveConvolveMatrix, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_CONVOLVE_MATRIX);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
+    filter->KernelMatrix = NULL;
     filter->divisor = 0;
     filter->bias = 0;
     filter->dx = 0;
     filter->dy = 0;
     filter->preservealpha = FALSE;
     filter->edgemode = 0;
-    filter->super.render = &rsvg_filter_primitive_convolve_matrix_render;
-    filter->super.super.free = &rsvg_filter_primitive_convolve_matrix_free;
+    filter->super.render = rsvg_filter_primitive_convolve_matrix_render;
+    filter->super.super.free = rsvg_filter_primitive_convolve_matrix_free;
     filter->super.super.set_atts = rsvg_filter_primitive_convolve_matrix_set_atts;
     return (RsvgNode *) filter;
 }
@@ -1251,182 +1320,597 @@ struct _RsvgFilterPrimitiveGaussianBlur {
 };
 
 static void
-box_blur (GdkPixbuf * in, GdkPixbuf * output, guchar * intermediate, gint kw,
-          gint kh, RsvgIRect boundarys, RsvgFilterPrimitiveOutput op)
+box_blur_line (gint box_width, gint even_offset,
+               guchar *src, guchar *dest,
+               gint len, gint bpp)
 {
-    gint ch;
-    gint x, y;
-    gint rowstride, height, width;
+    gint  i;
+    gint  lead;    /* This marks the leading edge of the kernel              */
+    gint  output;  /* This marks the center of the kernel                    */
+    gint  trail;   /* This marks the pixel BEHIND the last 1 in the
+                      kernel; it's the pixel to remove from the accumulator. */
+    gint  *ac;     /* Accumulator for each channel                           */
+
+    ac = g_new (gint, bpp);
+
+    /* The algorithm differs for even and odd-sized kernels.
+     * With the output at the center,
+     * If odd, the kernel might look like this: 0011100
+     * If even, the kernel will either be centered on the boundary between
+     * the output and its left neighbor, or on the boundary between the
+     * output and its right neighbor, depending on even_lr.
+     * So it might be 0111100 or 0011110, where output is on the center
+     * of these arrays.
+     */
+    lead = 0;
+
+    if (box_width % 2 != 0) {
+        /* Odd-width kernel */
+        output = lead - (box_width - 1) / 2;
+        trail  = lead - box_width;
+    } else {
+        /* Even-width kernel. */
+        if (even_offset == 1) {
+            /* Right offset */
+            output = lead + 1 - box_width / 2;
+            trail  = lead - box_width;
+        } else if (even_offset == -1) {
+            /* Left offset */
+            output = lead - box_width / 2;
+            trail  = lead - box_width;
+        } else {
+            /* If even_offset isn't 1 or -1, there's some error. */
+            g_assert_not_reached ();
+        }
+    }
 
-    guchar *in_pixels;
-    guchar *output_pixels;
+    /* Initialize accumulator */
+    for (i = 0; i < bpp; i++)
+        ac[i] = 0;
+
+    /* As the kernel moves across the image, it has a leading edge and a
+     * trailing edge, and the output is in the middle. */
+    while (output < len) {
+        /* The number of pixels that are both in the image and
+         * currently covered by the kernel. This is necessary to
+         * handle edge cases. */
+        guint coverage = (lead < len ? lead : len - 1) - (trail >= 0 ? trail : -1);
+
+#ifdef READABLE_BOXBLUR_CODE
+/* The code here does the same as the code below, but the code below
+ * has been optimized by moving the if statements out of the tight for
+ * loop, and is harder to understand.
+ * Don't use both this code and the code below. */
+        for (i = 0; i < bpp; i++) {
+            /* If the leading edge of the kernel is still on the image,
+             * add the value there to the accumulator. */
+            if (lead < len)
+                ac[i] += src[bpp * lead + i];
+
+            /* If the trailing edge of the kernel is on the image,
+             * subtract the value there from the accumulator. */
+            if (trail >= 0)
+                ac[i] -= src[bpp * trail + i];
+
+            /* Take the averaged value in the accumulator and store
+             * that value in the output. The number of pixels currently
+             * stored in the accumulator can be less than the nominal
+             * width of the kernel because the kernel can go "over the edge"
+             * of the image. */
+            if (output >= 0)
+                dest[bpp * output + i] = (ac[i] + (coverage >> 1)) / coverage;
+        }
+#endif
+
+        /* If the leading edge of the kernel is still on the image... */
+        if (lead < len) {
+            if (trail >= 0) {
+                /* If the trailing edge of the kernel is on the image. (Since
+                 * the output is in between the lead and trail, it must be on
+                 * the image. */
+                for (i = 0; i < bpp; i++) {
+                    ac[i] += src[bpp * lead + i];
+                    ac[i] -= src[bpp * trail + i];
+                    dest[bpp * output + i] = (ac[i] + (coverage >> 1)) / coverage;
+                }
+            } else if (output >= 0) {
+                /* If the output is on the image, but the trailing edge isn't yet
+                 * on the image. */
+            
+                for (i = 0; i < bpp; i++) {
+                    ac[i] += src[bpp * lead + i];
+                    dest[bpp * output + i] = (ac[i] + (coverage >> 1)) / coverage;
+                }
+            } else {
+                /* If leading edge is on the image, but the output and trailing
+                 * edge aren't yet on the image. */
+                for (i = 0; i < bpp; i++)
+                    ac[i] += src[bpp * lead + i];
+            }
+        } else if (trail >= 0) {
+            /* If the leading edge has gone off the image, but the output and
+             * trailing edge are on the image. (The big loop exits when the
+             * output goes off the image. */
+            for (i = 0; i < bpp; i++) {
+                ac[i] -= src[bpp * trail + i];
+                dest[bpp * output + i] = (ac[i] + (coverage >> 1)) / coverage;
+            }
+        } else if (output >= 0) {
+            /* Leading has gone off the image and trailing isn't yet in it
+             * (small image) */
+            for (i = 0; i < bpp; i++)
+                dest[bpp * output + i] = (ac[i] + (coverage >> 1)) / coverage;
+        }
+
+        lead++;
+        output++;
+        trail++;
+    }
+
+    g_free (ac);
+}
+
+static gint
+compute_box_blur_width (double radius)
+{
+    double width;
+
+    width = radius * 3 * sqrt (2 * G_PI) / 4;
+    return (gint) (width + 0.5);
+}
+
+#define SQR(x) ((x) * (x))
+
+static void
+make_gaussian_convolution_matrix (gdouble radius, gdouble **out_matrix, gint *out_matrix_len)
+{
+    gdouble *matrix;
+    gdouble std_dev;
+    gdouble sum;
+    gint matrix_len;
+    gint i, j;
+
+    std_dev = radius + 1.0;
+    radius = std_dev * 2;
+
+    matrix_len = 2 * ceil (radius - 0.5) + 1;
+    if (matrix_len <= 0)
+        matrix_len = 1;
+
+    matrix = g_new (gdouble, matrix_len);
+
+    /* Fill the matrix by doing numerical integration approximation
+     * from -2*std_dev to 2*std_dev, sampling 50 points per pixel.
+     * We do the bottom half, mirror it to the top half, then compute the
+     * center point.  Otherwise asymmetric quantization errors will occur.
+     * The formula to integrate is e^-(x^2/2s^2).
+     */
+
+    for (i = matrix_len / 2 + 1; i < matrix_len; i++)
+    {
+        gdouble base_x = i - (matrix_len / 2) - 0.5;
+
+        sum = 0;
+        for (j = 1; j <= 50; j++)
+        {
+            gdouble r = base_x + 0.02 * j;
 
-    gint sum;
+            if (r <= radius)
+                sum += exp (- SQR (r) / (2 * SQR (std_dev)));
+        }
+
+        matrix[i] = sum / 50;
+    }
+
+    /* mirror to the bottom half */
+    for (i = 0; i <= matrix_len / 2; i++)
+        matrix[i] = matrix[matrix_len - 1 - i];
 
+    /* find center val -- calculate an odd number of quanta to make it
+     * symmetric, even if the center point is weighted slightly higher
+     * than others.
+     */
+    sum = 0;
+    for (j = 0; j <= 50; j++)
+        sum += exp (- SQR (- 0.5 + 0.02 * j) / (2 * SQR (std_dev)));
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    matrix[matrix_len / 2] = sum / 51;
 
-    in_pixels = gdk_pixbuf_get_pixels (in);
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    /* normalize the distribution by scaling the total sum to one */
+    sum = 0;
+    for (i = 0; i < matrix_len; i++)
+        sum += matrix[i];
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    for (i = 0; i < matrix_len; i++)
+        matrix[i] = matrix[i] / sum;
 
-    if (kw > boundarys.x1 - boundarys.x0)
-        kw = boundarys.x1 - boundarys.x0;
+    *out_matrix = matrix;
+    *out_matrix_len = matrix_len;
+}
+
+static void
+gaussian_blur_line (gdouble *matrix,
+                    gint matrix_len,
+                    guchar *src,
+                    guchar *dest,
+                    gint len,
+                    gint bpp)
+{
+    guchar *src_p;
+    guchar *src_p1;
+    gint matrix_middle;
+    gint row;
+    gint i, j;
 
-    if (kh > boundarys.y1 - boundarys.y0)
-        kh = boundarys.y1 - boundarys.y0;
+    matrix_middle = matrix_len / 2;
 
+    /* picture smaller than the matrix? */
+    if (matrix_len > len) {
+        for (row = 0; row < len; row++) {
+            /* find the scale factor */
+            gdouble scale = 0;
 
-    if (kw >= 1) {
-        for (ch = 0; ch < 4; ch++) {
-            switch (ch) {
-            case 0:
-                if (!op.Rused)
-                    continue;
-            case 1:
-                if (!op.Gused)
-                    continue;
-            case 2:
-                if (!op.Bused)
-                    continue;
-            case 3:
-                if (!op.Aused)
-                    continue;
+            for (j = 0; j < len; j++) {
+                /* if the index is in bounds, add it to the scale counter */
+                if (j + matrix_middle - row >= 0 &&
+                    j + matrix_middle - row < matrix_len)
+                    scale += matrix[j];
             }
-            for (y = boundarys.y0; y < boundarys.y1; y++) {
-                sum = 0;
-                for (x = boundarys.x0; x < boundarys.x0 + kw; x++) {
-                    sum += (intermediate[x % kw] = in_pixels[4 * x + y * rowstride + ch]);
 
-                    if (x - kw / 2 >= 0 && x - kw / 2 < boundarys.x1)
-                        output_pixels[4 * (x - kw / 2) + y * rowstride + ch] = sum / kw;
-                }
-                for (x = boundarys.x0 + kw; x < boundarys.x1; x++) {
-                    sum -= intermediate[x % kw];
-                    sum += (intermediate[x % kw] = in_pixels[4 * x + y * rowstride + ch]);
-                    output_pixels[4 * (x - kw / 2) + y * rowstride + ch] = sum / kw;
-                }
-                for (x = boundarys.x1; x < boundarys.x1 + kw; x++) {
-                    sum -= intermediate[x % kw];
+            src_p = src;
+
+            for (i = 0; i < bpp; i++) {
+                gdouble sum = 0;
+
+                src_p1 = src_p++;
 
-                    if (x - kw / 2 >= 0 && x - kw / 2 < boundarys.x1)
-                        output_pixels[4 * (x - kw / 2) + y * rowstride + ch] = sum / kw;
+                for (j = 0; j < len; j++) {
+                    if (j + matrix_middle - row >= 0 &&
+                        j + matrix_middle - row < matrix_len)
+                        sum += *src_p1 * matrix[j];
+
+                    src_p1 += bpp;
                 }
+
+                *dest++ = (guchar) (sum / scale + 0.5);
             }
         }
-        in_pixels = output_pixels;
-    }
+    } else {
+        /* left edge */
 
-    if (kh >= 1) {
-        for (ch = 0; ch < 4; ch++) {
-            switch (ch) {
-            case 0:
-                if (!op.Rused)
-                    continue;
-            case 1:
-                if (!op.Gused)
-                    continue;
-            case 2:
-                if (!op.Bused)
-                    continue;
-            case 3:
-                if (!op.Aused)
-                    continue;
-            }
+        for (row = 0; row < matrix_middle; row++) {
+            /* find scale factor */
+            gdouble scale = 0;
 
+            for (j = matrix_middle - row; j < matrix_len; j++)
+                scale += matrix[j];
 
-            for (x = boundarys.x0; x < boundarys.x1; x++) {
-                sum = 0;
+            src_p = src;
 
-                for (y = boundarys.y0; y < boundarys.y0 + kh; y++) {
-                    sum += (intermediate[y % kh] = in_pixels[4 * x + y * rowstride + ch]);
+            for (i = 0; i < bpp; i++) {
+                gdouble sum = 0;
 
-                    if (y - kh / 2 >= 0 && y - kh / 2 < boundarys.y1)
-                        output_pixels[4 * x + (y - kh / 2) * rowstride + ch] = sum / kh;
+                src_p1 = src_p++;
+
+                for (j = matrix_middle - row; j < matrix_len; j++) {
+                    sum += *src_p1 * matrix[j];
+                    src_p1 += bpp;
                 }
-                for (; y < boundarys.y1; y++) {
-                    sum -= intermediate[y % kh];
-                    sum += (intermediate[y % kh] = in_pixels[4 * x + y * rowstride + ch]);
-                    output_pixels[4 * x + (y - kh / 2) * rowstride + ch] = sum / kh;
+
+                *dest++ = (guchar) (sum / scale + 0.5);
+            }
+        }
+
+        /* go through each pixel in each col */
+        for (; row < len - matrix_middle; row++) {
+            src_p = src + (row - matrix_middle) * bpp;
+
+            for (i = 0; i < bpp; i++) {
+                gdouble sum = 0;
+
+                src_p1 = src_p++;
+
+                for (j = 0; j < matrix_len; j++) {
+                    sum += matrix[j] * *src_p1;
+                    src_p1 += bpp;
                 }
-                for (; y < boundarys.y1 + kh; y++) {
-                    sum -= intermediate[y % kh];
 
-                    if (y - kh / 2 >= 0 && y - kh / 2 < boundarys.y1)
-                        output_pixels[4 * x + (y - kh / 2) * rowstride + ch] = sum / kh;
+                *dest++ = (guchar) (sum + 0.5);
+            }
+        }
+
+        /* for the edge condition, we only use available info and scale to one */
+        for (; row < len; row++) {
+            /* find scale factor */
+            gdouble scale = 0;
+
+            for (j = 0; j < len - row + matrix_middle; j++)
+                scale += matrix[j];
+
+            src_p = src + (row - matrix_middle) * bpp;
+
+            for (i = 0; i < bpp; i++) {
+                gdouble sum = 0;
+
+                src_p1 = src_p++;
+
+                for (j = 0; j < len - row + matrix_middle; j++) {
+                    sum += *src_p1 * matrix[j];
+                    src_p1 += bpp;
                 }
+
+                *dest++ = (guchar) (sum / scale + 0.5);
             }
         }
     }
 }
 
 static void
-fast_blur (GdkPixbuf * in, GdkPixbuf * output, gfloat sx,
-           gfloat sy, RsvgIRect boundarys, RsvgFilterPrimitiveOutput op)
+get_column (guchar *column_data,
+            guchar *src_data,
+            gint src_stride,
+            gint bpp,
+            gint height,
+            gint x)
 {
-    gint kx, ky;
-    guchar *intermediate;
+    gint y;
+    gint c;
+
+    for (y = 0; y < height; y++) {
+        guchar *src = src_data + y * src_stride + x * bpp;
+
+        for (c = 0; c < bpp; c++)
+            column_data[c] = src[c];
+
+        column_data += bpp;
+    }
+}
+
+static void
+put_column (guchar *column_data, guchar *dest_data, gint dest_stride, gint bpp, gint height, gint x)
+{
+    gint y;
+    gint c;
+
+    for (y = 0; y < height; y++) {
+        guchar *dst = dest_data + y * dest_stride + x * bpp;
+
+        for (c = 0; c < bpp; c++)
+            dst[c] = column_data[c];
 
-    kx = floor (sx * 3 * sqrt (2 * M_PI) / 4 + 0.5);
-    ky = floor (sy * 3 * sqrt (2 * M_PI) / 4 + 0.5);
+        column_data += bpp;
+    }
+}
 
-    if (kx < 1 && ky < 1)
+static void
+gaussian_blur_surface (cairo_surface_t *in,
+                       cairo_surface_t *out,
+                       gdouble sx,
+                       gdouble sy)
+{
+    gboolean use_box_blur;
+    gint width, height;
+    cairo_format_t in_format, out_format;
+    gint in_stride;
+    gint out_stride;
+    guchar *in_data, *out_data;
+    gint bpp;
+    gboolean out_has_data;
+    
+    cairo_surface_flush (in);
+
+    width = cairo_image_surface_get_width (in);
+    height = cairo_image_surface_get_height (in);
+
+    g_assert (width == cairo_image_surface_get_width (out)
+              && height == cairo_image_surface_get_height (out));
+
+    in_format = cairo_image_surface_get_format (in);
+    out_format = cairo_image_surface_get_format (out);
+    g_assert (in_format == out_format);
+    g_assert (in_format == CAIRO_FORMAT_ARGB32
+              || in_format == CAIRO_FORMAT_A8);
+
+    if (in_format == CAIRO_FORMAT_ARGB32)
+        bpp = 4;
+    else if (in_format == CAIRO_FORMAT_A8)
+        bpp = 1;
+    else {
+        g_assert_not_reached ();
         return;
+    }
 
-    intermediate = g_new (guchar, MAX (kx, ky));
+    in_stride = cairo_image_surface_get_stride (in);
+    out_stride = cairo_image_surface_get_stride (out);
 
-    box_blur (in, output, intermediate, kx, ky, boundarys, op);
-    box_blur (output, output, intermediate, kx, ky, boundarys, op);
-    box_blur (output, output, intermediate, kx, ky, boundarys, op);
+    in_data = cairo_image_surface_get_data (in);
+    out_data = cairo_image_surface_get_data (out);
 
-    g_free (intermediate);
+    if (sx < 0.0)
+        sx = 0.0;
+
+    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) {
+        cairo_t *cr;
+
+        cr = cairo_create (out);
+        cairo_set_source_surface (cr, in, 0, 0);
+        cairo_paint (cr);
+        cairo_destroy (cr);
+        return;
+    }
+
+    if (sx != 0.0) {
+        gint box_width;
+        gdouble *gaussian_matrix;
+        gint gaussian_matrix_len;
+        int y;
+        guchar *row_buffer = NULL;
+        guchar *row1, *row2;
+
+        if (use_box_blur) {
+            box_width = compute_box_blur_width (sx);
+
+            /* twice the size so we can have "two" scratch rows */
+            row_buffer = g_new (guchar, width * bpp * 2);
+            row1 = row_buffer;
+            row2 = row_buffer + width * bpp;
+        } else
+            make_gaussian_convolution_matrix (sx, &gaussian_matrix, &gaussian_matrix_len);
+
+        for (y = 0; y < height; y++) {
+            guchar *in_row, *out_row;
+
+            in_row = in_data + in_stride * y;
+            out_row = out_data + out_stride * y;
+
+            if (use_box_blur) {
+                if (box_width % 2 != 0) {
+                    /* Odd-width box blur: repeat 3 times, centered on output pixel */
+
+                    box_blur_line (box_width, 0, in_row, row1,    width, bpp);
+                    box_blur_line (box_width, 0, row1,   row2,    width, bpp);
+                    box_blur_line (box_width, 0, row2,   out_row, width, bpp);
+                } else {
+                    /* Even-width box blur:
+                     * This method is suggested by the specification for SVG.
+                     * One pass with width n, centered between output and right pixel
+                     * One pass with width n, centered between output and left pixel
+                     * One pass with width n+1, centered on output pixel
+                     */
+                    box_blur_line (box_width,     -1, in_row, row1,    width, bpp);
+                    box_blur_line (box_width,      1, row1,   row2,    width, bpp);
+                    box_blur_line (box_width + 1,  0, row2,   out_row, width, bpp);
+                }
+            } else
+                gaussian_blur_line (gaussian_matrix, gaussian_matrix_len, in_row, out_row, width, bpp);
+        }
+
+        if (!use_box_blur)
+            g_free (gaussian_matrix);
+
+        g_free (row_buffer);
+
+        out_has_data = TRUE;
+    } else
+        out_has_data = FALSE;
+
+    if (sy != 0.0) {
+        gint box_height;
+        gdouble *gaussian_matrix = NULL;
+        gint gaussian_matrix_len;
+        guchar *col_buffer;
+        guchar *col1, *col2;
+        int x;
+
+        /* twice the size so we can have the source pixels and the blurred pixels */
+        col_buffer = g_new (guchar, height * bpp * 2);
+        col1 = col_buffer;
+        col2 = col_buffer + height * bpp;
+
+        if (use_box_blur) {
+            box_height = compute_box_blur_width (sy);
+        } else
+            make_gaussian_convolution_matrix (sy, &gaussian_matrix, &gaussian_matrix_len);
+
+        for (x = 0; x < width; x++) {
+            if (out_has_data)
+                get_column (col1, out_data, out_stride, bpp, height, x);
+            else
+                get_column (col1, in_data, in_stride, bpp, height, x);
+
+            if (use_box_blur) {
+                if (box_height % 2 != 0) {
+                    /* Odd-width box blur */
+                    box_blur_line (box_height, 0, col1, col2, height, bpp);
+                    box_blur_line (box_height, 0, col2, col1, height, bpp);
+                    box_blur_line (box_height, 0, col1, col2, height, bpp);
+                } else {
+                    /* Even-width box blur */
+                    box_blur_line (box_height,     -1, col1, col2, height, bpp);
+                    box_blur_line (box_height,      1, col2, col1, height, bpp);
+                    box_blur_line (box_height + 1,  0, col1, col2, height, bpp);
+                }
+            } else
+                gaussian_blur_line (gaussian_matrix, gaussian_matrix_len, col1, col2, height, bpp);
+
+            put_column (col2, out_data, out_stride, bpp, height, x);
+        }
+
+        g_free (gaussian_matrix);
+        g_free (col_buffer);
+    }
+
+    cairo_surface_mark_dirty (out);
 }
 
 static void
 rsvg_filter_primitive_gaussian_blur_render (RsvgFilterPrimitive * self, RsvgFilterContext * ctx)
 {
     RsvgFilterPrimitiveGaussianBlur *upself;
-
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    int width, height;
+    cairo_surface_t *output, *in;
     RsvgIRect boundarys;
     gfloat 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.result;
+    in = op.surface;
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8,
-                                       gdk_pixbuf_get_width (in), gdk_pixbuf_get_height (in));
+    width = cairo_image_surface_get_width (in);
+    height = cairo_image_surface_get_height (in);
 
-    /* scale the SD values */
-    sdx = upself->sdx * ctx->paffine[0];
-    sdy = upself->sdy * ctx->paffine[3];
+    output = _rsvg_image_surface_new (width, height);
 
-    fast_blur (in, output, sdx, sdy, boundarys, op);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    op.result = output;
+    /* scale the SD values */
+    sdx = upself->sdx * ctx->paffine.xx;
+    sdy = upself->sdy * ctx->paffine.yy;
+
+    gaussian_blur_surface (in, output, sdx, sdy);
+
+    /* Hard-clip to the filter area */
+    if (!(boundarys.x0 == 0
+          && boundarys.y0 == 0
+          && boundarys.x1 == width
+          && boundarys.y1 == height)) {
+        cr = cairo_create (output);
+        cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+        cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+        cairo_rectangle (cr, 0, 0, width, height);
+        cairo_rectangle (cr,
+                         boundarys.x0, boundarys.y0,
+                         boundarys.x1 - boundarys.x0, boundarys.y1 - boundarys.y0);
+        cairo_fill (cr);
+        cairo_destroy (cr);
+    }
+
+    op.surface = output;
     op.bounds = boundarys;
     rsvg_filter_store_output (self->result, op, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
-}
-
-static void
-rsvg_filter_primitive_gaussian_blur_free (RsvgNode * self)
-{
-    RsvgFilterPrimitiveGaussianBlur *upself;
-
-    upself = (RsvgFilterPrimitiveGaussianBlur *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -1463,15 +1947,15 @@ rsvg_new_filter_primitive_gaussian_blur (void)
 {
     RsvgFilterPrimitiveGaussianBlur *filter;
     filter = g_new (RsvgFilterPrimitiveGaussianBlur, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_GAUSSIAN_BLUR);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
     filter->sdx = 0;
     filter->sdy = 0;
-    filter->super.render = &rsvg_filter_primitive_gaussian_blur_render;
-    filter->super.super.free = &rsvg_filter_primitive_gaussian_blur_free;
+    filter->super.render = rsvg_filter_primitive_gaussian_blur_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_gaussian_blur_set_atts;
     return (RsvgNode *) filter;
 }
@@ -1500,8 +1984,7 @@ rsvg_filter_primitive_offset_render (RsvgFilterPrimitive * self, RsvgFilterConte
     RsvgFilterPrimitiveOutput out;
     RsvgFilterPrimitiveOffset *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
 
     double dx, dy;
     int ox, oy;
@@ -1510,22 +1993,31 @@ rsvg_filter_primitive_offset_render (RsvgFilterPrimitive * self, RsvgFilterConte
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    cairo_surface_flush (in);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    in_pixels = cairo_image_surface_get_data (in);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    rowstride = cairo_image_surface_get_stride (in);
+
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
+
+    output_pixels = cairo_image_surface_get_data (output);
 
     dx = _rsvg_css_normalize_length (&upself->dx, ctx->ctx, 'w');
     dy = _rsvg_css_normalize_length (&upself->dy, ctx->ctx, 'v');
 
-    ox = ctx->paffine[0] * dx + ctx->paffine[2] * dy;
-    oy = ctx->paffine[1] * dx + ctx->paffine[3] * dy;
+    ox = ctx->paffine.xx * dx + ctx->paffine.xy * dy;
+    oy = ctx->paffine.yx * dx + ctx->paffine.yy * dy;
 
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++) {
@@ -1540,28 +2032,15 @@ rsvg_filter_primitive_offset_render (RsvgFilterPrimitive * self, RsvgFilterConte
             }
         }
 
-    out.result = output;
-    out.Rused = 1;
-    out.Gused = 1;
-    out.Bused = 1;
-    out.Aused = 1;
+    cairo_surface_mark_dirty (output);
+
+    out.surface = output;
     out.bounds = boundarys;
 
     rsvg_filter_store_output (self->result, out, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
-}
-
-static void
-rsvg_filter_primitive_offset_free (RsvgNode * self)
-{
-    RsvgFilterPrimitiveOffset *upself;
-
-    upself = (RsvgFilterPrimitiveOffset *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy  (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -1599,15 +2078,15 @@ rsvg_new_filter_primitive_offset (void)
 {
     RsvgFilterPrimitiveOffset *filter;
     filter = g_new (RsvgFilterPrimitiveOffset, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_OFFSET);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
     filter->dy = _rsvg_css_parse_length ("0");
     filter->dx = _rsvg_css_parse_length ("0");
-    filter->super.render = &rsvg_filter_primitive_offset_render;
-    filter->super.super.free = &rsvg_filter_primitive_offset_free;
+    filter->super.render = rsvg_filter_primitive_offset_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_offset_set_atts;
     return (RsvgNode *) filter;
 }
@@ -1629,37 +2108,42 @@ rsvg_filter_primitive_merge_render (RsvgFilterPrimitive * self, RsvgFilterContex
 
     RsvgFilterPrimitiveMerge *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
 
     upself = (RsvgFilterPrimitiveMerge *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, ctx->width, ctx->height);
+    output = _rsvg_image_surface_new (ctx->width, ctx->height);
+    if (output == NULL) {
+        return;
+    }
 
     for (i = 0; i < upself->super.super.children->len; i++) {
         RsvgFilterPrimitive *mn;
         mn = g_ptr_array_index (upself->super.super.children, i);
-        if (strcmp (mn->super.type->str, "feMergeNode"))
+        if (RSVG_NODE_TYPE (&mn->super) != RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE_NODE)
             continue;
         in = rsvg_filter_get_in (mn->in, ctx);
+        if (in == NULL)
+            continue;
+
         rsvg_alpha_blt (in, boundarys.x0, boundarys.y0, boundarys.x1 - boundarys.x0,
                         boundarys.y1 - boundarys.y0, output, boundarys.x0, boundarys.y0);
-        g_object_unref (G_OBJECT (in));
+        cairo_surface_destroy (in);
     }
 
     rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (output));
+    cairo_surface_destroy (output);
 }
 
 static void
 rsvg_filter_primitive_merge_free (RsvgNode * self)
 {
-    RsvgFilterPrimitiveMerge *upself;
+    RsvgFilterPrimitiveMerge *merge;
 
-    upself = (RsvgFilterPrimitiveMerge *) self;
-    g_string_free (upself->super.result, TRUE);
+    merge = (RsvgFilterPrimitiveMerge *) self;
+    g_string_free (merge->super.result, TRUE);
 
     _rsvg_node_free (self);
 }
@@ -1693,12 +2177,12 @@ rsvg_new_filter_primitive_merge (void)
 {
     RsvgFilterPrimitiveMerge *filter;
     filter = g_new (RsvgFilterPrimitiveMerge, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE);
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
-    filter->super.render = &rsvg_filter_primitive_merge_render;
-    filter->super.super.free = &rsvg_filter_primitive_merge_free;
+    filter->super.render = rsvg_filter_primitive_merge_render;
+    filter->super.super.free = rsvg_filter_primitive_merge_free;
 
     filter->super.super.set_atts = rsvg_filter_primitive_merge_set_atts;
     return (RsvgNode *) filter;
@@ -1719,9 +2203,11 @@ rsvg_filter_primitive_merge_node_set_atts (RsvgNode * self,
 static void
 rsvg_filter_primitive_merge_node_free (RsvgNode * self)
 {
-    RsvgFilterPrimitive *upself;
-    upself = (RsvgFilterPrimitive *) self;
-    g_string_free (upself->in, TRUE);
+    RsvgFilterPrimitive *merge_node;
+
+    merge_node = (RsvgFilterPrimitive *) self;
+    g_string_free (merge_node->in, TRUE);
+
     _rsvg_node_free (self);
 }
 
@@ -1736,10 +2222,10 @@ rsvg_new_filter_primitive_merge_node (void)
 {
     RsvgFilterPrimitive *filter;
     filter = g_new (RsvgFilterPrimitive, 1);
-    _rsvg_node_init (&filter->super);
+    _rsvg_node_init (&filter->super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE_NODE);
     filter->in = g_string_new ("none");
     filter->super.free = rsvg_filter_primitive_merge_node_free;
-    filter->render = &rsvg_filter_primitive_merge_node_render;
+    filter->render = rsvg_filter_primitive_merge_node_render;
     filter->super.set_atts = rsvg_filter_primitive_merge_node_set_atts;
     return (RsvgNode *) filter;
 }
@@ -1747,16 +2233,16 @@ rsvg_new_filter_primitive_merge_node (void)
 /*************************************************************/
 /*************************************************************/
 
-typedef struct _RsvgFilterPrimitiveColourMatrix
- RsvgFilterPrimitiveColourMatrix;
+typedef struct _RsvgFilterPrimitiveColorMatrix
+ RsvgFilterPrimitiveColorMatrix;
 
-struct _RsvgFilterPrimitiveColourMatrix {
+struct _RsvgFilterPrimitiveColorMatrix {
     RsvgFilterPrimitive super;
     gint *KernelMatrix;
 };
 
 static void
-rsvg_filter_primitive_colour_matrix_render (RsvgFilterPrimitive * self, RsvgFilterContext * ctx)
+rsvg_filter_primitive_color_matrix_render (RsvgFilterPrimitive * self, RsvgFilterContext * ctx)
 {
     guchar ch;
     gint x, y;
@@ -1767,26 +2253,35 @@ rsvg_filter_primitive_colour_matrix_render (RsvgFilterPrimitive * self, RsvgFilt
     guchar *in_pixels;
     guchar *output_pixels;
 
-    RsvgFilterPrimitiveColourMatrix *upself;
+    RsvgFilterPrimitiveColorMatrix *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
 
     int sum;
 
-    upself = (RsvgFilterPrimitiveColourMatrix *) self;
+    upself = (RsvgFilterPrimitiveColorMatrix *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
+
+    cairo_surface_flush (in);
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    in_pixels = cairo_image_surface_get_data (in);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    rowstride = cairo_image_surface_get_stride (in);
+
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
+
+    output_pixels = cairo_image_surface_get_data (output);
 
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++) {
@@ -1833,35 +2328,35 @@ rsvg_filter_primitive_colour_matrix_render (RsvgFilterPrimitive * self, RsvgFilt
             }
         }
 
+    cairo_surface_mark_dirty (output);
+
     rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
+    cairo_surface_destroy (in);
+    cairo_surface_destroy(output);
 }
 
 static void
-rsvg_filter_primitive_colour_matrix_free (RsvgNode * self)
+rsvg_filter_primitive_color_matrix_free (RsvgNode * self)
 {
-    RsvgFilterPrimitiveColourMatrix *upself;
+    RsvgFilterPrimitiveColorMatrix *matrix;
 
-    upself = (RsvgFilterPrimitiveColourMatrix *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    if (upself->KernelMatrix)
-        g_free (upself->KernelMatrix);
-    _rsvg_node_free (self);
+    matrix = (RsvgFilterPrimitiveColorMatrix *) self;
+    g_free (matrix->KernelMatrix);
+    
+    rsvg_filter_primitive_free (self);
 }
 
 static void
-rsvg_filter_primitive_colour_matrix_set_atts (RsvgNode * self, RsvgHandle * ctx,
+rsvg_filter_primitive_color_matrix_set_atts (RsvgNode * self, RsvgHandle * ctx,
                                               RsvgPropertyBag * atts)
 {
     gint type;
     guint listlen = 0;
     const char *value;
-    RsvgFilterPrimitiveColourMatrix *filter;
+    RsvgFilterPrimitiveColorMatrix *filter;
 
-    filter = (RsvgFilterPrimitiveColourMatrix *) self;
+    filter = (RsvgFilterPrimitiveColorMatrix *) self;
 
     type = 0;
 
@@ -1966,20 +2461,20 @@ rsvg_filter_primitive_colour_matrix_set_atts (RsvgNode * self, RsvgHandle * ctx,
 }
 
 RsvgNode *
-rsvg_new_filter_primitive_colour_matrix (void)
+rsvg_new_filter_primitive_color_matrix (void)
 {
-    RsvgFilterPrimitiveColourMatrix *filter;
-    filter = g_new (RsvgFilterPrimitiveColourMatrix, 1);
-    _rsvg_node_init (&filter->super.super);
+    RsvgFilterPrimitiveColorMatrix *filter;
+    filter = g_new (RsvgFilterPrimitiveColorMatrix, 1);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_COLOR_MATRIX);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
     filter->KernelMatrix = NULL;
-    filter->super.render = &rsvg_filter_primitive_colour_matrix_render;
-    filter->super.super.free = &rsvg_filter_primitive_colour_matrix_free;
+    filter->super.render = rsvg_filter_primitive_color_matrix_render;
+    filter->super.super.free = rsvg_filter_primitive_color_matrix_free;
 
-    filter->super.super.set_atts = rsvg_filter_primitive_colour_matrix_set_atts;
+    filter->super.super.set_atts = rsvg_filter_primitive_color_matrix_set_atts;
     return (RsvgNode *) filter;
 }
 
@@ -2002,8 +2497,9 @@ struct _RsvgNodeComponentTransferFunc {
     gint slope;
     gint intercept;
     gint amplitude;
-    gdouble exponent;
     gint offset;
+    gdouble exponent;
+    char channel;
 };
 
 struct _RsvgFilterPrimitiveComponentTransfer {
@@ -2021,14 +2517,17 @@ table_component_transfer_func (gint C, RsvgNodeComponentTransferFunc * user_data
 {
     guint k;
     gint vk, vk1, distancefromlast;
+    guint num_values;
 
     if (!user_data->nbTableValues)
         return C;
 
-    k = (C * (user_data->nbTableValues - 1)) / 255;
+    num_values = user_data->nbTableValues;
+
+    k = (C * (num_values - 1)) / 255;
 
-    vk = user_data->tableValues[k];
-    vk1 = user_data->tableValues[k + 1];
+    vk = user_data->tableValues[CLAMP (k, 0, num_values - 1)];
+    vk1 = user_data->tableValues[CLAMP (k + 1, 0, num_values - 1)];
 
     distancefromlast = (C * (user_data->nbTableValues - 1)) - k * 255;
 
@@ -2045,7 +2544,7 @@ discrete_component_transfer_func (gint C, RsvgNodeComponentTransferFunc * user_d
 
     k = (C * user_data->nbTableValues) / 255;
 
-    return user_data->tableValues[k];
+    return user_data->tableValues[CLAMP (k, 0, user_data->nbTableValues)];
 }
 
 static gint
@@ -2079,7 +2578,6 @@ rsvg_filter_primitive_component_transfer_render (RsvgFilterPrimitive *
 {
     gint x, y, c;
     guint i;
-    gint temp;
     gint rowstride, height, width;
     RsvgIRect boundarys;
     RsvgNodeComponentTransferFunc *channels[4];
@@ -2088,48 +2586,57 @@ rsvg_filter_primitive_component_transfer_render (RsvgFilterPrimitive *
     gint achan = ctx->channelmap[3];
     guchar *in_pixels;
     guchar *output_pixels;
+    cairo_surface_t *output, *in;
 
-    RsvgFilterPrimitiveComponentTransfer *upself;
-
-    GdkPixbuf *output;
-    GdkPixbuf *in;
-    upself = (RsvgFilterPrimitiveComponentTransfer *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     for (c = 0; c < 4; c++) {
-        char channel = "RGBA"[c];
+        char channel = "rgba"[c]; /* see rsvg_standard_element_start() for where these chars come from */
         for (i = 0; i < self->super.children->len; i++) {
-            RsvgNodeComponentTransferFunc *temp;
-            temp = (RsvgNodeComponentTransferFunc *)
-                g_ptr_array_index (self->super.children, i);
-            if (!strncmp (temp->super.type->str, "feFunc", 6))
-                if (temp->super.type->str[6] == channel) {
+            RsvgNode *child_node;
+
+            child_node = (RsvgNode *) g_ptr_array_index (self->super.children, i);
+            if (RSVG_NODE_TYPE (child_node) == RSVG_NODE_TYPE_COMPONENT_TRANFER_FUNCTION) {
+                RsvgNodeComponentTransferFunc *temp = (RsvgNodeComponentTransferFunc *) child_node;
+
+                if (temp->channel == channel) {
                     functions[ctx->channelmap[c]] = temp->function;
                     channels[ctx->channelmap[c]] = temp;
                     break;
                 }
+            }
         }
         if (i == self->super.children->len)
-            functions[c] = identity_component_transfer_func;
+            functions[ctx->channelmap[c]] = identity_component_transfer_func;
 
     }
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    cairo_surface_flush (in);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    in_pixels = cairo_image_surface_get_data (in);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    rowstride = cairo_image_surface_get_stride (in);
+
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
+
+    output_pixels = cairo_image_surface_get_data (output);
 
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++) {
             inpix = in_pixels + (y * rowstride + x * 4);
             for (c = 0; c < 4; c++) {
+                gint temp;
                 int inval;
                 if (c != achan) {
                     if (inpix[achan] == 0)
@@ -2151,10 +2658,13 @@ rsvg_filter_primitive_component_transfer_render (RsvgFilterPrimitive *
                     outpix[ctx->channelmap[c]] * outpix[achan] / 255;
             output_pixels[y * rowstride + x * 4 + achan] = outpix[achan];
         }
+
+    cairo_surface_mark_dirty (output);
+
     rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -2190,15 +2700,14 @@ rsvg_new_filter_primitive_component_transfer (void)
     RsvgFilterPrimitiveComponentTransfer *filter;
 
     filter = g_new (RsvgFilterPrimitiveComponentTransfer, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_COMPONENT_TRANSFER);
     filter->super.result = g_string_new ("none");
     filter->super.in = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
-    filter->super.render = &rsvg_filter_primitive_component_transfer_render;
-
+    filter->super.render = rsvg_filter_primitive_component_transfer_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_component_transfer_set_atts;
-
     return (RsvgNode *) filter;
 }
 
@@ -2263,12 +2772,13 @@ rsvg_new_node_component_transfer_function (char channel)
 {
     RsvgNodeComponentTransferFunc *filter;
 
-    filter = g_new (RsvgNodeComponentTransferFunc, 1);
-    _rsvg_node_init (&filter->super);
+    filter = g_new0 (RsvgNodeComponentTransferFunc, 1);
+    _rsvg_node_init (&filter->super, RSVG_NODE_TYPE_COMPONENT_TRANFER_FUNCTION);
     filter->super.free = rsvg_component_transfer_function_free;
     filter->super.set_atts = rsvg_node_component_transfer_function_set_atts;
     filter->function = identity_component_transfer_func;
     filter->nbTableValues = 0;
+    filter->channel = channel;
     return (RsvgNode *) filter;
 }
 
@@ -2298,8 +2808,7 @@ rsvg_filter_primitive_erode_render (RsvgFilterPrimitive * self, RsvgFilterContex
 
     RsvgFilterPrimitiveErode *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
 
     gint kx, ky;
     guchar val;
@@ -2308,20 +2817,29 @@ rsvg_filter_primitive_erode_render (RsvgFilterPrimitive * self, RsvgFilterContex
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    cairo_surface_flush (in);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    in_pixels = cairo_image_surface_get_data (in);
+
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
+
+    rowstride = cairo_image_surface_get_stride (in);
 
     /* scale the radius values */
-    kx = upself->rx * ctx->paffine[0];
-    ky = upself->ry * ctx->paffine[3];
+    kx = upself->rx * ctx->paffine.xx;
+    ky = upself->ry * ctx->paffine.yy;
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    output_pixels = cairo_image_surface_get_data (output);
 
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++)
@@ -2349,21 +2867,13 @@ rsvg_filter_primitive_erode_render (RsvgFilterPrimitive * self, RsvgFilterContex
                     }
                 output_pixels[y * rowstride + x * 4 + ch] = extreme;
             }
-    rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
-}
+    cairo_surface_mark_dirty (output);
 
-static void
-rsvg_filter_primitive_erode_free (RsvgNode * self)
-{
-    RsvgFilterPrimitiveErode *upself;
+    rsvg_filter_store_result (self->result, output, ctx);
 
-    upself = (RsvgFilterPrimitiveErode *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -2406,7 +2916,7 @@ rsvg_new_filter_primitive_erode (void)
 {
     RsvgFilterPrimitiveErode *filter;
     filter = g_new (RsvgFilterPrimitiveErode, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_ERODE);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
@@ -2414,8 +2924,8 @@ rsvg_new_filter_primitive_erode (void)
     filter->rx = 0;
     filter->ry = 0;
     filter->mode = 0;
-    filter->super.render = &rsvg_filter_primitive_erode_render;
-    filter->super.super.free = &rsvg_filter_primitive_erode_free;
+    filter->super.render = rsvg_filter_primitive_erode_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_erode_set_atts;
     return (RsvgNode *) filter;
 }
@@ -2451,25 +2961,41 @@ rsvg_filter_primitive_composite_render (RsvgFilterPrimitive * self, RsvgFilterCo
 
     RsvgFilterPrimitiveComposite *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
-    GdkPixbuf *in2;
+    cairo_surface_t *output, *in, *in2;
 
     upself = (RsvgFilterPrimitiveComposite *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
+
+    cairo_surface_flush (in);
+
     in2 = rsvg_filter_get_in (upself->in2, ctx);
-    in2_pixels = gdk_pixbuf_get_pixels (in2);
+    if (in2 == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    cairo_surface_flush (in2);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    in_pixels = cairo_image_surface_get_data (in);
+    in2_pixels = cairo_image_surface_get_data (in2);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
+
+    rowstride = cairo_image_surface_get_stride (in);
+
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        cairo_surface_destroy (in2);
+        return;
+    }
+
+    output_pixels = cairo_image_surface_get_data (output);
 
     if (upself->mode == COMPOSITE_MODE_ARITHMETIC)
         for (y = boundarys.y0; y < boundarys.y1; y++)
@@ -2557,23 +3083,24 @@ rsvg_filter_primitive_composite_render (RsvgFilterPrimitive * self, RsvgFilterCo
                 output_pixels[4 * x + y * rowstride + 3] = qr;
             }
 
+    cairo_surface_mark_dirty (output);
+
     rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (in2));
-    g_object_unref (G_OBJECT (output));
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (in2);
+    cairo_surface_destroy (output);
 }
 
 static void
 rsvg_filter_primitive_composite_free (RsvgNode * self)
 {
-    RsvgFilterPrimitiveComposite *upself;
+    RsvgFilterPrimitiveComposite *composite;
 
-    upself = (RsvgFilterPrimitiveComposite *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    g_string_free (upself->in2, TRUE);
-    _rsvg_node_free (self);
+    composite = (RsvgFilterPrimitiveComposite *) self;
+    g_string_free (composite->in2, TRUE);
+
+    rsvg_filter_primitive_free (self);
 }
 
 static void
@@ -2631,7 +3158,7 @@ rsvg_new_filter_primitive_composite (void)
 {
     RsvgFilterPrimitiveComposite *filter;
     filter = g_new (RsvgFilterPrimitiveComposite, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_COMPOSITE);
     filter->mode = COMPOSITE_MODE_OVER;
     filter->super.in = g_string_new ("none");
     filter->in2 = g_string_new ("none");
@@ -2642,8 +3169,8 @@ rsvg_new_filter_primitive_composite (void)
     filter->k2 = 0;
     filter->k3 = 0;
     filter->k4 = 0;
-    filter->super.render = &rsvg_filter_primitive_composite_render;
-    filter->super.super.free = &rsvg_filter_primitive_composite_free;
+    filter->super.render = rsvg_filter_primitive_composite_render;
+    filter->super.super.free = rsvg_filter_primitive_composite_free;
     filter->super.super.set_atts = rsvg_filter_primitive_composite_set_atts;
     return (RsvgNode *) filter;
 }
@@ -2659,53 +3186,43 @@ rsvg_filter_primitive_flood_render (RsvgFilterPrimitive * self, RsvgFilterContex
     gint rowstride, height, width;
     RsvgIRect boundarys;
     guchar *output_pixels;
-    GdkPixbuf *output;
-    char pixcolour[4];
+    cairo_surface_t *output;
+    char pixcolor[4];
     RsvgFilterPrimitiveOutput out;
 
-    guint32 colour = self->super.state->flood_color;
+    guint32 color = self->super.state->flood_color;
     guint8 opacity = self->super.state->flood_opacity;
 
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     height = ctx->height;
     width = ctx->width;
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
-    rowstride = gdk_pixbuf_get_rowstride (output);
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL)
+        return;
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    rowstride = cairo_image_surface_get_stride (output);
+
+    output_pixels = cairo_image_surface_get_data (output);
 
     for (i = 0; i < 3; i++)
-        pixcolour[i] = (int) (((unsigned char *)
-                               (&colour))[2 - i]) * opacity / 255;
-    pixcolour[3] = opacity;
+        pixcolor[i] = (int) (((unsigned char *)
+                               (&color))[2 - i]) * opacity / 255;
+    pixcolor[3] = opacity;
 
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++)
             for (i = 0; i < 4; i++)
-                output_pixels[4 * x + y * rowstride + ctx->channelmap[i]] = pixcolour[i];
+                output_pixels[4 * x + y * rowstride + ctx->channelmap[i]] = pixcolor[i];
+
+    cairo_surface_mark_dirty (output);
 
-    out.result = output;
-    out.Rused = 1;
-    out.Gused = 1;
-    out.Bused = 1;
-    out.Aused = 1;
+    out.surface = output;
     out.bounds = boundarys;
 
     rsvg_filter_store_output (self->result, out, ctx);
 
-    g_object_unref (G_OBJECT (output));
-}
-
-static void
-rsvg_filter_primitive_flood_free (RsvgNode * self)
-{
-    RsvgFilterPrimitive *upself;
-
-    upself = (RsvgFilterPrimitive *) self;
-    g_string_free (upself->result, TRUE);
-    g_string_free (upself->in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -2736,12 +3253,12 @@ rsvg_new_filter_primitive_flood (void)
 {
     RsvgFilterPrimitive *filter;
     filter = g_new (RsvgFilterPrimitive, 1);
-    _rsvg_node_init (&filter->super);
+    _rsvg_node_init (&filter->super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_FLOOD);
     filter->in = g_string_new ("none");
     filter->result = g_string_new ("none");
     filter->x.factor = filter->y.factor = filter->width.factor = filter->height.factor = 'n';
-    filter->render = &rsvg_filter_primitive_flood_render;
-    filter->super.free = &rsvg_filter_primitive_flood_free;
+    filter->render = rsvg_filter_primitive_flood_render;
+    filter->super.free = rsvg_filter_primitive_free;
     filter->super.set_atts = rsvg_filter_primitive_flood_set_atts;
     return (RsvgNode *) filter;
 }
@@ -2773,9 +3290,7 @@ rsvg_filter_primitive_displacement_map_render (RsvgFilterPrimitive * self, RsvgF
 
     RsvgFilterPrimitiveDisplacementMap *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
-    GdkPixbuf *in2;
+    cairo_surface_t *output, *in, *in2;
 
     double ox, oy;
 
@@ -2783,19 +3298,35 @@ rsvg_filter_primitive_displacement_map_render (RsvgFilterPrimitive * self, RsvgF
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
+
+    cairo_surface_flush (in);
 
     in2 = rsvg_filter_get_in (upself->in2, ctx);
-    in2_pixels = gdk_pixbuf_get_pixels (in2);
+    if (in2 == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    cairo_surface_flush (in2);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    in_pixels = cairo_image_surface_get_data (in);
+    in2_pixels = cairo_image_surface_get_data (in2);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    rowstride = cairo_image_surface_get_stride (in);
+
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        cairo_surface_destroy (in2);
+        return;
+    }
+
+    output_pixels = cairo_image_surface_get_data (output);
 
     switch (upself->xChannelSelector) {
     case 'R':
@@ -2811,8 +3342,9 @@ rsvg_filter_primitive_displacement_map_render (RsvgFilterPrimitive * self, RsvgF
         xch = 3;
         break;
     default:
-        xch = 4;
-    };
+        xch = 0;
+        break;
+    }
 
     switch (upself->yChannelSelector) {
     case 'R':
@@ -2828,48 +3360,50 @@ rsvg_filter_primitive_displacement_map_render (RsvgFilterPrimitive * self, RsvgF
         ych = 3;
         break;
     default:
-        ych = 4;
-    };
+        ych = 1;
+        break;
+    }
 
     xch = ctx->channelmap[xch];
     ych = ctx->channelmap[ych];
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++) {
             if (xch != 4)
-                ox = x + upself->scale * ctx->paffine[0] *
+                ox = x + upself->scale * ctx->paffine.xx *
                     ((double) in2_pixels[y * rowstride + x * 4 + xch] / 255.0 - 0.5);
             else
                 ox = x;
 
             if (ych != 4)
-                oy = y + upself->scale * ctx->paffine[3] *
+                oy = y + upself->scale * ctx->paffine.yy *
                     ((double) in2_pixels[y * rowstride + x * 4 + ych] / 255.0 - 0.5);
             else
                 oy = y;
 
             for (ch = 0; ch < 4; ch++) {
                 output_pixels[y * rowstride + x * 4 + ch] =
-                    gdk_pixbuf_get_interp_pixel (in_pixels, ox, oy, ch, boundarys, rowstride);
+                    get_interp_pixel (in_pixels, ox, oy, ch, boundarys, rowstride);
             }
         }
 
+    cairo_surface_mark_dirty (output);
+
     rsvg_filter_store_result (self->result, output, ctx);
 
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (in2));
-    g_object_unref (G_OBJECT (output));
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (in2);
+    cairo_surface_destroy (output);
 }
 
 static void
 rsvg_filter_primitive_displacement_map_free (RsvgNode * self)
 {
-    RsvgFilterPrimitiveDisplacementMap *upself;
+    RsvgFilterPrimitiveDisplacementMap *dmap;
 
-    upself = (RsvgFilterPrimitiveDisplacementMap *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    g_string_free (upself->in2, TRUE);
-    _rsvg_node_free (self);
+    dmap = (RsvgFilterPrimitiveDisplacementMap *) self;
+    g_string_free (dmap->in2, TRUE);
+
+    rsvg_filter_primitive_free (self);
 }
 
 static void
@@ -2908,11 +3442,11 @@ rsvg_filter_primitive_displacement_map_set_atts (RsvgNode * self, RsvgHandle * c
 }
 
 RsvgNode *
-rsvg_new_filter_primitive_displacement_map ()
+rsvg_new_filter_primitive_displacement_map (void)
 {
     RsvgFilterPrimitiveDisplacementMap *filter;
     filter = g_new (RsvgFilterPrimitiveDisplacementMap, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_DISPLACEMENT_MAP);
     filter->super.in = g_string_new ("none");
     filter->in2 = g_string_new ("none");
     filter->super.result = g_string_new ("none");
@@ -2921,8 +3455,8 @@ rsvg_new_filter_primitive_displacement_map ()
     filter->xChannelSelector = ' ';
     filter->yChannelSelector = ' ';
     filter->scale = 0;
-    filter->super.render = &rsvg_filter_primitive_displacement_map_render;
-    filter->super.super.free = &rsvg_filter_primitive_displacement_map_free;
+    filter->super.render = rsvg_filter_primitive_displacement_map_render;
+    filter->super.super.free = rsvg_filter_primitive_displacement_map_free;
     filter->super.super.set_atts = rsvg_filter_primitive_displacement_map_set_atts;
     return (RsvgNode *) filter;
 }
@@ -3172,14 +3706,22 @@ rsvg_filter_primitive_turbulence_render (RsvgFilterPrimitive * self, RsvgFilterC
     gint x, y, tileWidth, tileHeight, rowstride, width, height;
     RsvgIRect boundarys;
     guchar *output_pixels;
-    GdkPixbuf *output;
-    gdouble affine[6];
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
+    cairo_matrix_t affine;
+
+    affine = ctx->paffine;
+    if (cairo_matrix_invert (&affine) != CAIRO_STATUS_SUCCESS)
+      return;
 
     in = rsvg_filter_get_in (self->in, ctx);
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    if (in == NULL)
+        return;
+
+    cairo_surface_flush (in);
+
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
+    rowstride = cairo_image_surface_get_stride (in);
 
     upself = (RsvgFilterPrimitiveTurbulence *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
@@ -3187,18 +3729,21 @@ rsvg_filter_primitive_turbulence_render (RsvgFilterPrimitive * self, RsvgFilterC
     tileWidth = (boundarys.x1 - boundarys.x0);
     tileHeight = (boundarys.y1 - boundarys.y0);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    _rsvg_affine_invert (affine, ctx->paffine);
+    output_pixels = cairo_image_surface_get_data (output);
 
     for (y = 0; y < tileHeight; y++) {
         for (x = 0; x < tileWidth; x++) {
             gint i;
             double point[2];
             guchar *pixel;
-            point[0] = affine[0] * (x + boundarys.x0) + affine[2] * (y + boundarys.y0) + affine[4];
-            point[1] = affine[1] * (x + boundarys.x0) + affine[3] * (y + boundarys.y0) + affine[5];
+            point[0] = affine.xx * (x + boundarys.x0) + affine.xy * (y + boundarys.y0) + affine.x0;
+            point[1] = affine.yx * (x + boundarys.x0) + affine.yy * (y + boundarys.y0) + affine.y0;
 
             pixel = output_pixels + 4 * (x + boundarys.x0) + (y + boundarys.y0) * rowstride;
 
@@ -3224,21 +3769,12 @@ rsvg_filter_primitive_turbulence_render (RsvgFilterPrimitive * self, RsvgFilterC
         }
     }
 
-    rsvg_filter_store_result (self->result, output, ctx);
-
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
-}
+    cairo_surface_mark_dirty (output);
 
-static void
-rsvg_filter_primitive_turbulence_free (RsvgNode * self)
-{
-    RsvgFilterPrimitiveTurbulence *upself;
+    rsvg_filter_store_result (self->result, output, ctx);
 
-    upself = (RsvgFilterPrimitiveTurbulence *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -3283,7 +3819,7 @@ rsvg_new_filter_primitive_turbulence (void)
 {
     RsvgFilterPrimitiveTurbulence *filter;
     filter = g_new (RsvgFilterPrimitiveTurbulence, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_TURBULENCE);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
@@ -3295,8 +3831,8 @@ rsvg_new_filter_primitive_turbulence (void)
     filter->bDoStitching = 0;
     filter->bFractalSum = 0;
     feTurbulence_init (filter);
-    filter->super.render = &rsvg_filter_primitive_turbulence_render;
-    filter->super.super.free = &rsvg_filter_primitive_turbulence_free;
+    filter->super.render = rsvg_filter_primitive_turbulence_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_turbulence_set_atts;
     return (RsvgNode *) filter;
 }
@@ -3313,13 +3849,13 @@ struct _RsvgFilterPrimitiveImage {
     GString *href;
 };
 
-static GdkPixbuf *
+static cairo_surface_t *
 rsvg_filter_primitive_image_render_in (RsvgFilterPrimitive * self, RsvgFilterContext * context)
 {
     RsvgDrawingCtx *ctx;
     RsvgFilterPrimitiveImage *upself;
-    int i;
     RsvgNode *drawable;
+    cairo_surface_t *result;
 
     ctx = context->ctx;
 
@@ -3328,27 +3864,30 @@ rsvg_filter_primitive_image_render_in (RsvgFilterPrimitive * self, RsvgFilterCon
     if (!upself->href)
         return NULL;
 
-    drawable = rsvg_defs_lookup (ctx->defs, upself->href->str);
+    drawable = rsvg_acquire_node (ctx, upself->href->str);
     if (!drawable)
         return NULL;
 
-    for (i = 0; i < 6; i++)
-        rsvg_state_current (ctx)->affine[i] = context->paffine[i];
+    rsvg_current_state (ctx)->affine = context->paffine;
 
-    return rsvg_get_image_of_node (ctx, drawable, context->width, context->height);
+    result = rsvg_get_surface_of_node (ctx, drawable, context->width, context->height);
+
+    rsvg_release_node (ctx, drawable);
+
+    return result;
 }
 
-static GdkPixbuf *
+static cairo_surface_t *
 rsvg_filter_primitive_image_render_ext (RsvgFilterPrimitive * self, RsvgFilterContext * ctx)
 {
     RsvgIRect boundarys;
     RsvgFilterPrimitiveImage *upself;
-    GdkPixbuf *img;
+    cairo_surface_t *img, *intermediate;
     int i;
-    GdkPixbuf *intermediate;
     unsigned char *pixels;
     int channelmap[4];
     int length;
+    int width, height;
 
     upself = (RsvgFilterPrimitiveImage *) self;
 
@@ -3357,34 +3896,35 @@ rsvg_filter_primitive_image_render_ext (RsvgFilterPrimitive * self, RsvgFilterCo
 
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
-    img = rsvg_pixbuf_new_from_href (upself->href->str,
-                                     rsvg_handle_get_base_uri (upself->ctx), NULL);
+    width = boundarys.x1 - boundarys.x0;
+    height = boundarys.y1 - boundarys.y0;
+    if (width == 0 || height == 0)
+        return NULL;
 
+    img = rsvg_cairo_surface_new_from_href (upself->ctx,
+                                            upself->href->str,
+                                            NULL);
     if (!img)
         return NULL;
 
-
-    intermediate = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 1, 8, boundarys.x1 - boundarys.x0,
-                                   boundarys.y1 - boundarys.y0);
-
-
-    rsvg_art_affine_image (img, intermediate,
-                           ctx->paffine,
-                           (boundarys.x1 - boundarys.x0) / ctx->paffine[0],
-                           (boundarys.y1 - boundarys.y0) / ctx->paffine[3]);
-
-    if (!intermediate) {
-        g_object_unref (G_OBJECT (img));
+    intermediate = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    if (cairo_surface_status (intermediate) != CAIRO_STATUS_SUCCESS ||
+        !rsvg_art_affine_image (img, intermediate,
+                                &ctx->paffine,
+                                (gdouble) width / ctx->paffine.xx,
+                                (gdouble) height / ctx->paffine.yy)) {
+        cairo_surface_destroy (intermediate);
+        cairo_surface_destroy (img);
         return NULL;
     }
 
+    cairo_surface_destroy (img);
 
-    g_object_unref (G_OBJECT (img));
-
-    length = gdk_pixbuf_get_height (intermediate) * gdk_pixbuf_get_rowstride (intermediate);
+    length = cairo_image_surface_get_height (intermediate) * 
+             cairo_image_surface_get_stride (intermediate);
     for (i = 0; i < 4; i++)
         channelmap[i] = ctx->channelmap[i];
-    pixels = gdk_pixbuf_get_pixels (intermediate);
+    pixels = cairo_image_surface_get_data (intermediate);
     for (i = 0; i < length; i += 4) {
         unsigned char alpha;
         unsigned char pixel[4];
@@ -3402,8 +3942,8 @@ rsvg_filter_primitive_image_render_ext (RsvgFilterPrimitive * self, RsvgFilterCo
             pixels[i + ch] = pixel[ch];
     }
 
+    cairo_surface_mark_dirty (intermediate);
     return intermediate;
-
 }
 
 static void
@@ -3412,8 +3952,9 @@ rsvg_filter_primitive_image_render (RsvgFilterPrimitive * self, RsvgFilterContex
     RsvgIRect boundarys;
     RsvgFilterPrimitiveImage *upself;
     RsvgFilterPrimitiveOutput op;
+    int x, y;
 
-    GdkPixbuf *output, *img;
+    cairo_surface_t *output, *img;
 
     upself = (RsvgFilterPrimitiveImage *) self;
 
@@ -3422,48 +3963,53 @@ rsvg_filter_primitive_image_render (RsvgFilterPrimitive * self, RsvgFilterContex
 
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, ctx->width, ctx->height);
+    output = _rsvg_image_surface_new (ctx->width, ctx->height);
+    if (output == NULL)
+        return;
 
     img = rsvg_filter_primitive_image_render_in (self, ctx);
     if (img == NULL) {
         img = rsvg_filter_primitive_image_render_ext (self, ctx);
-        if (img) {
-            gdk_pixbuf_copy_area (img, 0, 0,
-                                  boundarys.x1 - boundarys.x0,
-                                  boundarys.y1 - boundarys.y0, output, boundarys.x0, boundarys.y0);
-            g_object_unref (G_OBJECT (img));
-        }
+        x = y = 0;
     } else {
-        gdk_pixbuf_copy_area (img, boundarys.x0, boundarys.y0, boundarys.x1 - boundarys.x0,
-                              boundarys.y1 - boundarys.y0, output, boundarys.x0, boundarys.y0);
-        g_object_unref (G_OBJECT (img));
+        x = boundarys.x0;
+        y = boundarys.y0;
+    }
+    if (img) {
+        cairo_t *cr;
+
+        cr = cairo_create (output);
+        cairo_set_source_surface (cr, img, x, y);
+        cairo_rectangle (cr, 0, 0,
+                         boundarys.x1 - boundarys.x0,
+                         boundarys.y1 - boundarys.y0);
+        cairo_clip (cr);
+        cairo_translate (cr, -boundarys.x0, -boundarys.y0);
+        cairo_paint (cr);
+        cairo_destroy (cr);
+
+        cairo_surface_destroy (img);
     }
 
-    op.result = output;
+    op.surface = output;
     op.bounds = boundarys;
-    op.Rused = 1;
-    op.Gused = 1;
-    op.Bused = 1;
-    op.Aused = 1;
 
     rsvg_filter_store_output (self->result, op, ctx);
 
-    g_object_unref (G_OBJECT (output));
+    cairo_surface_destroy (output);
 }
 
 static void
 rsvg_filter_primitive_image_free (RsvgNode * self)
 {
-    RsvgFilterPrimitiveImage *upself;
+    RsvgFilterPrimitiveImage *image;
 
-    upself = (RsvgFilterPrimitiveImage *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
+    image = (RsvgFilterPrimitiveImage *) self;
 
-    if (upself->href)
-        g_string_free (upself->href, TRUE);
+    if (image->href)
+        g_string_free (image->href, TRUE);
 
-    _rsvg_node_free (self);
+    rsvg_filter_primitive_free (self);
 }
 
 static void
@@ -3502,14 +4048,15 @@ rsvg_new_filter_primitive_image (void)
 {
     RsvgFilterPrimitiveImage *filter;
     filter = g_new (RsvgFilterPrimitiveImage, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_IMAGE);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
-    filter->super.render = &rsvg_filter_primitive_image_render;
-    filter->super.super.free = &rsvg_filter_primitive_image_free;
+    filter->super.render = rsvg_filter_primitive_image_render;
+    filter->super.super.free = rsvg_filter_primitive_image_free;
     filter->super.super.set_atts = rsvg_filter_primitive_image_set_atts;
+    filter->href = NULL;
     return (RsvgNode *) filter;
 }
 
@@ -3703,59 +4250,59 @@ get_surface_normal (guchar * I, RsvgIRect boundarys, gint x, gint y,
 
     Nx = -surfaceScale * factorx * ((gdouble)
                                     (Kx[0] *
-                                     gdk_pixbuf_get_interp_pixel (I, x - dx, y - dy, chan,
+                                     get_interp_pixel (I, x - dx, y - dy, chan,
                                                                   boundarys,
                                                                   rowstride) +
-                                     Kx[1] * gdk_pixbuf_get_interp_pixel (I, x, y - dy, chan,
+                                     Kx[1] * get_interp_pixel (I, x, y - dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Kx[2] * gdk_pixbuf_get_interp_pixel (I, x + dx, y - dy, chan,
+                                     Kx[2] * get_interp_pixel (I, x + dx, y - dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Kx[3] * gdk_pixbuf_get_interp_pixel (I, x - dx, y, chan,
+                                     Kx[3] * get_interp_pixel (I, x - dx, y, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Kx[4] * gdk_pixbuf_get_interp_pixel (I, x, y, chan, boundarys,
+                                     Kx[4] * get_interp_pixel (I, x, y, chan, boundarys,
                                                                           rowstride) +
-                                     Kx[5] * gdk_pixbuf_get_interp_pixel (I, x + dx, y, chan,
+                                     Kx[5] * get_interp_pixel (I, x + dx, y, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Kx[6] * gdk_pixbuf_get_interp_pixel (I, x - dx, y + dy, chan,
+                                     Kx[6] * get_interp_pixel (I, x - dx, y + dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Kx[7] * gdk_pixbuf_get_interp_pixel (I, x, y + dy, chan,
+                                     Kx[7] * get_interp_pixel (I, x, y + dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Kx[8] * gdk_pixbuf_get_interp_pixel (I, x + dx, y + dy, chan,
+                                     Kx[8] * get_interp_pixel (I, x + dx, y + dy, chan,
                                                                           boundarys,
                                                                           rowstride))) / 255.0;
 
     Ny = -surfaceScale * factory * ((gdouble)
                                     (Ky[0] *
-                                     gdk_pixbuf_get_interp_pixel (I, x - dx, y - dy, chan,
+                                     get_interp_pixel (I, x - dx, y - dy, chan,
                                                                   boundarys,
                                                                   rowstride) +
-                                     Ky[1] * gdk_pixbuf_get_interp_pixel (I, x, y - dy, chan,
+                                     Ky[1] * get_interp_pixel (I, x, y - dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Ky[2] * gdk_pixbuf_get_interp_pixel (I, x + dx, y - dy, chan,
+                                     Ky[2] * get_interp_pixel (I, x + dx, y - dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Ky[3] * gdk_pixbuf_get_interp_pixel (I, x - dx, y, chan,
+                                     Ky[3] * get_interp_pixel (I, x - dx, y, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Ky[4] * gdk_pixbuf_get_interp_pixel (I, x, y, chan, boundarys,
+                                     Ky[4] * get_interp_pixel (I, x, y, chan, boundarys,
                                                                           rowstride) +
-                                     Ky[5] * gdk_pixbuf_get_interp_pixel (I, x + dx, y, chan,
+                                     Ky[5] * get_interp_pixel (I, x + dx, y, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Ky[6] * gdk_pixbuf_get_interp_pixel (I, x - dx, y + dy, chan,
+                                     Ky[6] * get_interp_pixel (I, x - dx, y + dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Ky[7] * gdk_pixbuf_get_interp_pixel (I, x, y + dy, chan,
+                                     Ky[7] * get_interp_pixel (I, x, y + dy, chan,
                                                                           boundarys,
                                                                           rowstride) +
-                                     Ky[8] * gdk_pixbuf_get_interp_pixel (I, x + dx, y + dy, chan,
+                                     Ky[8] * get_interp_pixel (I, x + dx, y + dy, chan,
                                                                           boundarys,
                                                                           rowstride))) / 255.0;
 
@@ -3785,7 +4332,7 @@ struct _RsvgNodeLightSource {
 
 static vector3
 get_light_direction (RsvgNodeLightSource * source, gdouble x1, gdouble y1, gdouble z,
-                     gdouble * affine, RsvgDrawingCtx * ctx)
+                     cairo_matrix_t *affine, RsvgDrawingCtx * ctx)
 {
     vector3 output;
 
@@ -3798,8 +4345,8 @@ get_light_direction (RsvgNodeLightSource * source, gdouble x1, gdouble y1, gdoub
     default:
         {
             double x, y;
-            x = affine[0] * x1 + affine[2] * y1 + affine[4];
-            y = affine[1] * x1 + affine[3] * y1 + affine[5];
+            x = affine->xx * x1 + affine->xy * y1 + affine->x0;
+            y = affine->yx * x1 + affine->yy * y1 + affine->y0;
             output.x = _rsvg_css_normalize_length (&source->x, ctx, 'h') - x;
             output.y = _rsvg_css_normalize_length (&source->y, ctx, 'v') - y;
             output.z = _rsvg_css_normalize_length (&source->z, ctx, 'o') - z;
@@ -3811,8 +4358,8 @@ get_light_direction (RsvgNodeLightSource * source, gdouble x1, gdouble y1, gdoub
 }
 
 static vector3
-get_light_colour (RsvgNodeLightSource * source, vector3 colour,
-                  gdouble x1, gdouble y1, gdouble z, gdouble * affine, RsvgDrawingCtx * ctx)
+get_light_color (RsvgNodeLightSource * source, vector3 color,
+                  gdouble x1, gdouble y1, gdouble z, cairo_matrix_t *affine, RsvgDrawingCtx * ctx)
 {
     double base, angle, x, y;
     vector3 s;
@@ -3821,7 +4368,7 @@ get_light_colour (RsvgNodeLightSource * source, vector3 colour,
     double sx, sy, sz, spx, spy, spz;
 
     if (source->type != SPOTLIGHT)
-        return colour;
+        return color;
 
     sx = _rsvg_css_normalize_length (&source->x, ctx, 'h');
     sy = _rsvg_css_normalize_length (&source->y, ctx, 'v');
@@ -3830,8 +4377,8 @@ get_light_colour (RsvgNodeLightSource * source, vector3 colour,
     spy = _rsvg_css_normalize_length (&source->pointsAtY, ctx, 'v');
     spz = _rsvg_css_normalize_length (&source->pointsAtZ, ctx, 'o');
 
-    x = affine[0] * x1 + affine[2] * y1 + affine[4];
-    y = affine[1] * x1 + affine[3] * y1 + affine[5];
+    x = affine->xx * x1 + affine->xy * y1 + affine->x0;
+    y = affine->yx * x1 + affine->yy * y1 + affine->y0;
 
     L.x = sx - x;
     L.y = sy - y;
@@ -3845,7 +4392,7 @@ get_light_colour (RsvgNodeLightSource * source, vector3 colour,
 
     base = -dotproduct (L, s);
 
-    angle = acos (base) * 180.0 / M_PI;
+    angle = acos (base);
 
     if (base < 0 || angle > source->limitingconeAngle) {
         output.x = 0;
@@ -3854,17 +4401,17 @@ get_light_colour (RsvgNodeLightSource * source, vector3 colour,
         return output;
     }
 
-    output.x = colour.x * pow (base, source->specularExponent);
-    output.y = colour.y * pow (base, source->specularExponent);
-    output.z = colour.z * pow (base, source->specularExponent);
+    output.x = color.x * pow (base, source->specularExponent);
+    output.y = color.y * pow (base, source->specularExponent);
+    output.z = color.z * pow (base, source->specularExponent);
 
     return output;
 }
 
 
 static void
-rsvg_filter_primitive_light_source_set_atts (RsvgNode * self,
-                                             RsvgHandle * ctx, RsvgPropertyBag * atts)
+rsvg_node_light_source_set_atts (RsvgNode * self,
+                                 RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
     RsvgNodeLightSource *data;
     const char *value;
@@ -3877,7 +4424,7 @@ rsvg_filter_primitive_light_source_set_atts (RsvgNode * self,
         if ((value = rsvg_property_bag_lookup (atts, "elevation")))
             data->elevation = rsvg_css_parse_angle (value) / 180.0 * M_PI;
         if ((value = rsvg_property_bag_lookup (atts, "limitingConeAngle")))
-            data->limitingconeAngle = rsvg_css_parse_angle (value);
+            data->limitingconeAngle = rsvg_css_parse_angle (value) / 180.0 * M_PI;
         if ((value = rsvg_property_bag_lookup (atts, "x")))
             data->x = data->pointsAtX = _rsvg_css_parse_length (value);
         if ((value = rsvg_property_bag_lookup (atts, "y")))
@@ -3896,13 +4443,13 @@ rsvg_filter_primitive_light_source_set_atts (RsvgNode * self,
 }
 
 RsvgNode *
-rsvg_new_filter_primitive_light_source (char type)
+rsvg_new_node_light_source (char type)
 {
     RsvgNodeLightSource *data;
     data = g_new (RsvgNodeLightSource, 1);
-    _rsvg_node_init (&data->super);
+    _rsvg_node_init (&data->super, RSVG_NODE_TYPE_LIGHT_SOURCE);
     data->super.free = _rsvg_node_free;
-    data->super.set_atts = rsvg_filter_primitive_light_source_set_atts;
+    data->super.set_atts = rsvg_node_light_source_set_atts;
     data->specularExponent = 1;
     if (type == 's')
         data->type = SPOTLIGHT;
@@ -3924,7 +4471,7 @@ struct _RsvgFilterPrimitiveDiffuseLighting {
     gdouble dx, dy;
     double diffuseConstant;
     double surfaceScale;
-    guint32 lightingcolour;
+    guint32 lightingcolor;
 };
 
 static void
@@ -3935,9 +4482,9 @@ rsvg_filter_primitive_diffuse_lighting_render (RsvgFilterPrimitive * self, RsvgF
     gdouble z;
     gint rowstride, height, width;
     gdouble factor, surfaceScale;
-    vector3 lightcolour, L, N;
-    vector3 colour;
-    gdouble iaffine[6];
+    vector3 lightcolor, L, N;
+    vector3 color;
+    cairo_matrix_t iaffine;
     RsvgNodeLightSource *source = NULL;
     RsvgIRect boundarys;
 
@@ -3946,38 +4493,52 @@ rsvg_filter_primitive_diffuse_lighting_render (RsvgFilterPrimitive * self, RsvgF
 
     RsvgFilterPrimitiveDiffuseLighting *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
+
     unsigned int i;
 
     for (i = 0; i < self->super.children->len; i++) {
         RsvgNode *temp;
+
         temp = g_ptr_array_index (self->super.children, i);
-        if (!strcmp (temp->type->str, "feDistantLight") ||
-            !strcmp (temp->type->str, "fePointLight") || !strcmp (temp->type->str, "feSpotLight"))
+        if (RSVG_NODE_TYPE (temp) == RSVG_NODE_TYPE_LIGHT_SOURCE) {
             source = (RsvgNodeLightSource *) temp;
+        }
     }
     if (source == NULL)
         return;
 
+    iaffine = ctx->paffine;
+    if (cairo_matrix_invert (&iaffine) != CAIRO_STATUS_SUCCESS)
+      return;
+
     upself = (RsvgFilterPrimitiveDiffuseLighting *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    cairo_surface_flush (in);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    in_pixels = cairo_image_surface_get_data (in);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    rowstride = cairo_image_surface_get_stride (in);
 
-    colour.x = ((guchar *) (&upself->lightingcolour))[2] / 255.0;
-    colour.y = ((guchar *) (&upself->lightingcolour))[1] / 255.0;
-    colour.z = ((guchar *) (&upself->lightingcolour))[0] / 255.0;
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
+
+    output_pixels = cairo_image_surface_get_data (output);
+
+    color.x = ((guchar *) (&upself->lightingcolor))[2] / 255.0;
+    color.y = ((guchar *) (&upself->lightingcolor))[1] / 255.0;
+    color.z = ((guchar *) (&upself->lightingcolor))[0] / 255.0;
 
     surfaceScale = upself->surfaceScale / 255.0;
 
@@ -3987,48 +4548,37 @@ rsvg_filter_primitive_diffuse_lighting_render (RsvgFilterPrimitive * self, RsvgF
         rawdx = 1;
         rawdy = 1;
     } else {
-        dx = upself->dx * ctx->paffine[0];
-        dy = upself->dy * ctx->paffine[3];
+        dx = upself->dx * ctx->paffine.xx;
+        dy = upself->dy * ctx->paffine.yy;
         rawdx = upself->dx;
         rawdy = upself->dy;
     }
 
-    _rsvg_affine_invert (iaffine, ctx->paffine);
-
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++) {
             z = surfaceScale * (double) in_pixels[y * rowstride + x * 4 + ctx->channelmap[3]];
-            L = get_light_direction (source, x, y, z, iaffine, ctx->ctx);
+            L = get_light_direction (source, x, y, z, &iaffine, ctx->ctx);
             N = get_surface_normal (in_pixels, boundarys, x, y,
                                     dx, dy, rawdx, rawdy, upself->surfaceScale,
                                     rowstride, ctx->channelmap[3]);
-            lightcolour = get_light_colour (source, colour, x, y, z, iaffine, ctx->ctx);
+            lightcolor = get_light_color (source, color, x, y, z, &iaffine, ctx->ctx);
             factor = dotproduct (N, L);
 
             output_pixels[y * rowstride + x * 4 + ctx->channelmap[0]] =
-                MAX (0, MIN (255, upself->diffuseConstant * factor * lightcolour.x * 255.0));
+                MAX (0, MIN (255, upself->diffuseConstant * factor * lightcolor.x * 255.0));
             output_pixels[y * rowstride + x * 4 + ctx->channelmap[1]] =
-                MAX (0, MIN (255, upself->diffuseConstant * factor * lightcolour.y * 255.0));
+                MAX (0, MIN (255, upself->diffuseConstant * factor * lightcolor.y * 255.0));
             output_pixels[y * rowstride + x * 4 + ctx->channelmap[2]] =
-                MAX (0, MIN (255, upself->diffuseConstant * factor * lightcolour.z * 255.0));
+                MAX (0, MIN (255, upself->diffuseConstant * factor * lightcolor.z * 255.0));
             output_pixels[y * rowstride + x * 4 + ctx->channelmap[3]] = 255;
         }
 
-    rsvg_filter_store_result (self->result, output, ctx);
-
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
-}
+    cairo_surface_mark_dirty (output);
 
-static void
-rsvg_filter_primitive_diffuse_lighting_free (RsvgNode * self)
-{
-    RsvgFilterPrimitiveDiffuseLighting *upself;
+    rsvg_filter_store_result (self->result, output, ctx);
 
-    upself = (RsvgFilterPrimitiveDiffuseLighting *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -4057,7 +4607,7 @@ rsvg_filter_primitive_diffuse_lighting_set_atts (RsvgNode * self, RsvgHandle * c
         if ((value = rsvg_property_bag_lookup (atts, "kernelUnitLength")))
             rsvg_css_parse_number_optional_number (value, &filter->dx, &filter->dy);
         if ((value = rsvg_property_bag_lookup (atts, "lighting-color")))
-            filter->lightingcolour = rsvg_css_parse_color (value, 0);
+            filter->lightingcolor = rsvg_css_parse_color (value, 0);
         if ((value = rsvg_property_bag_lookup (atts, "diffuseConstant")))
             filter->diffuseConstant = g_ascii_strtod (value, NULL);
         if ((value = rsvg_property_bag_lookup (atts, "surfaceScale")))
@@ -4072,7 +4622,7 @@ rsvg_new_filter_primitive_diffuse_lighting (void)
 {
     RsvgFilterPrimitiveDiffuseLighting *filter;
     filter = g_new (RsvgFilterPrimitiveDiffuseLighting, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_DIFFUSE_LIGHTING);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
@@ -4081,9 +4631,9 @@ rsvg_new_filter_primitive_diffuse_lighting (void)
     filter->diffuseConstant = 1;
     filter->dx = 1;
     filter->dy = 1;
-    filter->lightingcolour = 0xFFFFFFFF;
-    filter->super.render = &rsvg_filter_primitive_diffuse_lighting_render;
-    filter->super.super.free = &rsvg_filter_primitive_diffuse_lighting_free;
+    filter->lightingcolor = 0xFFFFFFFF;
+    filter->super.render = rsvg_filter_primitive_diffuse_lighting_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_diffuse_lighting_set_atts;
     return (RsvgNode *) filter;
 }
@@ -4098,7 +4648,7 @@ struct _RsvgFilterPrimitiveSpecularLighting {
     double specularConstant;
     double specularExponent;
     double surfaceScale;
-    guint32 lightingcolour;
+    guint32 lightingcolor;
 };
 
 static void
@@ -4108,9 +4658,9 @@ rsvg_filter_primitive_specular_lighting_render (RsvgFilterPrimitive * self, Rsvg
     gdouble z, surfaceScale;
     gint rowstride, height, width;
     gdouble factor, max, base;
-    vector3 lightcolour, colour;
+    vector3 lightcolor, color;
     vector3 L;
-    gdouble iaffine[6];
+    cairo_matrix_t iaffine;
     RsvgIRect boundarys;
     RsvgNodeLightSource *source = NULL;
 
@@ -4119,66 +4669,76 @@ rsvg_filter_primitive_specular_lighting_render (RsvgFilterPrimitive * self, Rsvg
 
     RsvgFilterPrimitiveSpecularLighting *upself;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
+    cairo_surface_t *output, *in;
 
     unsigned int i;
 
     for (i = 0; i < self->super.children->len; i++) {
         RsvgNode *temp;
         temp = g_ptr_array_index (self->super.children, i);
-        if (!strcmp (temp->type->str, "feDistantLight") ||
-            !strcmp (temp->type->str, "fePointLight") || !strcmp (temp->type->str, "feSpotLight"))
+        if (RSVG_NODE_TYPE (temp) == RSVG_NODE_TYPE_LIGHT_SOURCE) {
             source = (RsvgNodeLightSource *) temp;
+        }
     }
     if (source == NULL)
         return;
 
+    iaffine = ctx->paffine;
+    if (cairo_matrix_invert (&iaffine) != CAIRO_STATUS_SUCCESS)
+      return;
+
     upself = (RsvgFilterPrimitiveSpecularLighting *) self;
     boundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     in = rsvg_filter_get_in (self->in, ctx);
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    if (in == NULL)
+        return;
 
-    height = gdk_pixbuf_get_height (in);
-    width = gdk_pixbuf_get_width (in);
+    cairo_surface_flush (in);
 
-    rowstride = gdk_pixbuf_get_rowstride (in);
+    in_pixels = cairo_image_surface_get_data (in);
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height);
+    height = cairo_image_surface_get_height (in);
+    width = cairo_image_surface_get_width (in);
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    rowstride = cairo_image_surface_get_stride (in);
 
-    colour.x = ((guchar *) (&upself->lightingcolour))[2] / 255.0;
-    colour.y = ((guchar *) (&upself->lightingcolour))[1] / 255.0;
-    colour.z = ((guchar *) (&upself->lightingcolour))[0] / 255.0;
+    output = _rsvg_image_surface_new (width, height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    surfaceScale = upself->surfaceScale / 255.0;
+    output_pixels = cairo_image_surface_get_data (output);
+
+    color.x = ((guchar *) (&upself->lightingcolor))[2] / 255.0;
+    color.y = ((guchar *) (&upself->lightingcolor))[1] / 255.0;
+    color.z = ((guchar *) (&upself->lightingcolor))[0] / 255.0;
 
-    _rsvg_affine_invert (iaffine, ctx->paffine);
+    surfaceScale = upself->surfaceScale / 255.0;
 
     for (y = boundarys.y0; y < boundarys.y1; y++)
         for (x = boundarys.x0; x < boundarys.x1; x++) {
             z = in_pixels[y * rowstride + x * 4 + 3] * surfaceScale;
-            L = get_light_direction (source, x, y, z, iaffine, ctx->ctx);
+            L = get_light_direction (source, x, y, z, &iaffine, ctx->ctx);
             L.z += 1;
             L = normalise (L);
 
-            lightcolour = get_light_colour (source, colour, x, y, z, iaffine, ctx->ctx);
+            lightcolor = get_light_color (source, color, x, y, z, &iaffine, ctx->ctx);
             base = dotproduct (get_surface_normal (in_pixels, boundarys, x, y,
-                                                   1, 1, 1.0 / ctx->paffine[0],
-                                                   1.0 / ctx->paffine[3], upself->surfaceScale,
+                                                   1, 1, 1.0 / ctx->paffine.xx,
+                                                   1.0 / ctx->paffine.yy, upself->surfaceScale,
                                                    rowstride, ctx->channelmap[3]), L);
 
             factor = upself->specularConstant * pow (base, upself->specularExponent) * 255;
 
             max = 0;
-            if (max < lightcolour.x)
-                max = lightcolour.x;
-            if (max < lightcolour.y)
-                max = lightcolour.y;
-            if (max < lightcolour.z)
-                max = lightcolour.z;
+            if (max < lightcolor.x)
+                max = lightcolor.x;
+            if (max < lightcolor.y)
+                max = lightcolor.y;
+            if (max < lightcolor.z)
+                max = lightcolor.z;
 
             max *= factor;
             if (max > 255)
@@ -4186,28 +4746,19 @@ rsvg_filter_primitive_specular_lighting_render (RsvgFilterPrimitive * self, Rsvg
             if (max < 0)
                 max = 0;
 
-            output_pixels[y * rowstride + x * 4 + ctx->channelmap[0]] = lightcolour.x * max;
-            output_pixels[y * rowstride + x * 4 + ctx->channelmap[1]] = lightcolour.y * max;
-            output_pixels[y * rowstride + x * 4 + ctx->channelmap[2]] = lightcolour.z * max;
+            output_pixels[y * rowstride + x * 4 + ctx->channelmap[0]] = lightcolor.x * max;
+            output_pixels[y * rowstride + x * 4 + ctx->channelmap[1]] = lightcolor.y * max;
+            output_pixels[y * rowstride + x * 4 + ctx->channelmap[2]] = lightcolor.z * max;
             output_pixels[y * rowstride + x * 4 + ctx->channelmap[3]] = max;
 
         }
 
-    rsvg_filter_store_result (self->result, output, ctx);
-
-    g_object_unref (G_OBJECT (in));
-    g_object_unref (G_OBJECT (output));
-}
+    cairo_surface_mark_dirty (output);
 
-static void
-rsvg_filter_primitive_specular_lighting_free (RsvgNode * self)
-{
-    RsvgFilterPrimitiveSpecularLighting *upself;
+    rsvg_filter_store_result (self->result, output, ctx);
 
-    upself = (RsvgFilterPrimitiveSpecularLighting *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -4233,7 +4784,7 @@ rsvg_filter_primitive_specular_lighting_set_atts (RsvgNode * self, RsvgHandle *
         if ((value = rsvg_property_bag_lookup (atts, "height")))
             filter->super.height = _rsvg_css_parse_length (value);
         if ((value = rsvg_property_bag_lookup (atts, "lighting-color")))
-            filter->lightingcolour = rsvg_css_parse_color (value, 0);
+            filter->lightingcolor = rsvg_css_parse_color (value, 0);
         if ((value = rsvg_property_bag_lookup (atts, "specularConstant")))
             filter->specularConstant = g_ascii_strtod (value, NULL);
         if ((value = rsvg_property_bag_lookup (atts, "specularExponent")))
@@ -4251,7 +4802,7 @@ rsvg_new_filter_primitive_specular_lighting (void)
 {
     RsvgFilterPrimitiveSpecularLighting *filter;
     filter = g_new (RsvgFilterPrimitiveSpecularLighting, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_SPECULAR_LIGHTING);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
@@ -4259,9 +4810,9 @@ rsvg_new_filter_primitive_specular_lighting (void)
     filter->surfaceScale = 1;
     filter->specularConstant = 1;
     filter->specularExponent = 1;
-    filter->lightingcolour = 0xFFFFFFFF;
-    filter->super.render = &rsvg_filter_primitive_specular_lighting_render;
-    filter->super.super.free = &rsvg_filter_primitive_specular_lighting_free;
+    filter->lightingcolor = 0xFFFFFFFF;
+    filter->super.render = rsvg_filter_primitive_specular_lighting_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_specular_lighting_set_atts;
     return (RsvgNode *) filter;
 }
@@ -4296,25 +4847,27 @@ rsvg_filter_primitive_tile_render (RsvgFilterPrimitive * self, RsvgFilterContext
     guchar *in_pixels;
     guchar *output_pixels;
 
-    GdkPixbuf *output;
-    GdkPixbuf *in;
-
-    RsvgFilterPrimitiveTile *upself;
+    cairo_surface_t *output, *in;
 
-    upself = (RsvgFilterPrimitiveTile *) self;
     oboundarys = rsvg_filter_primitive_get_bounds (self, ctx);
 
     input = rsvg_filter_get_result (self->in, ctx);
-    in = input.result;
+    in = input.surface;
     boundarys = input.bounds;
 
+    cairo_surface_flush (in);
 
-    in_pixels = gdk_pixbuf_get_pixels (in);
+    in_pixels = cairo_image_surface_get_data (in);
+
+    output = _rsvg_image_surface_new (ctx->width, ctx->height);
+    if (output == NULL) {
+        cairo_surface_destroy (in);
+        return;
+    }
 
-    output = _rsvg_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, ctx->width, ctx->height);
-    rowstride = gdk_pixbuf_get_rowstride (output);
+    rowstride = cairo_image_surface_get_stride (output);
 
-    output_pixels = gdk_pixbuf_get_pixels (output);
+    output_pixels = cairo_image_surface_get_data (output);
 
     for (y = oboundarys.y0; y < oboundarys.y1; y++)
         for (x = oboundarys.x0; x < oboundarys.x1; x++)
@@ -4326,20 +4879,12 @@ rsvg_filter_primitive_tile_render (RsvgFilterPrimitive * self, RsvgFilterContext
                                boundarys.y0) * rowstride + i];
             }
 
-    rsvg_filter_store_result (self->result, output, ctx);
-
-    g_object_unref (G_OBJECT (output));
-}
+    cairo_surface_mark_dirty (output);
 
-static void
-rsvg_filter_primitive_tile_free (RsvgNode * self)
-{
-    RsvgFilterPrimitiveTile *upself;
+    rsvg_filter_store_result (self->result, output, ctx);
 
-    upself = (RsvgFilterPrimitiveTile *) self;
-    g_string_free (upself->super.result, TRUE);
-    g_string_free (upself->super.in, TRUE);
-    _rsvg_node_free (self);
+    cairo_surface_destroy (in);
+    cairo_surface_destroy (output);
 }
 
 static void
@@ -4373,13 +4918,13 @@ rsvg_new_filter_primitive_tile (void)
 {
     RsvgFilterPrimitiveTile *filter;
     filter = g_new (RsvgFilterPrimitiveTile, 1);
-    _rsvg_node_init (&filter->super.super);
+    _rsvg_node_init (&filter->super.super, RSVG_NODE_TYPE_FILTER_PRIMITIVE_TILE);
     filter->super.in = g_string_new ("none");
     filter->super.result = g_string_new ("none");
     filter->super.x.factor = filter->super.y.factor = filter->super.width.factor =
         filter->super.height.factor = 'n';
-    filter->super.render = &rsvg_filter_primitive_tile_render;
-    filter->super.super.free = &rsvg_filter_primitive_tile_free;
+    filter->super.render = rsvg_filter_primitive_tile_render;
+    filter->super.super.free = rsvg_filter_primitive_free;
     filter->super.super.set_atts = rsvg_filter_primitive_tile_set_atts;
     return (RsvgNode *) filter;
 }
diff --git a/rsvg-filter.h b/rsvg-filter.h
index 280f6a9..da7ffeb 100644
--- a/rsvg-filter.h
+++ b/rsvg-filter.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-filter.h : Provides filters
 
@@ -27,54 +28,69 @@
 
 #include "rsvg.h"
 #include "rsvg-defs.h"
-#include <libxml/SAX.h>
 
 G_BEGIN_DECLS 
 
+typedef struct  {
+    int x0, y0, x1, y1;
+} RsvgIRect;
+
 typedef RsvgCoordUnits RsvgFilterUnits;
 
 struct _RsvgFilter {
     RsvgNode super;
-    int refcnt;
     RsvgLength x, y, width, height;
     RsvgFilterUnits filterunits;
     RsvgFilterUnits primitiveunits;
 };
 
-GdkPixbuf   *rsvg_filter_render	    (RsvgFilter * self, GdkPixbuf * source, GdkPixbuf * bg,
-				     RsvgDrawingCtx * context, RsvgBbox * dimentions, char *channelmap);
+G_GNUC_INTERNAL
+cairo_surface_t *rsvg_filter_render (RsvgFilter *self,
+                                     cairo_surface_t *source,
+                                     RsvgDrawingCtx *context, 
+                                     RsvgBbox *dimentions, 
+                                     char *channelmap);
 
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_filter	    (void);
-RsvgFilter  *rsvg_filter_parse	    (const RsvgDefs * defs, const char *str);
-
-RsvgNode    *rsvg_new_filter_primitive_blend		    (void);
-RsvgNode    *rsvg_new_filter_primitive_convolve_matrix	    (void);
-RsvgNode    *rsvg_new_filter_primitive_gaussian_blur	    (void);
-RsvgNode    *rsvg_new_filter_primitive_offset		    (void);
-RsvgNode    *rsvg_new_filter_primitive_merge		    (void);
-RsvgNode    *rsvg_new_filter_primitive_merge_node	    (void);
-RsvgNode    *rsvg_new_filter_primitive_colour_matrix	    (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_blend                (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_convolve_matrix      (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_gaussian_blur        (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_offset               (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_merge                (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_merge_node           (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_color_matrix        (void);
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_filter_primitive_component_transfer   (void);
-RsvgNode    *rsvg_new_node_component_transfer_function	    (char channel);
-RsvgNode    *rsvg_new_filter_primitive_erode		    (void);
-RsvgNode    *rsvg_new_filter_primitive_composite	    (void);
-RsvgNode    *rsvg_new_filter_primitive_flood		    (void);
-RsvgNode    *rsvg_new_filter_primitive_displacement_map	    (void);
-RsvgNode    *rsvg_new_filter_primitive_turbulence	    (void);
-RsvgNode    *rsvg_new_filter_primitive_image		    (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_node_component_transfer_function      (char channel);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_erode                (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_composite            (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_flood                (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_displacement_map     (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_turbulence           (void);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_image                (void);
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_filter_primitive_diffuse_lighting	    (void);
-RsvgNode    *rsvg_new_filter_primitive_light_source	    (char type);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_node_light_source	                    (char type);
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_filter_primitive_specular_lighting    (void);
-RsvgNode    *rsvg_new_filter_primitive_tile		    (void);
-
-void	     rsvg_filter_adobe_blend	(gint modenum, GdkPixbuf * in, GdkPixbuf * bg,
-					 GdkPixbuf * output, RsvgIRect boundarys, 
-					 RsvgDrawingCtx * ctx);
-void	     rsvg_alpha_blt		(GdkPixbuf * src, gint srcx, gint srcy,
-					 gint srcwidth, gint srcheight, 
-					 GdkPixbuf * dst, gint dstx, gint dsty);
-void	     rsvg_art_affine_image	(const GdkPixbuf * img, GdkPixbuf * intermediate,
-					 double *affine, double w, double h);
+G_GNUC_INTERNAL
+RsvgNode    *rsvg_new_filter_primitive_tile                 (void);
 
 G_END_DECLS
 
diff --git a/rsvg-gobject.c b/rsvg-gobject.c
index 045640f..5639e77 100644
--- a/rsvg-gobject.c
+++ b/rsvg-gobject.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-gobject.c: GObject support.
 
@@ -20,11 +21,29 @@
    Boston, MA 02111-1307, USA.
 */
 
+/**
+ * SECTION: rsvg-handle
+ * @short_description: Create and manipulate SVG objects
+ *
+ * librsvg is a component used within software applications to enable
+ * support for SVG-format scalable graphics. In contrast to raster
+ * formats, scalable vector graphics provide users and artists a way
+ * to create, view, and provide imagery that is not limited to the
+ * pixel or dot density that an output device is capable of.
+ *
+ * Many software developers use the librsvg library to render
+ * SVG graphics. It is lightweight and portable.
+ */
+
+#include "config.h"
+
 #include "rsvg-private.h"
 #include "rsvg-defs.h"
+#include "rsvg.h"
 
 enum {
     PROP_0,
+    PROP_FLAGS,
     PROP_DPI_X,
     PROP_DPI_Y,
     PROP_BASE_URI,
@@ -41,63 +60,56 @@ enum {
 extern double rsvg_internal_dpi_x;
 extern double rsvg_internal_dpi_y;
 
-static GObjectClass *rsvg_parent_class = NULL;
+G_DEFINE_TYPE (RsvgHandle, rsvg_handle, G_TYPE_OBJECT)
 
 static void
-instance_init (RsvgHandle * self)
+rsvg_handle_init (RsvgHandle * self)
 {
-    self->priv = g_new0 (RsvgHandlePrivate, 1);
-    self->priv->defs = rsvg_defs_new ();
+    self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, RSVG_TYPE_HANDLE, RsvgHandlePrivate);
+
+    self->priv->flags = RSVG_HANDLE_FLAGS_NONE;
+    self->priv->load_policy = RSVG_LOAD_POLICY_DEFAULT;
+    self->priv->defs = rsvg_defs_new (self);
     self->priv->handler_nest = 0;
-    self->priv->entities = g_hash_table_new (g_str_hash, g_str_equal);
+    self->priv->entities = g_hash_table_new_full (g_str_hash, 
+                                                  g_str_equal,
+                                                  g_free,
+                                                  (GDestroyNotify) xmlFreeNode);
     self->priv->dpi_x = rsvg_internal_dpi_x;
     self->priv->dpi_y = rsvg_internal_dpi_y;
 
-    self->priv->css_props = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+    self->priv->css_props = g_hash_table_new_full (g_str_hash,
+                                                   g_str_equal,
+                                                   g_free,
+                                                   (GDestroyNotify) g_hash_table_destroy);
 
     self->priv->ctxt = NULL;
     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;
+#else
+    self->priv->gzipped_data = NULL;
+#endif
     self->priv->first_write = TRUE;
+    self->priv->cancellable = NULL;
 
     self->priv->is_disposed = FALSE;
     self->priv->in_loop = FALSE;
 }
 
 static void
-rsvg_ctx_free_helper (gpointer key, gpointer value, gpointer user_data)
-{
-    xmlEntityPtr entval = (xmlEntityPtr) value;
-
-#if LIBXML_VERSION < 20700
-    /* key == entval->name, so it's implicitly freed below */
-
-    xmlFree ((xmlChar *) entval->name);
-    xmlFree ((xmlChar *) entval->ExternalID);
-    xmlFree ((xmlChar *) entval->SystemID);
-    xmlFree (entval->content);
-    xmlFree (entval->orig);
-    xmlFree (entval);
-#else
-	xmlFreeNode(entval);
-#endif
-}
-
-static void
-instance_dispose (GObject * instance)
+rsvg_handle_dispose (GObject *instance)
 {
     RsvgHandle *self = (RsvgHandle *) instance;
 
-    self->priv->is_disposed = TRUE;
+    if (self->priv->is_disposed)
+      goto chain;
 
-#if HAVE_SVGZ
-    if (self->priv->is_gzipped)
-        g_object_unref (G_OBJECT (self->priv->gzipped_data));
-#endif
+    self->priv->is_disposed = TRUE;
 
-    g_hash_table_foreach (self->priv->entities, rsvg_ctx_free_helper, NULL);
     g_hash_table_destroy (self->priv->entities);
     rsvg_defs_free (self->priv->defs);
     g_hash_table_destroy (self->priv->css_props);
@@ -114,17 +126,40 @@ instance_dispose (GObject * instance)
     if (self->priv->base_uri)
         g_free (self->priv->base_uri);
 
-    g_free (self->priv);
+#if GLIB_CHECK_VERSION (2, 24, 0)
+    if (self->priv->base_gfile) {
+        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;
+    }
+#else
+    if (self->priv->gzipped_data) {
+        g_object_unref (self->priv->gzipped_data);
+        self->priv->gzipped_data = NULL;
+    }
+#endif
+
+    if (self->priv->cancellable) {
+        g_object_unref (self->priv->cancellable);
+        self->priv->cancellable = NULL;
+    }
 
-    rsvg_parent_class->dispose (G_OBJECT (self));
+  chain:
+    G_OBJECT_CLASS (rsvg_handle_parent_class)->dispose (instance);
 }
 
 static void
-set_property (GObject * instance, guint prop_id, GValue const *value, GParamSpec * pspec)
+rsvg_handle_set_property (GObject * instance, guint prop_id, GValue const *value, GParamSpec * pspec)
 {
     RsvgHandle *self = RSVG_HANDLE (instance);
 
     switch (prop_id) {
+    case PROP_FLAGS:
+        self->priv->flags = g_value_get_flags (value);
+        break;
     case PROP_DPI_X:
         rsvg_handle_set_dpi_x_y (self, g_value_get_double (value), self->priv->dpi_y);
         break;
@@ -140,12 +175,15 @@ set_property (GObject * instance, guint prop_id, GValue const *value, GParamSpec
 }
 
 static void
-get_property (GObject * instance, guint prop_id, GValue * value, GParamSpec * pspec)
+rsvg_handle_get_property (GObject * instance, guint prop_id, GValue * value, GParamSpec * pspec)
 {
     RsvgHandle *self = RSVG_HANDLE (instance);
     RsvgDimensionData dim;
 
     switch (prop_id) {
+    case PROP_FLAGS:
+        g_value_set_flags (value, self->priv->flags);
+        break;
     case PROP_DPI_X:
         g_value_set_double (value, self->priv->dpi_x);
         break;
@@ -186,21 +224,31 @@ get_property (GObject * instance, guint prop_id, GValue * value, GParamSpec * ps
 }
 
 static void
-class_init (RsvgHandleClass * klass)
+rsvg_handle_class_init (RsvgHandleClass * klass)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-    /* hook gobject vfuncs */
-    gobject_class->dispose = instance_dispose;
-
-    rsvg_parent_class = (GObjectClass *) g_type_class_peek_parent (klass);
-
-    gobject_class->set_property = set_property;
-    gobject_class->get_property = get_property;
-
-        /**
-	 * dpi-x:
-	 */
+    gobject_class->dispose = rsvg_handle_dispose;
+    gobject_class->set_property = rsvg_handle_set_property;
+    gobject_class->get_property = rsvg_handle_get_property;
+
+    /**
+     * RsvgHandle:flags:
+     * 
+     * Flags from #RsvgHandleFlags.
+     * 
+     * Since: 2.36
+     */
+    g_object_class_install_property (gobject_class,
+                                     PROP_FLAGS,
+                                     g_param_spec_flags ("flags", NULL, NULL,
+                                                         RSVG_TYPE_HANDLE_FLAGS,
+                                                         RSVG_HANDLE_FLAGS_NONE,
+                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+    /**
+     * dpi-x:
+     */
     g_object_class_install_property (gobject_class,
                                      PROP_DPI_X,
                                      g_param_spec_double ("dpi-x", _("Horizontal resolution"),
@@ -248,72 +296,63 @@ class_init (RsvgHandleClass * klass)
                                                           _("ex"), 0, G_MAXDOUBLE, 0,
                                                           (GParamFlags) (G_PARAM_READABLE)));
 
+    /**
+     * RsvgHandle:title:
+     *
+     * SVG's description
+     *
+     * Deprecated: 2.36
+     */
     g_object_class_install_property (gobject_class,
                                      PROP_TITLE,
                                      g_param_spec_string ("title", _("Title"),
                                                           _("SVG file title"), NULL,
                                                           (GParamFlags) (G_PARAM_READABLE)));
 
+    /**
+     * RsvgHandle:desc:
+     *
+     * SVG's description
+     *
+     * Deprecated: 2.36
+     */
     g_object_class_install_property (gobject_class,
                                      PROP_DESC,
                                      g_param_spec_string ("desc", _("Description"),
                                                           _("SVG file description"), NULL,
                                                           (GParamFlags) (G_PARAM_READABLE)));
 
+    /**
+     * RsvgHandle:metadata:
+     *
+     * SVG's description
+     *
+     * Deprecated: 2.36
+     */
     g_object_class_install_property (gobject_class,
                                      PROP_METADATA,
                                      g_param_spec_string ("metadata", _("Metadata"),
                                                           _("SVG file metadata"), NULL,
                                                           (GParamFlags) (G_PARAM_READABLE)));
 
-    rsvg_SAX_handler_struct_init ();
-}
-
-const GTypeInfo rsvg_type_info = {
-    sizeof (RsvgHandleClass),
-    NULL,                       /* base_init */
-    NULL,                       /* base_finalize */
-    (GClassInitFunc) class_init,
-    NULL,                       /* class_finalize */
-    NULL,                       /* class_data */
-    sizeof (RsvgHandle),
-    0,                          /* n_preallocs */
-    (GInstanceInitFunc) instance_init,
-};
+    g_type_class_add_private (klass, sizeof (RsvgHandlePrivate));
 
-static GType rsvg_type = 0;
-
-/* HACK to get around bugs 357406 and 362217. private API for now. */
-GType
-_rsvg_register_types (GTypeModule * module)
-{
-    rsvg_type = g_type_module_register_type (module,
-                                             G_TYPE_OBJECT,
-                                             "RsvgHandle", &rsvg_type_info, (GTypeFlags) 0);
-    return rsvg_type;
-}
+    xmlInitParser ();
 
-GType
-rsvg_handle_get_type (void)
-{
-    if (!rsvg_type) {
-        rsvg_type =
-            g_type_register_static (G_TYPE_OBJECT, "RsvgHandle", &rsvg_type_info, (GTypeFlags) 0);
-    }
-    return rsvg_type;
+    rsvg_SAX_handler_struct_init ();
 }
 
 /**
  * rsvg_handle_free:
  * @handle: An #RsvgHandle
  *
- * Frees #handle.
+ * Frees @handle.
  * Deprecated: Use g_object_unref() instead.
  **/
 void
 rsvg_handle_free (RsvgHandle * handle)
 {
-    g_object_unref (G_OBJECT (handle));
+    g_object_unref (handle);
 }
 
 /**
@@ -333,3 +372,22 @@ rsvg_handle_new (void)
 {
     return RSVG_HANDLE (g_object_new (RSVG_TYPE_HANDLE, NULL));
 }
+
+
+/**
+ * rsvg_handle_new_with_flags:
+ * @flags: flags from #RsvgHandleFlags
+ *
+ * Creates a new #RsvgHandle with flags @flags.
+ *
+ * Returns: (transfer full): a new #RsvgHandle
+ * 
+ * Since: 2.36
+ **/
+RsvgHandle *
+rsvg_handle_new_with_flags (RsvgHandleFlags flags)
+{
+    return g_object_new (RSVG_TYPE_HANDLE, 
+                         "flags", flags,
+                         NULL);
+}
diff --git a/rsvg-image.c b/rsvg-image.c
index 096ed09..8eb03ef 100644
--- a/rsvg-image.c
+++ b/rsvg-image.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-image.c: Image loading and displaying
 
@@ -33,398 +34,86 @@
 #include <math.h>
 #include <errno.h>
 #include "rsvg-css.h"
-#ifdef HAVE_GIO
-#include <gio/gio.h>
-#endif
-
-static const char s_UTF8_B64Alphabet[64] = {
-    0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
-    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,   /* A-Z */
-    0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
-    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,   /* a-z */
-    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, /* 0-9 */
-    0x2b,                       /* + */
-    0x2f                        /* / */
-};
-static const char utf8_b64_pad = 0x3d;
-
-static gboolean
-b64_decode_char (char c, int *b64)
-{
-    if ((c >= 0x41) && (c <= 0x5a)) {
-        *b64 = c - 0x41;
-        return TRUE;
-    }
-    if ((c >= 0x61) && (c <= 0x7a)) {
-        *b64 = c - (0x61 - 26);
-        return TRUE;
-    }
-    if ((c >= 0x30) && (c <= 0x39)) {
-        *b64 = c + (52 - 0x30);
-        return TRUE;
-    }
-    if (c == 0x2b) {
-        *b64 = 62;
-        return TRUE;
-    }
-    if (c == 0x2f) {
-        *b64 = 63;
-        return TRUE;
-    }
-    return FALSE;
-}
+#include "rsvg-io.h"
 
-static gboolean
-utf8_base64_decode (guchar ** binptr, size_t * binlen, const char *b64ptr, size_t b64len)
+cairo_surface_t *
+rsvg_cairo_surface_new_from_href (RsvgHandle *handle,
+                                  const char *href,
+                                  GError **error)
 {
-    gboolean decoded = TRUE;
-    gboolean padding = FALSE;
-
-    int i = 0;
-    glong ucs4_len, j;
-
-    unsigned char byte1 = 0;
-    unsigned char byte2;
-
-    gunichar ucs4, *ucs4_str;
-
-    if (b64len == 0)
-        return TRUE;
-
-    if ((binptr == 0) || (b64ptr == 0))
-        return FALSE;
-
-    ucs4_str = g_utf8_to_ucs4_fast (b64ptr, b64len, &ucs4_len);
-
-    for (j = 0; j < ucs4_len; j++) {
-        ucs4 = ucs4_str[j];
-        if ((ucs4 & 0x7f) == ucs4) {
-            int b64;
-            char c = (char) (ucs4);
-
-            if (b64_decode_char (c, &b64)) {
-                if (padding || (*binlen == 0)) {
-                    decoded = FALSE;
-                    break;
-                }
-
-                switch (i) {
-                case 0:
-                    byte1 = (unsigned char) (b64) << 2;
-                    i++;
-                    break;
-                case 1:
-                    byte2 = (unsigned char) (b64);
-                    byte1 |= byte2 >> 4;
-                    *(*binptr)++ = (char) (byte1);
-                    (*binlen)--;
-                    byte1 = (byte2 & 0x0f) << 4;
-                    i++;
-                    break;
-                case 2:
-                    byte2 = (unsigned char) (b64);
-                    byte1 |= byte2 >> 2;
-                    *(*binptr)++ = (char) (byte1);
-                    (*binlen)--;
-                    byte1 = (byte2 & 0x03) << 6;
-                    i++;
-                    break;
-                default:
-                    byte1 |= (unsigned char) (b64);
-                    *(*binptr)++ = (char) (byte1);
-                    (*binlen)--;
-                    i = 0;
-                    break;
-                }
-
-                if (!decoded)
-                    break;
-
-                continue;
-            } else if (c == utf8_b64_pad) {
-                switch (i) {
-                case 0:
-                case 1:
-                    decoded = FALSE;
-                    break;
-                case 2:
-                    if (*binlen == 0)
-                        decoded = FALSE;
-                    else {
-                        *(*binptr)++ = (char) (byte1);
-                        (*binlen)--;
-                        padding = TRUE;
-                    }
-                    i++;
-                    break;
-                default:
-                    if (!padding) {
-                        if (*binlen == 0)
-                            decoded = FALSE;
-                        else {
-                            *(*binptr)++ = (char) (byte1);
-                            (*binlen)--;
-                            padding = TRUE;
-                        }
-                    }
-                    i = 0;
-                    break;
-                }
-                if (!decoded)
-                    break;
-
-                continue;
-            }
-        }
-        if (g_unichar_isspace (ucs4))
-            continue;
-
-        decoded = FALSE;
-        break;
-    }
-
-    g_free (ucs4_str);
-    return decoded;
-}
-
-static GByteArray *
-rsvg_acquire_base64_resource (const char *data, GError ** error)
-{
-    GByteArray *array;
-
-    guchar *bufptr;
-    size_t buffer_len, buffer_max_len, data_len;
-
-    rsvg_return_val_if_fail (data != NULL, NULL, error);
-
-    while (*data)
-        if (*data++ == ',')
-            break;
-
-    data_len = strlen (data);
-
-    buffer_max_len = ((data_len >> 2) + 1) * 3;
-    buffer_len = buffer_max_len;
-
-    array = g_byte_array_sized_new (buffer_max_len);
-    bufptr = array->data;
-
-    if (!utf8_base64_decode (&bufptr, &buffer_len, data, data_len)) {
-        g_byte_array_free (array, TRUE);
+    char *data;
+    gsize data_len;
+    char *mime_type = NULL;
+    GdkPixbufLoader *loader = NULL;
+    GdkPixbuf *pixbuf = NULL;
+    cairo_surface_t *surface = NULL;
+
+    data = _rsvg_handle_acquire_data (handle, href, &mime_type, &data_len, error);
+    if (data == NULL)
         return NULL;
-    }
-
-    array->len = buffer_max_len - buffer_len;
-
-    return array;
-}
 
-gchar *
-rsvg_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);
+    if (mime_type) {
+        loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, error);
     } 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);
+        loader = gdk_pixbuf_loader_new ();
     }
 
-    return absolute_filename;
-}
-
-static GByteArray *
-rsvg_acquire_file_resource (const char *filename, const char *base_uri, GError ** error)
-{
-    GByteArray *array;
-    gchar *path;
-
-    guchar buffer[4096];
-    int length;
-    FILE *f;
+    if (loader == NULL)
+        goto out;
 
-    rsvg_return_val_if_fail (filename != NULL, NULL, error);
+    if (!gdk_pixbuf_loader_write (loader, (guchar *) data, data_len, error)) {
+        gdk_pixbuf_loader_close (loader, NULL);
+        goto out;
+    }
 
-    path = rsvg_get_file_path (filename, base_uri);
-    if (path == NULL)
-	return NULL;
+    if (!gdk_pixbuf_loader_close (loader, error))
+        goto out;
 
-    f = fopen (path, "rb");
-    g_free (path);
+    pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
 
-    if (!f) {
+    if (!pixbuf) {
         g_set_error (error,
-                     G_FILE_ERROR,
-                     g_file_error_from_errno (errno),
-                     _("Failed to open file '%s': %s"), filename, g_strerror (errno));
-        return NULL;
+                     GDK_PIXBUF_ERROR,
+                     GDK_PIXBUF_ERROR_FAILED,
+                      _("Failed to load image '%s': reason not known, probably a corrupt image file"),
+                      href);
+        goto out;
     }
 
-    /* TODO: an optimization is to use the file's size */
-    array = g_byte_array_new ();
+    surface = rsvg_cairo_surface_from_pixbuf (pixbuf);
 
-    while (!feof (f)) {
-        length = fread (buffer, 1, sizeof (buffer), f);
-        if (length > 0) {
-            if (g_byte_array_append (array, buffer, length) == NULL) {
-                fclose (f);
-                g_byte_array_free (array, TRUE);
-                return NULL;
-            }
-        } else if (ferror (f)) {
-            fclose (f);
-            g_byte_array_free (array, TRUE);
-            return NULL;
-        }
-    }
-
-    fclose (f);
+    if (mime_type == NULL) {
+        /* Try to get the information from the loader */
+        GdkPixbufFormat *format;
+        char **mime_types;
 
-    return array;
-}
+        if ((format = gdk_pixbuf_loader_get_format (loader)) != NULL) {
+            mime_types = gdk_pixbuf_format_get_mime_types (format);
 
-#ifdef HAVE_GIO
-
-static void
-rsvg_free_error (GError ** err)
-{
-	if (err) {
-		if (*err) {
-			g_error_free (*err);
-			*err = NULL;
-		}
-	}
-}
-
-static GByteArray *
-rsvg_acquire_vfs_resource (const char *filename, const char *base_uri, GError ** error)
-{
-    GByteArray *array;
-
-    GFile *file;
-    char *data;
-    gsize size;
-    gboolean res = FALSE;
-
-    rsvg_return_val_if_fail (filename != NULL, NULL, error);
-
-    file = g_file_new_for_uri (filename);
-
-    if (!(res = g_file_load_contents (file, NULL, &data, &size, NULL, error))) {
-        if (base_uri != NULL) {
-            GFile *base;
-
-			rsvg_free_error(error);
-			
-			g_object_unref (file);
-
-            base = g_file_new_for_uri (base_uri);
-            file = g_file_resolve_relative_path (base, filename);
-            g_object_unref (base);
-
-			res = g_file_load_contents (file, NULL, &data, &size, NULL, error);
+            if (mime_types != NULL)
+                mime_type = g_strdup (mime_types[0]);
+            g_strfreev (mime_types);
         }
     }
-
-	g_object_unref (file);
-
-    if (res) {
-        array = g_byte_array_new ();
-
-        g_byte_array_append (array, (guint8 *)data, size);
-        g_free (data);
-    } else {
-        return NULL;
+#if 0
+    /* Don't handle this at all.  Older versions of Cairo don't provide
+     * cairo_surface_set_mime_data() anyway.
+     */
+    if ((handle->priv->flags & RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA) != 0 &&
+        mime_type != NULL &&
+        cairo_surface_set_mime_data (surface, mime_type, (guchar *) data,
+                                     data_len, g_free, data) == CAIRO_STATUS_SUCCESS) {
+        data = NULL; /* transferred to the surface */
     }
-
-    return array;
-}
-#endif
-
-GByteArray *
-_rsvg_acquire_xlink_href_resource (const char *href, const char *base_uri, GError ** err)
-{
-    GByteArray *arr = NULL;
-
-    if (!(href && *href))
-        return NULL;
-
-    if (!strncmp (href, "data:", 5))
-        arr = rsvg_acquire_base64_resource (href, NULL);
-
-    if (!arr)
-        arr = rsvg_acquire_file_resource (href, base_uri, NULL);
-
-#ifdef HAVE_GIO
-    if (!arr)
-        arr = rsvg_acquire_vfs_resource (href, base_uri, NULL);
 #endif
 
-    return arr;
-}
-
-GdkPixbuf *
-rsvg_pixbuf_new_from_href (const char *href, const char *base_uri, GError ** error)
-{
-    GByteArray *arr;
-
-    arr = _rsvg_acquire_xlink_href_resource (href, base_uri, error);
-    if (arr) {
-        GdkPixbufLoader *loader;
-        GdkPixbuf *pixbuf = NULL;
-        int res;
-
-        loader = gdk_pixbuf_loader_new ();
-
-        res = gdk_pixbuf_loader_write (loader, arr->data, arr->len, error);
-        g_byte_array_free (arr, TRUE);
-
-        if (!res) {
-            gdk_pixbuf_loader_close (loader, NULL);
-            g_object_unref (loader);
-            return NULL;
-        }
-
-        if (!gdk_pixbuf_loader_close (loader, error)) {
-            g_object_unref (loader);
-            return NULL;
-        }
-
-        pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
-
-        if (!pixbuf) {
-            g_object_unref (loader);
-            g_set_error (error,
-                         GDK_PIXBUF_ERROR,
-                         GDK_PIXBUF_ERROR_FAILED,
-                         _
-                         ("Failed to load image '%s': reason not known, probably a corrupt image file"),
-                         href);
-            return NULL;
-        }
-
-        g_object_ref (pixbuf);
-
+  out:
+    if (loader)
         g_object_unref (loader);
+    g_free (mime_type);
+    g_free (data);
 
-        return pixbuf;
-    }
-
-    return NULL;
+    return surface;
 }
 
 void
@@ -473,8 +162,8 @@ rsvg_node_image_free (RsvgNode * self)
     rsvg_state_finalize (z->super.state);
     g_free (z->super.state);
     z->super.state = NULL;
-    if (z->img)
-        g_object_unref (G_OBJECT (z->img));
+    if (z->surface)
+        cairo_surface_destroy (z->surface);
     _rsvg_node_free(self);
 }
 
@@ -483,10 +172,10 @@ rsvg_node_image_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
     RsvgNodeImage *z = (RsvgNodeImage *) self;
     unsigned int aspect_ratio = z->preserve_aspect_ratio;
-    GdkPixbuf *img = z->img;
     gdouble x, y, w, h;
+    cairo_surface_t *surface = z->surface;
 
-    if (img == NULL)
+    if (surface == NULL)
         return;
 
     x = _rsvg_css_normalize_length (&z->x, ctx, 'h');
@@ -498,14 +187,16 @@ rsvg_node_image_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
     rsvg_push_discrete_layer (ctx);
 
-    if (!rsvg_state_current (ctx)->overflow && (aspect_ratio & RSVG_ASPECT_RATIO_SLICE)) {
+    if (!rsvg_current_state (ctx)->overflow && (aspect_ratio & RSVG_ASPECT_RATIO_SLICE)) {
         rsvg_add_clipping_rect (ctx, x, y, w, h);
     }
 
-    rsvg_preserve_aspect_ratio (aspect_ratio, (double) gdk_pixbuf_get_width (img),
-                                (double) gdk_pixbuf_get_height (img), &w, &h, &x, &y);
+    rsvg_preserve_aspect_ratio (aspect_ratio, 
+                                (double) cairo_image_surface_get_width (surface),
+                                (double) cairo_image_surface_get_height (surface), 
+                                &w, &h, &x, &y);
 
-    rsvg_render_image (ctx, img, x, y, w, h);
+    rsvg_render_surface (ctx, surface, x, y, w, h);
 
     rsvg_pop_discrete_layer (ctx);
 }
@@ -528,11 +219,13 @@ rsvg_node_image_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
         /* path is used by some older adobe illustrator versions */
         if ((value = rsvg_property_bag_lookup (atts, "path"))
             || (value = rsvg_property_bag_lookup (atts, "xlink:href"))) {
-            image->img = rsvg_pixbuf_new_from_href (value, rsvg_handle_get_base_uri (ctx), NULL);
+            image->surface = rsvg_cairo_surface_new_from_href (ctx,
+                                                               value, 
+                                                               NULL);
 
-            if (!image->img) {
+            if (!image->surface) {
 #ifdef G_ENABLE_DEBUG
-                g_warning (_("Couldn't load image: %s\n"), value);
+                g_warning ("Couldn't load image: %s\n", value);
 #endif
             }
         }
@@ -554,9 +247,9 @@ rsvg_new_image (void)
 {
     RsvgNodeImage *image;
     image = g_new (RsvgNodeImage, 1);
-    _rsvg_node_init (&image->super);
+    _rsvg_node_init (&image->super, RSVG_NODE_TYPE_IMAGE);
     g_assert (image->super.state);
-    image->img = NULL;
+    image->surface = NULL;
     image->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
     image->x = image->y = image->w = image->h = _rsvg_css_parse_length ("0");
     image->super.free = rsvg_node_image_free;
diff --git a/rsvg-image.h b/rsvg-image.h
index 273d2c7..9f291d0 100644
--- a/rsvg-image.h
+++ b/rsvg-image.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-image.h: Image loading and displaying
 
@@ -31,8 +32,11 @@
 
 #include "rsvg-structure.h"
 
+#include <cairo.h>
+
 G_BEGIN_DECLS 
 
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_image (void);
 
 typedef struct _RsvgNodeImage RsvgNodeImage;
@@ -41,16 +45,14 @@ struct _RsvgNodeImage {
     RsvgNode super;
     gint preserve_aspect_ratio;
     RsvgLength x, y, w, h;
-    GdkPixbuf *img;
+    cairo_surface_t *surface; /* a cairo image surface */
 };
 
+G_GNUC_INTERNAL
 void rsvg_preserve_aspect_ratio (unsigned int aspect_ratio, double width,
                                  double height, double *w, double *h, double *x, double *y);
-
-
-gchar *rsvg_get_file_path (const gchar * filename, const gchar * basedir);
-
-GdkPixbuf *rsvg_pixbuf_new_from_href (const char *href, const char *base_uri, GError ** error);
+G_GNUC_INTERNAL
+cairo_surface_t *rsvg_cairo_surface_new_from_href (RsvgHandle *handle, const char *href, GError ** error);
 
 G_END_DECLS
 
diff --git a/rsvg-io.c b/rsvg-io.c
new file mode 100644
index 0000000..7452b73
--- /dev/null
+++ b/rsvg-io.c
@@ -0,0 +1,358 @@
+/*
+   Copyright (C) 2000 Eazel, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005 Dom Lachowicz <cinamod@hotmail.com>
+   Copyright (C) 2003, 2004, 2005 Caleb Moore <c.moore@student.unsw.edu.au>
+   Copyright © 2011, 2012 Christian Persch
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+
+#include "rsvg-io.h"
+#include "rsvg-private.h"
+
+#include <string.h>
+
+/* Copied from soup-request-data.c (LGPL2+):
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ * and from soup-uri.c:
+ * Copyright 1999-2003 Ximian, Inc.
+ */
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+static char *
+uri_decoded_copy (const char *part, 
+                  gsize length)
+{
+    unsigned char *s, *d;
+    char *decoded = g_strndup (part, length);
+
+    s = d = (unsigned char *)decoded;
+    do {
+        if (*s == '%') {
+            if (!g_ascii_isxdigit (s[1]) ||
+                !g_ascii_isxdigit (s[2])) {
+                *d++ = *s;
+                continue;
+            }
+            *d++ = HEXCHAR (s);
+            s += 2;
+        } else {
+            *d++ = *s;
+        }
+    } while (*s++);
+
+    return decoded;
+}
+
+#define BASE64_INDICATOR     ";base64"
+#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)
+{
+    const char *comma, *start, *end;
+    char *mime_type;
+    char *data;
+    gsize data_len;
+    gboolean base64 = FALSE;
+
+    g_assert (out_len != NULL);
+    g_assert (strncmp (uri, "data:", 5) == 0);
+
+    mime_type = NULL;
+    start = uri + 5;
+    comma = strchr (start, ',');
+
+    if (comma && comma != start) {
+        /* Deal with MIME type / params */
+        if (comma > start + BASE64_INDICATOR_LEN && 
+            !g_ascii_strncasecmp (comma - BASE64_INDICATOR_LEN, BASE64_INDICATOR, BASE64_INDICATOR_LEN)) {
+            end = comma - BASE64_INDICATOR_LEN;
+            base64 = TRUE;
+        } else {
+            end = comma;
+        }
+
+        if (end != start) {
+            mime_type = uri_decoded_copy (start, end - start);
+        }
+    }
+
+    if (comma)
+        start = comma + 1;
+
+    if (*start) {
+	data = uri_decoded_copy (start, strlen (start));
+
+        if (base64)
+            data = (char *) g_base64_decode_inplace (data, &data_len);
+        else
+            data_len = strlen (data);
+    } else {
+        data = NULL;
+        data_len = 0;
+    }
+
+    if (out_mime_type)
+        *out_mime_type = mime_type;
+    else
+        g_free (mime_type);
+
+    *out_len = data_len;
+    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,
+                        char **out_mime_type,
+                        gsize *out_len,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+    gchar *path, *data;
+    gsize len;
+    char *content_type;
+
+    rsvg_return_val_if_fail (filename != NULL, NULL, error);
+    g_assert (out_len != NULL);
+
+    path = _rsvg_io_get_file_path (filename, base_uri);
+    if (path == NULL)
+        return NULL;
+
+    if (!g_file_get_contents (path, &data, &len, error)) {
+        g_free (path);
+        return NULL;
+    }
+
+    if (out_mime_type &&
+        (content_type = g_content_type_guess (path, (guchar *) data, len, NULL))) {
+        *out_mime_type = g_content_type_get_mime_type (content_type);
+        g_free (content_type);
+    }
+
+    g_free (path);
+
+    *out_len = len;
+    return data;
+}
+
+static GInputStream *
+rsvg_acquire_gvfs_stream (const char *uri, 
+                          const char *base_uri, 
+                          char **out_mime_type,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+    GFile *base, *file;
+    GFileInputStream *stream;
+    GError *err = NULL;
+
+    file = g_file_new_for_uri (uri);
+
+    stream = g_file_read (file, cancellable, &err);
+    g_object_unref (file);
+
+    if (stream == NULL &&
+        g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+        g_clear_error (&err);
+
+        base = g_file_new_for_uri (base_uri);
+        file = g_file_resolve_relative_path (base, uri);
+        g_object_unref (base);
+
+        stream = g_file_read (file, cancellable, &err);
+        g_object_unref (file);
+    }
+
+    if (stream == NULL) {
+        g_propagate_error (error, err);
+        return NULL;
+    }
+
+    if (out_mime_type) {
+        GFileInfo *file_info;
+        const char *content_type;
+
+        file_info = g_file_input_stream_query_info (stream, 
+                                                    G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+                                                    cancellable,
+                                                    NULL /* error */);
+        if (file_info &&
+            (content_type = g_file_info_get_content_type (file_info)))
+            *out_mime_type = g_content_type_get_mime_type (content_type);
+        else
+            *out_mime_type = NULL;
+
+        if (file_info)
+            g_object_unref (file_info);
+    }
+
+    return G_INPUT_STREAM (stream);
+}
+
+static char *
+rsvg_acquire_gvfs_data (const char *uri,
+                        const char *base_uri,
+                        char **out_mime_type,
+                        gsize *out_len,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+    GFile *base, *file;
+    GError *err;
+    char *data;
+    gsize len;
+    char *content_type;
+    gboolean res;
+
+    file = g_file_new_for_uri (uri);
+
+    err = NULL;
+    data = NULL;
+    if (!(res = g_file_load_contents (file, cancellable, &data, &len, NULL, &err)) &&
+        g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) &&
+        base_uri != NULL) {
+        g_clear_error (&err);
+        g_object_unref (file);
+
+        base = g_file_new_for_uri (base_uri);
+        file = g_file_resolve_relative_path (base, uri);
+        g_object_unref (base);
+
+        res = g_file_load_contents (file, cancellable, &data, &len, NULL, &err);
+    }
+
+    g_object_unref (file);
+
+    if (err) {
+        g_propagate_error (error, err);
+        return NULL;
+    }
+
+    if (out_mime_type &&
+        (content_type = g_content_type_guess (uri, (guchar *) data, len, NULL))) {
+        *out_mime_type = g_content_type_get_mime_type (content_type);
+        g_free (content_type);
+    }
+
+    *out_len = len;
+    return data;
+}
+
+char *
+_rsvg_io_acquire_data (const char *href, 
+                       const char *base_uri, 
+                       char **mime_type,
+                       gsize *len,
+                       GCancellable *cancellable,
+                       GError **error)
+{
+    char *data;
+    gsize llen;
+
+    if (!(href && *href)) {
+        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            "Invalid URI");
+        return NULL;
+    }
+
+    if (!len)
+        len = &llen;
+
+    if (strncmp (href, "data:", 5) == 0)
+      return rsvg_acquire_data_data (href, NULL, mime_type, len, error);
+
+    if ((data = rsvg_acquire_file_data (href, base_uri, mime_type, len, cancellable, NULL)))
+      return data;
+
+    if ((data = rsvg_acquire_gvfs_data (href, base_uri, mime_type, len, cancellable, error)))
+      return data;
+
+    return NULL;
+}
+
+GInputStream *
+_rsvg_io_acquire_stream (const char *href, 
+                         const char *base_uri, 
+                         char **mime_type,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+    GInputStream *stream;
+    char *data;
+    gsize len;
+
+    if (!(href && *href)) {
+        g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            "Invalid URI");
+        return NULL;
+    }
+
+    if (strncmp (href, "data:", 5) == 0) {
+        if (!(data = rsvg_acquire_data_data (href, NULL, 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)))
+      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)))
+      return stream;
+
+    return NULL;
+}
diff --git a/rsvg-io.h b/rsvg-io.h
new file mode 100644
index 0000000..d710ccf
--- /dev/null
+++ b/rsvg-io.h
@@ -0,0 +1,46 @@
+/*
+   Copyright (C) 2000 Eazel, Inc.
+   Copyright (C) 2002, 2003, 2004, 2005 Dom Lachowicz <cinamod@hotmail.com>
+   Copyright (C) 2003, 2004, 2005 Caleb Moore <c.moore@student.unsw.edu.au>
+   Copyright © 2011, 2012 Christian Persch
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+*/
+
+#ifndef RSVG_IO_H
+#define RSVG_IO_H
+
+#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,
+			     gsize *len,
+			     GCancellable *cancellable,
+			     GError **error);
+
+GInputStream *_rsvg_io_acquire_stream (const char *uri,
+                                       const char *base_uri,
+                                       char **mime_type,
+                                       GCancellable *cancellable,
+                                       GError **error);
+
+#endif /* RSVG_IO_H */
diff --git a/rsvg-marker.c b/rsvg-marker.c
index 658d699..bcca6d7 100644
--- a/rsvg-marker.c
+++ b/rsvg-marker.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-marker.c: Marker loading and rendering
 
@@ -22,6 +23,8 @@
    Authors: Caleb Moore <c.moore@student.unsw.edu.au>
 */
 
+#include "config.h"
+
 #include "rsvg-marker.h"
 #include "rsvg-private.h"
 #include "rsvg-styles.h"
@@ -31,6 +34,7 @@
 #include "rsvg-filter.h"
 #include "rsvg-mask.h"
 #include "rsvg-image.h"
+#include "rsvg-path.h"
 
 #include <string.h>
 #include <math.h>
@@ -83,45 +87,55 @@ rsvg_new_marker (void)
 {
     RsvgMarker *marker;
     marker = g_new (RsvgMarker, 1);
-    _rsvg_node_init (&marker->super);
+    _rsvg_node_init (&marker->super, RSVG_NODE_TYPE_MARKER);
     marker->orient = 0;
     marker->orientAuto = FALSE;
     marker->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
     marker->refX = marker->refY = _rsvg_css_parse_length ("0");
-    marker->width = marker->height = _rsvg_css_parse_length ("1");
+    marker->width = marker->height = _rsvg_css_parse_length ("3");
     marker->bbox = TRUE;
     marker->vbox.active = FALSE;
     marker->super.set_atts = rsvg_node_marker_set_atts;
     return &marker->super;
 }
 
-void
-rsvg_marker_render (RsvgMarker * self, gdouble x, gdouble y, gdouble orient, gdouble linewidth,
+static void
+rsvg_marker_render (const char * marker_name, gdouble xpos, gdouble ypos, gdouble orient, gdouble linewidth,
                     RsvgDrawingCtx * ctx)
 {
-    gdouble affine[6];
-    gdouble taffine[6];
+    RsvgMarker *self;
+    cairo_matrix_t affine, taffine;
     unsigned int i;
     gdouble rotation;
-    RsvgState *state = rsvg_state_current (ctx);
+    RsvgState *state = rsvg_current_state (ctx);
+
+    if (marker_name == NULL)
+        return; /* to avoid the caller having to check for nonexistent markers on every call */
 
-    _rsvg_affine_translate (taffine, x, y);
-    _rsvg_affine_multiply (affine, taffine, state->affine);
+    self = (RsvgMarker *) rsvg_acquire_node (ctx, marker_name);
+    if (self == NULL || RSVG_NODE_TYPE (&self->super) != RSVG_NODE_TYPE_MARKER)
+      {
+        rsvg_release_node (ctx, &self->super);
+        return;
+      }
+
+    cairo_matrix_init_translate (&taffine, xpos, ypos);
+    cairo_matrix_multiply (&affine, &taffine, &state->affine);
 
     if (self->orientAuto)
-        rotation = orient * 180. / M_PI;
+        rotation = orient;
     else
-        rotation = self->orient;
-    _rsvg_affine_rotate (taffine, rotation);
-    _rsvg_affine_multiply (affine, taffine, affine);
+        rotation = self->orient * M_PI / 180.;
+
+    cairo_matrix_init_rotate (&taffine, rotation);
+    cairo_matrix_multiply (&affine, &taffine, &affine);
 
     if (self->bbox) {
-        _rsvg_affine_scale (taffine, linewidth, linewidth);
-        _rsvg_affine_multiply (affine, taffine, affine);
+        cairo_matrix_init_scale (&taffine, linewidth, linewidth);
+        cairo_matrix_multiply (&affine, &taffine, &affine);
     }
 
     if (self->vbox.active) {
-
         double w, h, x, y;
         w = _rsvg_css_normalize_length (&self->width, ctx, 'h');
         h = _rsvg_css_normalize_length (&self->height, ctx, 'v');
@@ -129,48 +143,42 @@ rsvg_marker_render (RsvgMarker * self, gdouble x, gdouble y, gdouble orient, gdo
         y = 0;
 
         rsvg_preserve_aspect_ratio (self->preserve_aspect_ratio,
-                                    self->vbox.w, self->vbox.h, &w, &h, &x, &y);
-
-        x = -self->vbox.x * w / self->vbox.w;
-        y = -self->vbox.y * h / self->vbox.h;
-
-        taffine[0] = w / self->vbox.w;
-        taffine[1] = 0.;
-        taffine[2] = 0.;
-        taffine[3] = h / self->vbox.h;
-        taffine[4] = x;
-        taffine[5] = y;
-        _rsvg_affine_multiply (affine, taffine, affine);
-        _rsvg_push_view_box (ctx, self->vbox.w, self->vbox.h);
+                                    self->vbox.rect.width,
+                                    self->vbox.rect.height,
+                                    &w, &h, &x, &y);
+
+        cairo_matrix_init_scale (&taffine, w / self->vbox.rect.width, h / self->vbox.rect.height);
+        cairo_matrix_multiply (&affine, &taffine, &affine);
+
+        _rsvg_push_view_box (ctx, self->vbox.rect.width, self->vbox.rect.height);
     }
-    _rsvg_affine_translate (taffine,
-                            -_rsvg_css_normalize_length (&self->refX, ctx, 'h'),
-                            -_rsvg_css_normalize_length (&self->refY, ctx, 'v'));
-    _rsvg_affine_multiply (affine, taffine, affine);
 
+    cairo_matrix_init_translate (&taffine,
+                                 -_rsvg_css_normalize_length (&self->refX, ctx, 'h'),
+                                 -_rsvg_css_normalize_length (&self->refY, ctx, 'v'));
+    cairo_matrix_multiply (&affine, &taffine, &affine);
 
     rsvg_state_push (ctx);
-    state = rsvg_state_current (ctx);
+    state = rsvg_current_state (ctx);
 
-    rsvg_state_finalize (state);
-    rsvg_state_init (state);
+    rsvg_state_reinit (state);
 
     rsvg_state_reconstruct (state, &self->super);
 
-    for (i = 0; i < 6; i++)
-        state->affine[i] = affine[i];
+    state->affine = affine;
 
     rsvg_push_discrete_layer (ctx);
 
-    state = rsvg_state_current (ctx);
+    state = rsvg_current_state (ctx);
 
     if (!state->overflow) {
-	if (self->vbox.active)
-	    rsvg_add_clipping_rect (ctx, self->vbox.x, self->vbox.y, self->vbox.w, self->vbox.h);
-	else
-	    rsvg_add_clipping_rect (ctx, 0, 0,
-				    _rsvg_css_normalize_length (&self->width, ctx, 'h'),
-				    _rsvg_css_normalize_length (&self->height, ctx, 'v'));
+        if (self->vbox.active)
+            rsvg_add_clipping_rect (ctx, self->vbox.rect.x, self->vbox.rect.y,
+                                    self->vbox.rect.width, self->vbox.rect.height);
+        else
+            rsvg_add_clipping_rect (ctx, 0, 0,
+                                    _rsvg_css_normalize_length (&self->width, ctx, 'h'),
+                                    _rsvg_css_normalize_length (&self->height, ctx, 'v'));
     }
 
     for (i = 0; i < self->super.children->len; i++) {
@@ -185,123 +193,453 @@ rsvg_marker_render (RsvgMarker * self, gdouble x, gdouble y, gdouble orient, gdo
     rsvg_state_pop (ctx);
     if (self->vbox.active)
         _rsvg_pop_view_box (ctx);
+
+    rsvg_release_node (ctx, (RsvgNode *) self);
 }
 
-RsvgNode *
-rsvg_marker_parse (const RsvgDefs * defs, const char *str)
+typedef struct {
+    gboolean is_degenerate; /* If true, only (p1x, p1y) are valid.  If false, all are valid */
+    double p1x, p1y;
+    double p2x, p2y;
+    double p3x, p3y;
+    double p4x, p4y;
+} Segment;
+
+typedef enum {
+    SEGMENT_START,
+    SEGMENT_END,
+} SegmentState;
+
+/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
+ *
+ * 1. segment->is_degenerate = TRUE => the segment is actually a single point (segment->p1x, segment->p1y)
+ *
+ * 2. segment->is_degenerate = FALSE => either a lineto or a curveto (or the effective lineto that results from a closepath).
+ *    We have the following points:
+ *       P1 = (p1x, p1y)
+ *       P2 = (p2x, p2y)
+ *       P3 = (p3x, p3y)
+ *       P4 = (p4x, p4y)
+ *
+ *    The start and end points are P1 and P4, respectively.
+ *    The tangent at the start point is given by the vector (P2 - P1).
+ *    The tangent at the end point is given by the vector (P4 - P3).
+ *    The tangents also work if the segment refers to a lineto (they will both just point in the same direction).
+ */
+
+#define EPSILON 1e-10
+#define DOUBLE_EQUALS(a, b) (fabs ((a) - (b)) < EPSILON)
+
+static void
+path_to_segments (const cairo_path_t *path,
+                  Segment **out_segments,
+                  int *num_segments)
 {
-    char *name;
+    int i;
+    double last_x, last_y;
+    double cur_x, cur_y;
+    double subpath_start_x, subpath_start_y;
+    int max_segments;
+    int segment_num;
+    Segment *segments;
+    SegmentState state;
+
+    max_segments = path->num_data; /* We'll generate maximum this many segments */
+    segments = g_new (Segment, max_segments);
+    *out_segments = segments;
+
+    last_x = last_y = cur_x = cur_y = subpath_start_x = subpath_start_y = 0.0;
+
+    segment_num = -1;
+    state = SEGMENT_END;
+
+    for (i = 0; i < path->num_data; i += path->data[i].header.length) {
+        last_x = cur_x;
+        last_y = cur_y;
+
+        switch (path->data[i].header.type) {
+        case CAIRO_PATH_MOVE_TO:
+            segment_num++;
+            g_assert (segment_num < max_segments);
+
+            g_assert (i + 1 < path->num_data);
+            cur_x = path->data[i + 1].point.x;
+            cur_y = path->data[i + 1].point.y;
+
+            subpath_start_x = cur_x;
+            subpath_start_y = cur_y;
+
+            segments[segment_num].is_degenerate = TRUE;
+
+            segments[segment_num].p1x = cur_x;
+            segments[segment_num].p1y = cur_y;
+
+            state = SEGMENT_START;
+
+            break;
+
+        case CAIRO_PATH_LINE_TO:
+            g_assert (i + 1 < path->num_data);
+            cur_x = path->data[i + 1].point.x;
+            cur_y = path->data[i + 1].point.y;
+
+            if (state == SEGMENT_START) {
+                segments[segment_num].is_degenerate = FALSE;
+                state = SEGMENT_END;
+            } else {
+                segment_num++;
+                g_assert (segment_num < max_segments);
+
+                segments[segment_num].is_degenerate = FALSE;
+
+                segments[segment_num].p1x = last_x;
+                segments[segment_num].p1y = last_y;
+            }
+
+            segments[segment_num].p2x = cur_x;
+            segments[segment_num].p2y = cur_y;
 
-    name = rsvg_get_url_string (str);
-    if (name) {
-        RsvgNode *val;
-        val = rsvg_defs_lookup (defs, name);
-        g_free (name);
+            segments[segment_num].p3x = last_x;
+            segments[segment_num].p3y = last_y;
 
-        if (val && (!strcmp (val->type->str, "marker")))
-            return val;
+            segments[segment_num].p4x = cur_x;
+            segments[segment_num].p4y = cur_y;
+
+            break;
+
+        case CAIRO_PATH_CURVE_TO:
+            g_assert (i + 3 < path->num_data);
+            cur_x = path->data[i + 3].point.x;
+            cur_y = path->data[i + 3].point.y;
+
+            if (state == SEGMENT_START) {
+                segments[segment_num].is_degenerate = FALSE;
+                state = SEGMENT_END;
+            } else {
+                segment_num++;
+                g_assert (segment_num < max_segments);
+
+                segments[segment_num].is_degenerate = FALSE;
+
+                segments[segment_num].p1x = last_x;
+                segments[segment_num].p1y = last_y;
+            }
+
+            segments[segment_num].p2x = path->data[i + 1].point.x;
+            segments[segment_num].p2y = path->data[i + 1].point.y;
+
+            segments[segment_num].p3x = path->data[i + 2].point.x;
+            segments[segment_num].p3y = path->data[i + 2].point.y;
+
+            segments[segment_num].p4x = cur_x;
+            segments[segment_num].p4y = cur_y;
+
+            /* Fix the tangents for when the middle control points coincide with their respective endpoints */
+
+            if (DOUBLE_EQUALS (segments[segment_num].p2x, segments[segment_num].p1x)
+                && DOUBLE_EQUALS (segments[segment_num].p2y, segments[segment_num].p1y)) {
+                segments[segment_num].p2x = segments[segment_num].p3x;
+                segments[segment_num].p2y = segments[segment_num].p3y;
+            }
+
+            if (DOUBLE_EQUALS (segments[segment_num].p3x, segments[segment_num].p4x)
+                && DOUBLE_EQUALS (segments[segment_num].p3y, segments[segment_num].p4y)) {
+                segments[segment_num].p3x = segments[segment_num].p2x;
+                segments[segment_num].p3y = segments[segment_num].p2y;
+            }
+
+            break;
+
+        case CAIRO_PATH_CLOSE_PATH:
+            cur_x = subpath_start_x;
+            cur_y = subpath_start_y;
+
+            if (state == SEGMENT_START) {
+                segments[segment_num].is_degenerate = FALSE;
+
+                segments[segment_num].p2x = cur_x;
+                segments[segment_num].p2y = cur_y;
+
+                segments[segment_num].p3x = last_x;
+                segments[segment_num].p3y = last_y;
+
+                segments[segment_num].p4x = cur_x;
+                segments[segment_num].p4y = cur_y;
+
+                state = SEGMENT_END;
+            } else {
+                /* nothing; closepath after moveto (or a single lone closepath) does nothing */
+            }
+
+            break;
+
+        default:
+            g_assert_not_reached ();
+        }
     }
-    return NULL;
+
+    *num_segments = segment_num + 1;
+    g_assert (*num_segments <= max_segments);
 }
 
+static gboolean
+points_equal (double x1, double y1, double x2, double y2)
+{
+    return DOUBLE_EQUALS (x1, x2) && DOUBLE_EQUALS (y1, y2);
+}
+
+/* A segment is zero length if it is degenerate, or if all four control points
+ * coincide (the first and last control points may coincide, but the others may
+ * define a loop - thus nonzero length)
+ */
+static gboolean
+is_zero_length_segment (Segment *segment)
+{
+    double p1x, p1y;
+    double p2x, p2y;
+    double p3x, p3y;
+    double p4x, p4y;
+
+    if (segment->is_degenerate)
+        return TRUE;
+
+    p1x = segment->p1x;
+    p1y = segment->p1y;
+
+    p2x = segment->p2x;
+    p2y = segment->p2y;
+
+    p3x = segment->p3x;
+    p3y = segment->p3y;
+
+    p4x = segment->p4x;
+    p4y = segment->p4y;
+
+    return (points_equal (p1x, p1y, p2x, p2y)
+            && points_equal (p1x, p1y, p3x, p3y)
+            && points_equal (p1x, p1y, p4x, p4y));
+}
+
+/* The SVG spec 1.1 says http://www.w3.org/TR/SVG/implnote.html#PathElementImplementationNotes
+ *
+ * Certain line-capping and line-joining situations and markers
+ * require that a path segment have directionality at its start and
+ * end points. Zero-length path segments have no directionality. In
+ * these cases, the following algorithm is used to establish
+ * directionality:  to determine the directionality of the start
+ * point of a zero-length path segment, go backwards in the path
+ * data specification within the current subpath until you find a
+ * segment which has directionality at its end point (e.g., a path
+ * segment with non-zero length) and use its ending direction;
+ * otherwise, temporarily consider the start point to lack
+ * directionality. Similarly, to determine the directionality of the
+ * end point of a zero-length path segment, go forwards in the path
+ * data specification within the current subpath until you find a
+ * segment which has directionality at its start point (e.g., a path
+ * segment with non-zero length) and use its starting direction;
+ * otherwise, temporarily consider the end point to lack
+ * directionality. If the start point has directionality but the end
+ * point doesn't, then the end point uses the start point's
+ * directionality. If the end point has directionality but the start
+ * point doesn't, then the start point uses the end point's
+ * directionality. Otherwise, set the directionality for the path
+ * segment's start and end points to align with the positive x-axis
+ * in user space.
+ */
+static gboolean
+find_incoming_directionality_backwards (Segment *segments, int num_segments, int start_index, double *vx, double *vy)
+{
+    int j;
+    gboolean found;
+
+    /* "go backwards ... within the current subpath until ... segment which has directionality at its end point" */
+
+    found = FALSE;
+
+    for (j = start_index; j >= 0; j--) {
+        if (segments[j].is_degenerate)
+            break; /* reached the beginning of the subpath as we ran into a standalone point */
+        else {
+            if (is_zero_length_segment (&segments[j]))
+                continue;
+            else {
+                found = TRUE;
+                break;
+            }
+        }
+    }
+
+    if (found) {
+        g_assert (j >= 0);
+        *vx = segments[j].p4x - segments[j].p3x;
+        *vy = segments[j].p4y - segments[j].p3y;
+        return TRUE;
+    } else {
+        *vx = 0.0;
+        *vy = 0.0;
+        return FALSE;
+    }
+}
+
+static gboolean
+find_outgoing_directionality_forwards (Segment *segments, int num_segments, int start_index, double *vx, double *vy)
+{
+    int j;
+    gboolean found;
+
+    /* "go forwards ... within the current subpath until ... segment which has directionality at its start point" */
+
+    found = FALSE;
+
+    for (j = start_index; j < num_segments; j++) {
+        if (segments[j].is_degenerate)
+            break; /* reached the end of a subpath as we ran into a standalone point */
+        else {
+            if (is_zero_length_segment (&segments[j]))
+                continue;
+            else {
+                found = TRUE;
+                break;
+            }
+        }
+    }
+
+    if (found) {
+        g_assert (j < num_segments);
+        *vx = segments[j].p2x - segments[j].p1x;
+        *vy = segments[j].p2y - segments[j].p1y;
+        return TRUE;
+    } else {
+        *vx = 0.0;
+        *vy = 0.0;
+        return FALSE;
+    }
+}
+
+static double
+angle_from_vector (double vx, double vy)
+{
+    double angle;
+
+    angle = atan2 (vy, vx);
+
+    if (isnan (angle))
+        return 0.0;
+    else
+        return angle;
+}
+
+typedef enum {
+    NO_SUBPATH,
+    IN_SUBPATH,
+} SubpathState;
+
 void
-rsvg_render_markers (const RsvgBpathDef * bpath_def, RsvgDrawingCtx * ctx)
+rsvg_render_markers (RsvgDrawingCtx * ctx,
+                     const cairo_path_t *path)
 {
+    RsvgState *state;
+    double linewidth;
+    const char *startmarker;
+    const char *middlemarker;
+    const char *endmarker;
+
     int i;
+    double incoming_vx, incoming_vy;
+    double outgoing_vx, outgoing_vy;
 
-    double x, y;
-    double lastx, lasty;
-    double nextx, nexty;
-    double linewidth;
-    RsvgPathcode code, lastcode, nextcode;
+    Segment *segments;
+    int num_segments;
 
-    RsvgState *state;
-    RsvgMarker *startmarker;
-    RsvgMarker *middlemarker;
-    RsvgMarker *endmarker;
+    SubpathState subpath_state;
 
-    state = rsvg_state_current (ctx);
+    state = rsvg_current_state (ctx);
 
     linewidth = _rsvg_css_normalize_length (&state->stroke_width, ctx, 'o');
-    startmarker = (RsvgMarker *) state->startMarker;
-    middlemarker = (RsvgMarker *) state->middleMarker;
-    endmarker = (RsvgMarker *) state->endMarker;
+    startmarker = state->startMarker;
+    middlemarker = state->middleMarker;
+    endmarker = state->endMarker;
+
+    if (linewidth == 0)
+        return;
 
     if (!startmarker && !middlemarker && !endmarker)
         return;
 
-    x = 0;
-    y = 0;
-    code = RSVG_END;
-    nextx = bpath_def->bpath[0].x3;
-    nexty = bpath_def->bpath[0].y3;
-    nextcode = bpath_def->bpath[0].code;
-
-    for (i = 0; i < bpath_def->n_bpath - 1; i++) {
-        lastx = x;
-        lasty = y;
-	lastcode = code;
-        x = nextx;
-        y = nexty;
-	code = nextcode;
-        nextx = bpath_def->bpath[i + 1].x3;
-        nexty = bpath_def->bpath[i + 1].y3;
-	nextcode = bpath_def->bpath[i + 1].code;
-
-        if (nextcode == RSVG_MOVETO 
-	    || nextcode == RSVG_MOVETO_OPEN 
-	    || nextcode == RSVG_END) {
-            if (endmarker) {
-		if (code == RSVG_CURVETO) 
-		    rsvg_marker_render (endmarker, x, y, 
-					atan2 (y - bpath_def->bpath[i].y2, 
-					       x - bpath_def->bpath[i].x2), 
-					linewidth, ctx);
-		else
-		    rsvg_marker_render (endmarker, x, y, atan2 (y - lasty, x - lastx), linewidth, ctx);
-	    }
-        } else if (code == RSVG_MOVETO
-                   || code == RSVG_MOVETO_OPEN) {
-            if (startmarker) {
-		if (nextcode == RSVG_CURVETO)
-		    rsvg_marker_render (startmarker, x, y, 
-					atan2 (bpath_def->bpath[i + 1].y1 - y, 
-					       bpath_def->bpath[i + 1].x1 - x), linewidth,
-					ctx);
-		else 
-		    rsvg_marker_render (startmarker, x, y, atan2 (nexty - y, nextx - x), linewidth,
-					ctx);
-	    }
-	} else {
-	    if (middlemarker) {
-		double xdifin, ydifin, xdifout, ydifout, intot, outtot, angle;
-
-		if (code == RSVG_CURVETO) {
-		    xdifin = x - bpath_def->bpath[i].x2;
-		    ydifin = y - bpath_def->bpath[i].y2;
-		} else {
-		    xdifin = x - lastx;
-		    ydifin = y - lasty;
-		}
-		if (nextcode == RSVG_CURVETO) {
-		    xdifout = bpath_def->bpath[i+1].x1 - x;
-		    ydifout = bpath_def->bpath[i+1].y1 - y;
-		} else {
-		    xdifout = nextx - x;
-		    ydifout = nexty - y;
-		}
-
-		intot = sqrt (xdifin * xdifin + ydifin * ydifin);
-		outtot = sqrt (xdifout * xdifout + ydifout * ydifout);
-
-                xdifin /= intot;
-                ydifin /= intot;
-                xdifout /= outtot;
-                ydifout /= outtot;
-
-                angle = atan2 ((ydifin + ydifout) / 2, (xdifin + xdifout) / 2);
-                rsvg_marker_render (middlemarker, x, y, angle, linewidth, ctx);
+    if (path->num_data <= 0)
+        return;
+
+    /* Convert the path to a list of segments and bare points (i.e. degenerate segments) */
+    path_to_segments (path, &segments, &num_segments);
+
+    subpath_state = NO_SUBPATH;
+
+    for (i = 0; i < num_segments; i++) {
+        incoming_vx = incoming_vy = outgoing_vx = outgoing_vy = 0.0;
+
+        if (segments[i].is_degenerate) {
+            if (subpath_state == IN_SUBPATH) {
+                g_assert (i > 0);
+
+                /* Got a lone point after a subpath; render the subpath's end marker first */
+
+                find_incoming_directionality_backwards (segments, num_segments, i - 1, &incoming_vx, &incoming_vy);
+                rsvg_marker_render (endmarker, segments[i - 1].p4x, segments[i - 1].p4y, angle_from_vector (incoming_vx, incoming_vy), linewidth, ctx);
+            }
+
+            /* Render marker for the lone point; no directionality */
+            rsvg_marker_render (middlemarker, segments[i].p1x, segments[i].p1y, 0.0, linewidth, ctx);
+
+            subpath_state = NO_SUBPATH;
+        } else {
+            /* Not a degenerate segment */
+
+            if (subpath_state == NO_SUBPATH) {
+                find_outgoing_directionality_forwards (segments, num_segments, i, &outgoing_vx, &outgoing_vy);
+                rsvg_marker_render (startmarker, segments[i].p1x, segments[i].p1y, angle_from_vector (outgoing_vx, outgoing_vy), linewidth, ctx);
+
+                subpath_state = IN_SUBPATH;
+            } else {
+                /* subpath_state == IN_SUBPATH */
+
+                gboolean has_incoming, has_outgoing;
+                double incoming, outgoing;
+                double angle;
+
+                g_assert (i > 0);
+
+                has_incoming = find_incoming_directionality_backwards (segments, num_segments, i - 1, &incoming_vx, &incoming_vy);
+                has_outgoing = find_outgoing_directionality_forwards (segments, num_segments, i, &outgoing_vx, &outgoing_vy);
+
+                if (has_incoming)
+                    incoming = angle_from_vector (incoming_vx, incoming_vy);
+
+                if (has_outgoing)
+                    outgoing = angle_from_vector (outgoing_vx, outgoing_vy);
+
+                if (has_incoming && has_outgoing)
+                    angle = (incoming + outgoing) / 2;
+                else if (has_incoming)
+                    angle = incoming;
+                else if (has_outgoing)
+                    angle = outgoing;
+                else
+                    angle = 0.0;
+
+                rsvg_marker_render (middlemarker, segments[i].p1x, segments[i].p1y, angle, linewidth, ctx);
             }
         }
     }
+
+    /* Finally, render the last point */
+
+    if (num_segments > 0) {
+        if (!segments[num_segments - 1].is_degenerate) {
+            find_incoming_directionality_backwards (segments, num_segments, num_segments - 1, &incoming_vx, &incoming_vy);
+
+            rsvg_marker_render (endmarker, segments[num_segments - 1].p4x, segments[num_segments - 1].p4y, angle_from_vector (incoming_vx, incoming_vy), linewidth, ctx);
+        }
+    }
+
+    g_free (segments);
 }
diff --git a/rsvg-marker.h b/rsvg-marker.h
index 39817ee..e4e6726 100644
--- a/rsvg-marker.h
+++ b/rsvg-marker.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-marker.h: Marker loading and rendering
 
@@ -41,11 +42,10 @@ struct _RsvgMarker {
     RsvgViewBox vbox;
 };
 
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_marker	    (void);
-void	     rsvg_marker_render	    (RsvgMarker * self, gdouble x, gdouble y, 
-				     gdouble orient, gdouble linewidth, RsvgDrawingCtx * ctx);
-RsvgNode    *rsvg_marker_parse	    (const RsvgDefs * defs, const char *str);
-void	     rsvg_render_markers    (const RsvgBpathDef * bpath_def, RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
+void	     rsvg_render_markers    (RsvgDrawingCtx *ctx, const cairo_path_t *path);
 
 G_END_DECLS
 
diff --git a/rsvg-mask.c b/rsvg-mask.c
index db58580..44dd100 100644
--- a/rsvg-mask.c
+++ b/rsvg-mask.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg-filter.c: Provides filters
  
@@ -73,7 +74,7 @@ rsvg_new_mask (void)
     RsvgMask *mask;
 
     mask = g_new (RsvgMask, 1);
-    _rsvg_node_init (&mask->super);
+    _rsvg_node_init (&mask->super, RSVG_NODE_TYPE_MASK);
     mask->maskunits = objectBoundingBox;
     mask->contentunits = userSpaceOnUse;
     mask->x = _rsvg_css_parse_length ("0");
@@ -101,40 +102,6 @@ rsvg_get_url_string (const char *str)
     return NULL;
 }
 
-RsvgNode *
-rsvg_mask_parse (const RsvgDefs * defs, const char *str)
-{
-    char *name;
-
-    name = rsvg_get_url_string (str);
-    if (name) {
-        RsvgNode *val;
-        val = rsvg_defs_lookup (defs, name);
-        g_free (name);
-
-        if (val && (!strcmp (val->type->str, "mask")))
-            return val;
-    }
-    return NULL;
-}
-
-RsvgNode *
-rsvg_clip_path_parse (const RsvgDefs * defs, const char *str)
-{
-    char *name;
-
-    name = rsvg_get_url_string (str);
-    if (name) {
-        RsvgNode *val;
-        val = rsvg_defs_lookup (defs, name);
-        g_free (name);
-
-        if (val && (!strcmp (val->type->str, "clipPath")))
-            return val;
-    }
-    return NULL;
-}
-
 static void
 rsvg_clip_path_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
@@ -167,7 +134,7 @@ rsvg_new_clip_path (void)
     RsvgClipPath *clip_path;
 
     clip_path = g_new (RsvgClipPath, 1);
-    _rsvg_node_init (&clip_path->super);
+    _rsvg_node_init (&clip_path->super, RSVG_NODE_TYPE_CLIP_PATH);
     clip_path->units = userSpaceOnUse;
     clip_path->super.set_atts = rsvg_clip_path_set_atts;
     clip_path->super.free = _rsvg_node_free;
diff --git a/rsvg-mask.h b/rsvg-mask.h
index db225b3..bb6e4e2 100644
--- a/rsvg-mask.h
+++ b/rsvg-mask.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-mask.h : Provides Masks
 
@@ -45,8 +46,8 @@ struct _RsvgMask {
     RsvgMaskUnits contentunits;
 };
 
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_mask	    (void);
-RsvgNode *rsvg_mask_parse   (const RsvgDefs * defs, const char *str);
 
 typedef struct _RsvgClipPath RsvgClipPath;
 
@@ -55,8 +56,8 @@ struct _RsvgClipPath {
     RsvgCoordUnits units;
 };
 
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_clip_path	(void);
-RsvgNode *rsvg_clip_path_parse	(const RsvgDefs * defs, const char *str);
 
 G_END_DECLS
 #endif
diff --git a/rsvg-paint-server.c b/rsvg-paint-server.c
index 0132b1b..3519467 100644
--- a/rsvg-paint-server.c
+++ b/rsvg-paint-server.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg-paint-server.c: Implement the SVG paint server abstraction.
  
@@ -36,123 +37,86 @@
 #include "rsvg-css.h"
 
 static RsvgPaintServer *
-rsvg_paint_server_solid (guint32 rgb)
+rsvg_paint_server_solid (guint32 argb)
 {
     RsvgPaintServer *result = g_new (RsvgPaintServer, 1);
 
     result->refcnt = 1;
     result->type = RSVG_PAINT_SERVER_SOLID;
-    result->core.colour = g_new (RsvgSolidColour, 1);
-    result->core.colour->rgb = rgb;
-    result->core.colour->currentcolour = FALSE;
+    result->core.color = g_new (RsvgSolidColor, 1);
+    result->core.color->argb = argb;
+    result->core.color->currentcolor = FALSE;
 
     return result;
 }
 
 static RsvgPaintServer *
-rsvg_paint_server_solid_current_colour ()
+rsvg_paint_server_solid_current_color (void)
 {
     RsvgPaintServer *result = g_new (RsvgPaintServer, 1);
 
     result->refcnt = 1;
     result->type = RSVG_PAINT_SERVER_SOLID;
-    result->core.colour = g_new (RsvgSolidColour, 1);
-    result->core.colour->currentcolour = TRUE;
+    result->core.color = g_new (RsvgSolidColor, 1);
+    result->core.color->currentcolor = TRUE;
 
     return result;
 }
 
 static RsvgPaintServer *
-rsvg_paint_server_lin_grad (RsvgLinearGradient * gradient)
+rsvg_paint_server_iri (char *iri)
 {
     RsvgPaintServer *result = g_new (RsvgPaintServer, 1);
 
     result->refcnt = 1;
-    result->type = RSVG_PAINT_SERVER_LIN_GRAD;
-    result->core.lingrad = gradient;
-
-    return result;
-}
-
-static RsvgPaintServer *
-rsvg_paint_server_rad_grad (RsvgRadialGradient * gradient)
-{
-    RsvgPaintServer *result = g_new (RsvgPaintServer, 1);
-
-    result->refcnt = 1;
-    result->type = RSVG_PAINT_SERVER_RAD_GRAD;
-    result->core.radgrad = gradient;
-
-    return result;
-}
-
-static RsvgPaintServer *
-rsvg_paint_server_pattern (RsvgPattern * pattern)
-{
-    RsvgPaintServer *result = g_new (RsvgPaintServer, 1);
-
-    result->refcnt = 1;
-    result->type = RSVG_PAINT_SERVER_PATTERN;
-    rsvg_pattern_fix_fallback (pattern);
-    result->core.pattern = pattern;
+    result->type = RSVG_PAINT_SERVER_IRI;
+    result->core.iri = iri;
 
     return result;
 }
 
 /**
- * rsvg_paint_server_parse: Parse an SVG paint specification.
- * @defs: Defs for looking up gradients.
+ * rsvg_paint_server_parse:
  * @str: The SVG paint specification string to parse.
  *
  * Parses the paint specification @str, creating a new paint server
  * object.
  *
- * Return value: The newly created paint server, or NULL on error.
+ * Return value: (nullable): The newly created paint server, or %NULL
+ *   on error.
  **/
 RsvgPaintServer *
-rsvg_paint_server_parse (gboolean * inherit, const RsvgDefs * defs, const char *str,
-                         guint32 current_color)
+rsvg_paint_server_parse (gboolean * inherit, const char *str)
 {
     char *name;
-    guint32 rgb;
+    guint32 argb;
     if (inherit != NULL)
         *inherit = 1;
-    if (!strcmp (str, "none"))
+    if (str == NULL || !strcmp (str, "none"))
         return NULL;
 
     name = rsvg_get_url_string (str);
     if (name) {
-        RsvgNode *val;
-        val = rsvg_defs_lookup (defs, name);
-        g_free (name);
-
-        if (val == NULL)
-            return NULL;
-        if (!strcmp (val->type->str, "linearGradient"))
-            return rsvg_paint_server_lin_grad ((RsvgLinearGradient *) val);
-        else if (!strcmp (val->type->str, "radialGradient"))
-            return rsvg_paint_server_rad_grad ((RsvgRadialGradient *) val);
-        else if (!strcmp (val->type->str, "pattern"))
-            return rsvg_paint_server_pattern ((RsvgPattern *) val);
-        else
-            return NULL;
+        return rsvg_paint_server_iri (name);
     } else if (!strcmp (str, "inherit")) {
         if (inherit != NULL)
             *inherit = 0;
         return rsvg_paint_server_solid (0);
     } else if (!strcmp (str, "currentColor")) {
         RsvgPaintServer *ps;
-        ps = rsvg_paint_server_solid_current_colour ();
+        ps = rsvg_paint_server_solid_current_color ();
         return ps;
     } else {
-        rgb = rsvg_css_parse_color (str, inherit);
-        return rsvg_paint_server_solid (rgb);
+        argb = rsvg_css_parse_color (str, inherit);
+        return rsvg_paint_server_solid (argb);
     }
 }
 
 /**
- * rsvg_paint_server_ref: Reference a paint server object.
+ * rsvg_paint_server_ref:
  * @ps: The paint server object to reference.
+ *
+ * Reference a paint server object.
  **/
 void
 rsvg_paint_server_ref (RsvgPaintServer * ps)
@@ -163,8 +127,10 @@ rsvg_paint_server_ref (RsvgPaintServer * ps)
 }
 
 /**
- * rsvg_paint_server_unref: Unreference a paint server object.
+ * rsvg_paint_server_unref:
  * @ps: The paint server object to unreference.
+ *
+ * Unreference a paint server object.
  **/
 void
 rsvg_paint_server_unref (RsvgPaintServer * ps)
@@ -173,7 +139,9 @@ rsvg_paint_server_unref (RsvgPaintServer * ps)
         return;
     if (--ps->refcnt == 0) {
         if (ps->type == RSVG_PAINT_SERVER_SOLID)
-            g_free (ps->core.colour);
+            g_free (ps->core.color);
+        else if (ps->type == RSVG_PAINT_SERVER_IRI)
+            g_free (ps->core.iri);
         g_free (ps);
     }
 }
@@ -223,7 +191,7 @@ RsvgNode *
 rsvg_new_stop (void)
 {
     RsvgGradientStop *stop = g_new (RsvgGradientStop, 1);
-    _rsvg_node_init (&stop->super);
+    _rsvg_node_init (&stop->super, RSVG_NODE_TYPE_STOP);
     stop->super.set_atts = rsvg_stop_set_atts;
     stop->offset = 0;
     stop->rgba = 0;
@@ -257,18 +225,18 @@ rsvg_linear_gradient_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBa
         }
         if ((value = rsvg_property_bag_lookup (atts, "spreadMethod"))) {
             if (!strcmp (value, "pad")) {
-                grad->spread = RSVG_GRADIENT_PAD;
+                grad->spread = CAIRO_EXTEND_PAD;
             } else if (!strcmp (value, "reflect")) {
-                grad->spread = RSVG_GRADIENT_REFLECT;
+                grad->spread = CAIRO_EXTEND_REFLECT;
             } else if (!strcmp (value, "repeat")) {
-                grad->spread = RSVG_GRADIENT_REPEAT;
+                grad->spread = CAIRO_EXTEND_REPEAT;
             }
             grad->hasspread = TRUE;
         }
-        if ((value = rsvg_property_bag_lookup (atts, "xlink:href")))
-            rsvg_defs_add_resolver (ctx->priv->defs, &grad->fallback, value);
+        g_free (grad->fallback);
+        grad->fallback = g_strdup (rsvg_property_bag_lookup (atts, "xlink:href"));
         if ((value = rsvg_property_bag_lookup (atts, "gradientTransform"))) {
-            rsvg_parse_transform (grad->affine, value);
+            rsvg_parse_transform (&grad->affine, value);
             grad->hastransform = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "color")))
@@ -284,20 +252,28 @@ rsvg_linear_gradient_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBa
     }
 }
 
+static void
+rsvg_linear_gradient_free (RsvgNode * node)
+{
+    RsvgLinearGradient *self = (RsvgLinearGradient *) node;
+    g_free (self->fallback);
+    _rsvg_node_free (node);
+}
 
 RsvgNode *
 rsvg_new_linear_gradient (void)
 {
     RsvgLinearGradient *grad = NULL;
     grad = g_new (RsvgLinearGradient, 1);
-    _rsvg_node_init (&grad->super);
-    _rsvg_affine_identity (grad->affine);
+    _rsvg_node_init (&grad->super, RSVG_NODE_TYPE_LINEAR_GRADIENT);
+    cairo_matrix_init_identity (&grad->affine);
     grad->has_current_color = FALSE;
     grad->x1 = grad->y1 = grad->y2 = _rsvg_css_parse_length ("0");
     grad->x2 = _rsvg_css_parse_length ("1");
     grad->fallback = NULL;
     grad->obj_bbox = TRUE;
-    grad->spread = RSVG_GRADIENT_PAD;
+    grad->spread = CAIRO_EXTEND_PAD;
+    grad->super.free = rsvg_linear_gradient_free;
     grad->super.set_atts = rsvg_linear_gradient_set_atts;
     grad->hasx1 = grad->hasy1 = grad->hasx2 = grad->hasy2 = grad->hasbbox = grad->hasspread =
         grad->hastransform = FALSE;
@@ -337,10 +313,10 @@ rsvg_radial_gradient_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBa
             grad->fy = _rsvg_css_parse_length (value);
             grad->hasfy = TRUE;
         }
-        if ((value = rsvg_property_bag_lookup (atts, "xlink:href")))
-            rsvg_defs_add_resolver (ctx->priv->defs, &grad->fallback, value);
+        g_free (grad->fallback);
+        grad->fallback = g_strdup (rsvg_property_bag_lookup (atts, "xlink:href"));
         if ((value = rsvg_property_bag_lookup (atts, "gradientTransform"))) {
-            rsvg_parse_transform (grad->affine, value);
+            rsvg_parse_transform (&grad->affine, value);
             grad->hastransform = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "color"))) {
@@ -348,11 +324,11 @@ rsvg_radial_gradient_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBa
         }
         if ((value = rsvg_property_bag_lookup (atts, "spreadMethod"))) {
             if (!strcmp (value, "pad"))
-                grad->spread = RSVG_GRADIENT_PAD;
+                grad->spread = CAIRO_EXTEND_PAD;
             else if (!strcmp (value, "reflect"))
-                grad->spread = RSVG_GRADIENT_REFLECT;
+                grad->spread = CAIRO_EXTEND_REFLECT;
             else if (!strcmp (value, "repeat"))
-                grad->spread = RSVG_GRADIENT_REPEAT;
+                grad->spread = CAIRO_EXTEND_REPEAT;
             grad->hasspread = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "gradientUnits"))) {
@@ -366,18 +342,27 @@ rsvg_radial_gradient_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBa
     }
 }
 
+static void
+rsvg_radial_gradient_free (RsvgNode * node)
+{
+    RsvgRadialGradient *self = (RsvgRadialGradient *) node;
+    g_free (self->fallback);
+    _rsvg_node_free (node);
+}
+
 RsvgNode *
 rsvg_new_radial_gradient (void)
 {
 
     RsvgRadialGradient *grad = g_new (RsvgRadialGradient, 1);
-    _rsvg_node_init (&grad->super);
-    _rsvg_affine_identity (grad->affine);
+    _rsvg_node_init (&grad->super, RSVG_NODE_TYPE_RADIAL_GRADIENT);
+    cairo_matrix_init_identity (&grad->affine);
     grad->has_current_color = FALSE;
     grad->obj_bbox = TRUE;
-    grad->spread = RSVG_GRADIENT_PAD;
+    grad->spread = CAIRO_EXTEND_PAD;
     grad->fallback = NULL;
     grad->cx = grad->cy = grad->r = grad->fx = grad->fy = _rsvg_css_parse_length ("0.5");
+    grad->super.free = rsvg_radial_gradient_free;
     grad->super.set_atts = rsvg_radial_gradient_set_atts;
     grad->hascx = grad->hascy = grad->hasfx = grad->hasfy = grad->hasr = grad->hasbbox =
         grad->hasspread = grad->hastransform = FALSE;
@@ -413,16 +398,10 @@ rsvg_pattern_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts
             pattern->height = _rsvg_css_parse_length (value);
             pattern->hasheight = TRUE;
         }
-        if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) {
-            /* The (void *) cast is to avoid a GCC warning like:
-             * "warning: dereferencing type-punned pointer will break strict-aliasing rules"
-             * which is wrong for this code. (void *) introduces a compatible
-             * intermediate type in the cast list. */
-            rsvg_defs_add_resolver (ctx->priv->defs, (RsvgNode **) (void *) &pattern->fallback,
-                                    value);
-        }
+        g_free (pattern->fallback);
+        pattern->fallback = g_strdup (rsvg_property_bag_lookup (atts, "xlink:href"));
         if ((value = rsvg_property_bag_lookup (atts, "patternTransform"))) {
-            rsvg_parse_transform (pattern->affine, value);
+            rsvg_parse_transform (&pattern->affine, value);
             pattern->hastransform = TRUE;
         }
         if ((value = rsvg_property_bag_lookup (atts, "patternUnits"))) {
@@ -446,23 +425,31 @@ rsvg_pattern_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts
     }
 }
 
+static void
+rsvg_pattern_free (RsvgNode * node)
+{
+    RsvgPattern *self = (RsvgPattern *) node;
+    g_free (self->fallback);
+    _rsvg_node_free (node);
+}
+
 
 RsvgNode *
 rsvg_new_pattern (void)
 {
     RsvgPattern *pattern = g_new (RsvgPattern, 1);
-    _rsvg_node_init (&pattern->super);
+    _rsvg_node_init (&pattern->super, RSVG_NODE_TYPE_PATTERN);
+    cairo_matrix_init_identity (&pattern->affine);
     pattern->obj_bbox = TRUE;
     pattern->obj_cbbox = FALSE;
     pattern->x = pattern->y = pattern->width = pattern->height = _rsvg_css_parse_length ("0");
     pattern->fallback = NULL;
     pattern->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
     pattern->vbox.active = FALSE;
-    _rsvg_affine_identity (pattern->affine);
+    pattern->super.free = rsvg_pattern_free;
     pattern->super.set_atts = rsvg_pattern_set_atts;
     pattern->hasx = pattern->hasy = pattern->haswidth = pattern->hasheight = pattern->hasbbox =
-        pattern->hascbox = pattern->hasvbox = pattern->hasaspect = pattern->hastransform =
-        pattern->hasaspect = FALSE;
+        pattern->hascbox = pattern->hasvbox = pattern->hasaspect = pattern->hastransform = FALSE;
     return &pattern->super;
 }
 
@@ -471,191 +458,279 @@ hasstop (GPtrArray * lookin)
 {
     unsigned int i;
     for (i = 0; i < lookin->len; i++) {
-        if (!strcmp (((RsvgNode *) g_ptr_array_index (lookin, i))->type->str, "stop"))
+        RsvgNode *node = g_ptr_array_index (lookin, i);
+        if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_STOP)
             return 1;
     }
     return 0;
 }
 
-void
-rsvg_linear_gradient_fix_fallback (RsvgLinearGradient * grad)
+typedef const char * (* GetFallbackFn) (RsvgNode *node);
+typedef void (* ApplyFallbackFn) (RsvgNode *node, RsvgNode *fallback_node);
+
+/* Some SVG paint servers can reference a "parent" or "fallback" paint server
+ * through the xlink:href attribute (for example,
+ * http://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute )
+ * This is used to define a chain of properties to be resolved from each
+ * fallback.
+ */
+static void
+resolve_fallbacks (RsvgDrawingCtx *ctx,
+                   RsvgNode *data,
+                   RsvgNode *last_fallback,
+                   GetFallbackFn get_fallback,
+                   ApplyFallbackFn apply_fallback)
 {
-    RsvgNode *ufallback;
-    int i;
-    ufallback = grad->fallback;
-    while (ufallback != NULL) {
-        if (!strcmp (ufallback->type->str, "linearGradient")) {
-            RsvgLinearGradient *fallback = (RsvgLinearGradient *) ufallback;
-            if (!grad->hasx1 && fallback->hasx1) {
-                grad->hasx1 = TRUE;
-                grad->x1 = fallback->x1;
-            }
-            if (!grad->hasy1 && fallback->hasy1) {
-                grad->hasy1 = TRUE;
-                grad->y1 = fallback->y1;
-            }
-            if (!grad->hasx2 && fallback->hasx2) {
-                grad->hasx2 = TRUE;
-                grad->x2 = fallback->x2;
-            }
-            if (!grad->hasy2 && fallback->hasy2) {
-                grad->hasy2 = TRUE;
-                grad->y2 = fallback->y2;
-            }
-            if (!grad->hastransform && fallback->hastransform) {
-                grad->hastransform = TRUE;
-                for (i = 0; i < 6; i++)
-                    grad->affine[i] = fallback->affine[i];
-            }
-            if (!grad->hasspread && fallback->hasspread) {
-                grad->hasspread = TRUE;
-                grad->spread = fallback->spread;
-            }
-            if (!grad->hasbbox && fallback->hasbbox) {
-                grad->hasbbox = TRUE;
-                grad->obj_bbox = fallback->obj_bbox;
-            }
-            if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
-                grad->super.children = fallback->super.children;
-            }
-            ufallback = fallback->fallback;
-        } else if (!strcmp (ufallback->type->str, "radialGradient")) {
-            RsvgRadialGradient *fallback = (RsvgRadialGradient *) ufallback;
-            if (!grad->hastransform && fallback->hastransform) {
-                grad->hastransform = TRUE;
-                for (i = 0; i < 6; i++)
-                    grad->affine[i] = fallback->affine[i];
-            }
-            if (!grad->hasspread && fallback->hasspread) {
-                grad->hasspread = TRUE;
-                grad->spread = fallback->spread;
-            }
-            if (!grad->hasbbox && fallback->hasbbox) {
-                grad->hasbbox = TRUE;
-                grad->obj_bbox = fallback->obj_bbox;
-            }
-            if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
-                grad->super.children = fallback->super.children;
-            }
-            ufallback = fallback->fallback;
-        }
-    }
+    RsvgNode *fallback;
+    const char *fallback_id;
+
+    fallback_id = get_fallback (last_fallback);
+    if (fallback_id == NULL)
+        return;
+    fallback = rsvg_acquire_node (ctx, fallback_id);
+    if (fallback == NULL)
+      return;
+
+    apply_fallback (data, fallback);
+    resolve_fallbacks (ctx, data, fallback, get_fallback, apply_fallback);
+
+    rsvg_release_node (ctx, fallback);
 }
 
-void
-rsvg_radial_gradient_fix_fallback (RsvgRadialGradient * grad)
+static const char *
+gradient_get_fallback (RsvgNode *node)
 {
-    RsvgNode *ufallback;
-    int i;
-    ufallback = grad->fallback;
-    while (ufallback != NULL) {
-        if (!strcmp (ufallback->type->str, "radialGradient")) {
-            RsvgRadialGradient *fallback = (RsvgRadialGradient *) ufallback;
-            if (!grad->hascx && fallback->hascx) {
-                grad->hascx = TRUE;
-                grad->cx = fallback->cx;
-            }
-            if (!grad->hascy && fallback->hascy) {
-                grad->hascy = TRUE;
-                grad->cy = fallback->cy;
-            }
-            if (!grad->hasfx && fallback->hasfx) {
-                grad->hasfx = TRUE;
-                grad->fx = fallback->fx;
-            }
-            if (!grad->hasfy && fallback->hasfy) {
-                grad->hasfy = TRUE;
-                grad->fy = fallback->fy;
-            }
-            if (!grad->hasr && fallback->hasr) {
-                grad->hasr = TRUE;
-                grad->r = fallback->r;
-            }
-            if (!grad->hastransform && fallback->hastransform) {
-                grad->hastransform = TRUE;
-                for (i = 0; i < 6; i++)
-                    grad->affine[i] = fallback->affine[i];
-            }
-            if (!grad->hasspread && fallback->hasspread) {
-                grad->hasspread = TRUE;
-                grad->spread = fallback->spread;
-            }
-            if (!grad->hasbbox && fallback->hasbbox) {
-                grad->hasbbox = TRUE;
-                grad->obj_bbox = fallback->obj_bbox;
-            }
-            if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
-                grad->super.children = fallback->super.children;
-            }
-            ufallback = fallback->fallback;
-        } else if (!strcmp (ufallback->type->str, "linearGradient")) {
-            RsvgLinearGradient *fallback = (RsvgLinearGradient *) ufallback;
-            if (!grad->hastransform && fallback->hastransform) {
-                grad->hastransform = TRUE;
-                for (i = 0; i < 6; i++)
-                    grad->affine[i] = fallback->affine[i];
-            }
-            if (!grad->hasspread && fallback->hasspread) {
-                grad->hasspread = TRUE;
-                grad->spread = fallback->spread;
-            }
-            if (!grad->hasbbox && fallback->hasbbox) {
-                grad->hasbbox = TRUE;
-                grad->obj_bbox = fallback->obj_bbox;
-            }
-            if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
-                grad->super.children = fallback->super.children;
-            }
-            ufallback = fallback->fallback;
+    if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
+        RsvgLinearGradient *g = (RsvgLinearGradient *) node;
+        return g->fallback;
+    } else if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
+        RsvgRadialGradient *g = (RsvgRadialGradient *) node;
+        return g->fallback;
+    } else
+        return NULL;
+}
+
+static void
+linear_gradient_apply_fallback (RsvgNode *node, RsvgNode *fallback_node)
+{
+    RsvgLinearGradient *grad;
+
+    g_assert (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT);
+    grad = (RsvgLinearGradient *) node;
+
+    if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
+        RsvgLinearGradient *fallback = (RsvgLinearGradient *) fallback_node;
+
+        if (!grad->hasx1 && fallback->hasx1) {
+            grad->hasx1 = TRUE;
+            grad->x1 = fallback->x1;
+        }
+        if (!grad->hasy1 && fallback->hasy1) {
+            grad->hasy1 = TRUE;
+            grad->y1 = fallback->y1;
+        }
+        if (!grad->hasx2 && fallback->hasx2) {
+            grad->hasx2 = TRUE;
+            grad->x2 = fallback->x2;
+        }
+        if (!grad->hasy2 && fallback->hasy2) {
+            grad->hasy2 = TRUE;
+            grad->y2 = fallback->y2;
+        }
+        if (!grad->hastransform && fallback->hastransform) {
+            grad->hastransform = TRUE;
+            grad->affine = fallback->affine;
+        }
+        if (!grad->hasspread && fallback->hasspread) {
+            grad->hasspread = TRUE;
+            grad->spread = fallback->spread;
+        }
+        if (!grad->hasbbox && fallback->hasbbox) {
+            grad->hasbbox = TRUE;
+            grad->obj_bbox = fallback->obj_bbox;
+        }
+        if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
+            grad->super.children = fallback->super.children;
+        }
+    } else if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
+        RsvgRadialGradient *fallback = (RsvgRadialGradient *) fallback_node;
+
+        if (!grad->hastransform && fallback->hastransform) {
+            grad->hastransform = TRUE;
+            grad->affine = fallback->affine;
+        }
+        if (!grad->hasspread && fallback->hasspread) {
+            grad->hasspread = TRUE;
+            grad->spread = fallback->spread;
+        }
+        if (!grad->hasbbox && fallback->hasbbox) {
+            grad->hasbbox = TRUE;
+            grad->obj_bbox = fallback->obj_bbox;
+        }
+        if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
+            grad->super.children = fallback->super.children;
         }
     }
 }
 
-
 void
-rsvg_pattern_fix_fallback (RsvgPattern * pattern)
+rsvg_linear_gradient_fix_fallback (RsvgDrawingCtx *ctx, RsvgLinearGradient * grad)
 {
-    RsvgPattern *fallback;
-    int i;
-    for (fallback = pattern->fallback; fallback != NULL; fallback = fallback->fallback) {
-        if (!pattern->hasx && fallback->hasx) {
-            pattern->hasx = TRUE;
-            pattern->x = fallback->x;
+    resolve_fallbacks (ctx,
+                       (RsvgNode *) grad,
+                       (RsvgNode *) grad,
+                       gradient_get_fallback,
+                       linear_gradient_apply_fallback);
+}
+
+static void
+radial_gradient_apply_fallback (RsvgNode *node, RsvgNode *fallback_node)
+{
+    RsvgRadialGradient *grad;
+
+    g_assert (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT);
+    grad = (RsvgRadialGradient *) node;
+
+    if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
+        RsvgRadialGradient *fallback = (RsvgRadialGradient *) fallback_node;
+
+        if (!grad->hascx && fallback->hascx) {
+            grad->hascx = TRUE;
+            grad->cx = fallback->cx;
         }
-        if (!pattern->hasy && fallback->hasy) {
-            pattern->hasy = TRUE;
-            pattern->y = fallback->y;
+        if (!grad->hascy && fallback->hascy) {
+            grad->hascy = TRUE;
+            grad->cy = fallback->cy;
         }
-        if (!pattern->haswidth && fallback->haswidth) {
-            pattern->haswidth = TRUE;
-            pattern->width = fallback->width;
+        if (!grad->hasfx && fallback->hasfx) {
+            grad->hasfx = TRUE;
+            grad->fx = fallback->fx;
         }
-        if (!pattern->hasheight && fallback->hasheight) {
-            pattern->hasheight = TRUE;
-            pattern->height = fallback->height;
+        if (!grad->hasfy && fallback->hasfy) {
+            grad->hasfy = TRUE;
+            grad->fy = fallback->fy;
         }
-        if (!pattern->hastransform && fallback->hastransform) {
-            pattern->hastransform = TRUE;
-            for (i = 0; i < 6; i++)
-                pattern->affine[i] = fallback->affine[i];
+        if (!grad->hasr && fallback->hasr) {
+            grad->hasr = TRUE;
+            grad->r = fallback->r;
         }
-        if (!pattern->hasvbox && fallback->hasvbox) {
-            pattern->vbox = fallback->vbox;
+        if (!grad->hastransform && fallback->hastransform) {
+            grad->hastransform = TRUE;
+            grad->affine = fallback->affine;
         }
-        if (!pattern->hasaspect && fallback->hasaspect) {
-            pattern->hasaspect = TRUE;
-            pattern->preserve_aspect_ratio = fallback->preserve_aspect_ratio;
+        if (!grad->hasspread && fallback->hasspread) {
+            grad->hasspread = TRUE;
+            grad->spread = fallback->spread;
         }
-        if (!pattern->hasbbox && fallback->hasbbox) {
-            pattern->hasbbox = TRUE;
-            pattern->obj_bbox = fallback->obj_bbox;
+        if (!grad->hasbbox && fallback->hasbbox) {
+            grad->hasbbox = TRUE;
+            grad->obj_bbox = fallback->obj_bbox;
         }
-        if (!pattern->hascbox && fallback->hascbox) {
-            pattern->hascbox = TRUE;
-            pattern->obj_cbbox = fallback->obj_cbbox;
+        if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
+            grad->super.children = fallback->super.children;
         }
-        if (!pattern->super.children->len && fallback->super.children->len) {
-            pattern->super.children = fallback->super.children;
+    } else if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
+        RsvgLinearGradient *fallback = (RsvgLinearGradient *) fallback_node;
+
+        if (!grad->hastransform && fallback->hastransform) {
+            grad->hastransform = TRUE;
+            grad->affine = fallback->affine;
+        }
+        if (!grad->hasspread && fallback->hasspread) {
+            grad->hasspread = TRUE;
+            grad->spread = fallback->spread;
+        }
+        if (!grad->hasbbox && fallback->hasbbox) {
+            grad->hasbbox = TRUE;
+            grad->obj_bbox = fallback->obj_bbox;
+        }
+        if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
+            grad->super.children = fallback->super.children;
         }
     }
 }
+
+void
+rsvg_radial_gradient_fix_fallback (RsvgDrawingCtx *ctx, RsvgRadialGradient * grad)
+{
+    resolve_fallbacks (ctx,
+                       (RsvgNode *) grad,
+                       (RsvgNode *) grad,
+                       gradient_get_fallback,
+                       radial_gradient_apply_fallback);
+}
+
+static const char *
+pattern_get_fallback (RsvgNode *node)
+{
+    if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_PATTERN) {
+        RsvgPattern *pattern = (RsvgPattern *) node;
+
+        return pattern->fallback;
+    } else
+        return NULL;
+}
+
+static void
+pattern_apply_fallback (RsvgNode *pattern_node, RsvgNode *fallback_node)
+{
+    RsvgPattern *pattern;
+    RsvgPattern *fallback;
+
+    g_assert (RSVG_NODE_TYPE (pattern_node) == RSVG_NODE_TYPE_PATTERN);
+
+    if (RSVG_NODE_TYPE (fallback_node) != RSVG_NODE_TYPE_PATTERN)
+        return;
+
+    pattern = (RsvgPattern *) pattern_node;
+    fallback = (RsvgPattern *) fallback_node;
+
+    if (!pattern->hasx && fallback->hasx) {
+        pattern->hasx = TRUE;
+        pattern->x = fallback->x;
+    }
+    if (!pattern->hasy && fallback->hasy) {
+        pattern->hasy = TRUE;
+        pattern->y = fallback->y;
+    }
+    if (!pattern->haswidth && fallback->haswidth) {
+        pattern->haswidth = TRUE;
+        pattern->width = fallback->width;
+    }
+    if (!pattern->hasheight && fallback->hasheight) {
+        pattern->hasheight = TRUE;
+        pattern->height = fallback->height;
+    }
+    if (!pattern->hastransform && fallback->hastransform) {
+        pattern->hastransform = TRUE;
+        pattern->affine = fallback->affine;
+    }
+    if (!pattern->hasvbox && fallback->hasvbox) {
+        pattern->vbox = fallback->vbox;
+    }
+    if (!pattern->hasaspect && fallback->hasaspect) {
+        pattern->hasaspect = TRUE;
+        pattern->preserve_aspect_ratio = fallback->preserve_aspect_ratio;
+    }
+    if (!pattern->hasbbox && fallback->hasbbox) {
+        pattern->hasbbox = TRUE;
+        pattern->obj_bbox = fallback->obj_bbox;
+    }
+    if (!pattern->hascbox && fallback->hascbox) {
+        pattern->hascbox = TRUE;
+        pattern->obj_cbbox = fallback->obj_cbbox;
+    }
+    if (!pattern->super.children->len && fallback->super.children->len) {
+        pattern->super.children = fallback->super.children;
+    }
+}
+
+void
+rsvg_pattern_fix_fallback (RsvgDrawingCtx *ctx, RsvgPattern * pattern)
+{
+    resolve_fallbacks (ctx,
+                       (RsvgNode *) pattern,
+                       (RsvgNode *) pattern,
+                       pattern_get_fallback,
+                       pattern_apply_fallback);
+}
diff --git a/rsvg-paint-server.h b/rsvg-paint-server.h
index b6e37aa..8f11af5 100644
--- a/rsvg-paint-server.h
+++ b/rsvg-paint-server.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-paint-server.h : RSVG colors
 
@@ -27,6 +28,7 @@
 #define RSVG_PAINT_SERVER_H
 
 #include <glib.h>
+#include <cairo.h>
 #include "rsvg-defs.h"
 
 G_BEGIN_DECLS 
@@ -36,18 +38,12 @@ typedef struct _RsvgGradientStops RsvgGradientStops;
 typedef struct _RsvgLinearGradient RsvgLinearGradient;
 typedef struct _RsvgRadialGradient RsvgRadialGradient;
 typedef struct _RsvgPattern RsvgPattern;
-typedef struct _RsvgSolidColour RsvgSolidColour;
+typedef struct _RsvgSolidColor RsvgSolidColor;
 
 typedef struct _RsvgPaintServer RsvgPaintServer;
 
 typedef struct _RsvgPSCtx RsvgPSCtx;
 
-typedef enum {
-    RSVG_GRADIENT_PAD,
-    RSVG_GRADIENT_REFLECT,
-    RSVG_GRADIENT_REPEAT
-} RsvgGradientSpread;
-
 struct _RsvgGradientStop {
     RsvgNode super;
     double offset;
@@ -57,8 +53,8 @@ struct _RsvgGradientStop {
 struct _RsvgLinearGradient {
     RsvgNode super;
     gboolean obj_bbox;
-    double affine[6];           /* user space to actual at time of gradient def */
-    RsvgGradientSpread spread;
+    cairo_matrix_t affine; /* user space to actual at time of gradient def */
+    cairo_extend_t spread;
     RsvgLength x1, y1, x2, y2;
     guint32 current_color;
     gboolean has_current_color;
@@ -69,14 +65,14 @@ struct _RsvgLinearGradient {
     int hasbbox:1;
     int hasspread:1;
     int hastransform:1;
-    RsvgNode *fallback;
+    char *fallback;
 };
 
 struct _RsvgRadialGradient {
     RsvgNode super;
     gboolean obj_bbox;
-    double affine[6];           /* user space to actual at time of gradient def */
-    RsvgGradientSpread spread;
+    cairo_matrix_t affine; /* user space to actual at time of gradient def */
+    cairo_extend_t spread;
     RsvgLength cx, cy, r, fx, fy;
     guint32 current_color;
     gboolean has_current_color;
@@ -88,14 +84,14 @@ struct _RsvgRadialGradient {
     int hasspread:1;
     int hasbbox:1;
     int hastransform:1;
-    RsvgNode *fallback;
+    char *fallback;
 };
 
 struct _RsvgPattern {
     RsvgNode super;
     gboolean obj_cbbox;
     gboolean obj_bbox;
-    double affine[6];           /* user space to actual at time of gradient def */
+    cairo_matrix_t affine; /* user space to actual at time of gradient def */
     RsvgLength x, y, width, height;
     RsvgViewBox vbox;
     unsigned int preserve_aspect_ratio;
@@ -108,30 +104,26 @@ struct _RsvgPattern {
     int hascbox:1;
     int hasbbox:1;
     int hastransform:1;
-    RsvgPattern *fallback;
+    char *fallback;
 };
 
-struct _RsvgSolidColour {
-    gboolean currentcolour;
-    guint32 rgb;
+struct _RsvgSolidColor {
+    gboolean currentcolor;
+    guint32 argb;
 };
 
-typedef struct _RsvgSolidColour RsvgPaintServerColour;
+typedef struct _RsvgSolidColor RsvgPaintServerColor;
 typedef enum _RsvgPaintServerType RsvgPaintServerType;
 typedef union _RsvgPaintServerCore RsvgPaintServerCore;
 
 union _RsvgPaintServerCore {
-    RsvgLinearGradient *lingrad;
-    RsvgRadialGradient *radgrad;
-    RsvgSolidColour *colour;
-    RsvgPattern *pattern;
+    RsvgSolidColor *color;
+    char *iri;
 };
 
 enum _RsvgPaintServerType {
-    RSVG_PAINT_SERVER_RAD_GRAD,
-    RSVG_PAINT_SERVER_LIN_GRAD,
     RSVG_PAINT_SERVER_SOLID,
-    RSVG_PAINT_SERVER_PATTERN
+    RSVG_PAINT_SERVER_IRI
 };
 
 struct _RsvgPaintServer {
@@ -141,21 +133,30 @@ struct _RsvgPaintServer {
 };
 
 /* Create a new paint server based on a specification string. */
-RsvgPaintServer	    *rsvg_paint_server_parse	(gboolean * inherit, const RsvgDefs * defs,
-						 const char *str, guint32 current_color);
-void		     rsvg_paint_server_ref	(RsvgPaintServer * ps);
-void		     rsvg_paint_server_unref	(RsvgPaintServer * ps);
-RsvgRadialGradient  *rsvg_clone_radial_gradient (const RsvgRadialGradient * grad,
-						 gboolean * shallow_cloned);
-RsvgLinearGradient  *rsvg_clone_linear_gradient (const RsvgLinearGradient * grad,
-						 gboolean * shallow_cloned);
+G_GNUC_INTERNAL
+RsvgPaintServer	    *rsvg_paint_server_parse    (gboolean * inherit, const char *str);
+G_GNUC_INTERNAL
+void                 rsvg_paint_server_ref      (RsvgPaintServer * ps);
+G_GNUC_INTERNAL
+void                 rsvg_paint_server_unref    (RsvgPaintServer * ps);
+
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_linear_gradient  (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_radial_gradient  (void);
-RsvgNode *rsvg_new_stop		    (void);
-RsvgNode *rsvg_new_pattern	    (void);
-void rsvg_pattern_fix_fallback		(RsvgPattern * pattern);
-void rsvg_linear_gradient_fix_fallback	(RsvgLinearGradient * grad);
-void rsvg_radial_gradient_fix_fallback	(RsvgRadialGradient * grad);
+G_GNUC_INTERNAL
+RsvgNode *rsvg_new_stop	        (void);
+G_GNUC_INTERNAL
+RsvgNode *rsvg_new_pattern      (void);
+G_GNUC_INTERNAL
+void rsvg_pattern_fix_fallback          (RsvgDrawingCtx * ctx,
+                                         RsvgPattern * pattern);
+G_GNUC_INTERNAL
+void rsvg_linear_gradient_fix_fallback	(RsvgDrawingCtx * ctx,
+                                         RsvgLinearGradient * grad);
+G_GNUC_INTERNAL
+void rsvg_radial_gradient_fix_fallback	(RsvgDrawingCtx * ctx,
+                                         RsvgRadialGradient * grad);
 
 G_END_DECLS
 
diff --git a/rsvg-path.c b/rsvg-path.c
index 3ba5cb1..ee32ee6 100644
--- a/rsvg-path.c
+++ b/rsvg-path.c
@@ -1,7 +1,10 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg-path.c: Parse SVG path element data into bezier path.
  
    Copyright (C) 2000 Eazel, Inc.
+   Copyright © 2011 Christian Persch
   
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public License as
@@ -34,7 +37,7 @@
 
 #include "rsvg-private.h"
 
-/* This module parses an SVG path element into an RsvgBpathDef.
+/* This module parses an SVG path element into an cairo_path_t.
 
    At present, there is no support for <marker> or any other contextual
    information from the SVG file. The API will need to change rather
@@ -46,20 +49,138 @@
 typedef struct _RSVGParsePathCtx RSVGParsePathCtx;
 
 struct _RSVGParsePathCtx {
-    RsvgBpathDef *bpath;
-    double cpx, cpy;            /* current point */
-    double rpx, rpy;            /* reflection point (for 's' and 't' commands) */
+    RsvgPathBuilder builder;
+
+    cairo_path_data_t cp;       /* current point */
+    cairo_path_data_t rp;       /* reflection point (for 's' and 't' commands) */
     char cmd;                   /* current command (lowercase) */
     int param;                  /* parameter number */
     gboolean rel;               /* true if relative coords */
     double params[7];           /* parameters that have been parsed */
 };
 
+static inline void
+rsvg_path_builder_ensure_capacity (RsvgPathBuilder *builder,
+                                   int additional_capacity)
+{
+}
+
+static inline void
+rsvg_path_builder_add_element (RsvgPathBuilder *builder,
+                               cairo_path_data_t *data)
+{
+  g_array_append_val (builder->path_data, *data);
+}
+
+void
+rsvg_path_builder_init (RsvgPathBuilder *builder,
+                        int n_elements)
+{
+  builder->path_data = g_array_sized_new (FALSE, FALSE, sizeof (cairo_path_data_t), n_elements);
+  builder->last_move_to_index = -1;
+}
+
+void
+rsvg_path_builder_move_to (RsvgPathBuilder *builder,
+                           double x,
+                           double y)
+{
+  cairo_path_data_t data;
+
+  rsvg_path_builder_ensure_capacity (builder, 2);
+
+  data.header.type = CAIRO_PATH_MOVE_TO;
+  data.header.length = 2;
+  rsvg_path_builder_add_element (builder, &data);
+  builder->last_move_to_index = builder->path_data->len - 1;
+
+  data.point.x = x;
+  data.point.y = y;
+  rsvg_path_builder_add_element (builder, &data);
+}
+
+void
+rsvg_path_builder_line_to (RsvgPathBuilder *builder,
+                           double x,
+                           double y)
+{
+  cairo_path_data_t data;
+
+  rsvg_path_builder_ensure_capacity (builder, 2);
+
+  data.header.type = CAIRO_PATH_LINE_TO;
+  data.header.length = 2;
+  rsvg_path_builder_add_element (builder, &data);
+  data.point.x = x;
+  data.point.y = y;
+  rsvg_path_builder_add_element (builder, &data);
+}
+
+void
+rsvg_path_builder_curve_to (RsvgPathBuilder *builder,
+                            double x1,
+                            double y1,
+                            double x2,
+                            double y2,
+                            double x3,
+                            double y3)
+{
+  cairo_path_data_t data;
+
+  rsvg_path_builder_ensure_capacity (builder, 4);
+
+  data.header.type = CAIRO_PATH_CURVE_TO;
+  data.header.length = 4;
+  rsvg_path_builder_add_element (builder, &data);
+  data.point.x = x1;
+  data.point.y = y1;
+  rsvg_path_builder_add_element (builder, &data);
+  data.point.x = x2;
+  data.point.y = y2;
+  rsvg_path_builder_add_element (builder, &data);
+  data.point.x = x3;
+  data.point.y = y3;
+  rsvg_path_builder_add_element (builder, &data);
+}
+
+void
+rsvg_path_builder_close_path (RsvgPathBuilder *builder)
+{
+  cairo_path_data_t data;
+
+  rsvg_path_builder_ensure_capacity (builder, 1);
+
+  data.header.type = CAIRO_PATH_CLOSE_PATH;
+  data.header.length = 1;
+  rsvg_path_builder_add_element (builder, &data);
+
+  /* Add a 'move-to' element */
+  if (builder->last_move_to_index >= 0) {
+    cairo_path_data_t *moveto = &g_array_index (builder->path_data, cairo_path_data_t, builder->last_move_to_index);
+
+    rsvg_path_builder_move_to (builder, moveto[1].point.x, moveto[1].point.y);
+  }
+}
+
+cairo_path_t *
+rsvg_path_builder_finish (RsvgPathBuilder *builder)
+{
+    cairo_path_t *path;
+
+    path = g_new (cairo_path_t, 1);
+    path->status = CAIRO_STATUS_SUCCESS;
+    path->data = (cairo_path_data_t *) builder->path_data->data; /* adopt array segment */
+    path->num_data = builder->path_data->len;
+    g_array_free (builder->path_data, FALSE);
+
+    return path;
+}
+
 static void
-rsvg_path_arc_segment (RSVGParsePathCtx * ctx,
+rsvg_path_arc_segment (RsvgPathBuilder *builder,
                        double xc, double yc,
                        double th0, double th1, double rx, double ry,
-		       double x_axis_rotation)
+                       double x_axis_rotation)
 {
     double x1, y1, x2, y2, x3, y3;
     double t;
@@ -79,145 +200,140 @@ rsvg_path_arc_segment (RSVGParsePathCtx * ctx,
     x2 = x3 + rx*(t * sin (th1));
     y2 = y3 + ry*(-t * cos (th1));
 
-    rsvg_bpath_def_curveto (ctx->bpath,
-                            xc + cosf*x1 - sinf*y1,
-			    yc + sinf*x1 + cosf*y1,
-                            xc + cosf*x2 - sinf*y2,
-			    yc + sinf*x2 + cosf*y2,
-                            xc + cosf*x3 - sinf*y3,
-			    yc + sinf*x3 + cosf*y3);
+    rsvg_path_builder_curve_to (builder,
+                                xc + cosf*x1 - sinf*y1,
+                                yc + sinf*x1 + cosf*y1,
+                                xc + cosf*x2 - sinf*y2,
+                                yc + sinf*x2 + cosf*y2,
+                                xc + cosf*x3 - sinf*y3,
+                                yc + sinf*x3 + cosf*y3);
 }
 
 /**
- * rsvg_path_arc: Add an RSVG arc to the path context.
- * @ctx: Path context.
+ * rsvg_path_builder_arc:
+ * @builder: Path builder.
+ * @x1: Starting x coordinate
+ * @y1: Starting y coordinate
  * @rx: Radius in x direction (before rotation).
  * @ry: Radius in y direction (before rotation).
  * @x_axis_rotation: Rotation angle for axes.
  * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
- * @sweep: 0 for "negative angle", 1 for "positive angle".
- * @x: New x coordinate.
- * @y: New y coordinate.
+ * @sweep_flag: 0 for "negative angle", 1 for "positive angle".
+ * @x2: Ending x coordinate
+ * @y2: Ending y coordinate
  *
+ * Add an RSVG arc to the path context.
  **/
-static void
-rsvg_path_arc (RSVGParsePathCtx * ctx,
-               double rx, double ry, double x_axis_rotation,
-               int large_arc_flag, int sweep_flag, double x, double y)
+void
+rsvg_path_builder_arc (RsvgPathBuilder *builder,
+                       double x1, double y1,
+                       double rx, double ry,
+                       double x_axis_rotation,
+                       gboolean large_arc_flag, gboolean sweep_flag,
+                       double x2, double y2)
 {
 
-  /* See Appendix F.6 Elliptical arc implementation notes
-     http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes */
-
-  double f, sinf, cosf;
-  double x1, y1, x2, y2;
-  double x1_, y1_;
-  double cx_, cy_, cx, cy;
-  double gamma;
-  double theta1, delta_theta;
-  double k1, k2, k3, k4, k5;
+    /* See Appendix F.6 Elliptical arc implementation notes
+       http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes */
 
-  int i, n_segs;
-
-    /* Start and end of path segment */
-    x1 = ctx->cpx;
-    y1 = ctx->cpy;
+    double f, sinf, cosf;
+    double x1_, y1_;
+    double cx_, cy_, cx, cy;
+    double gamma;
+    double theta1, delta_theta;
+    double k1, k2, k3, k4, k5;
 
-    x2 = x;
-    y2 = y;
+    int i, n_segs;
 
-    if(x1 == x2 && y1 == y2)
-      return;
+    if (x1 == x2 && y1 == y2)
+        return;
 
     /* X-axis */
     f = x_axis_rotation * M_PI / 180.0;
-    sinf = sin(f);
-    cosf = cos(f);
+    sinf = sin (f);
+    cosf = cos (f);
+
+    rx = fabs (rx);
+    ry = fabs (ry);
 
     /* Check the radius against floading point underflow.
        See http://bugs.debian.org/508443 */
-    if ((fabs(rx) < DBL_EPSILON) || (fabs(ry) < DBL_EPSILON))
-      {
-	rsvg_bpath_def_lineto (ctx->bpath, x, y);
+    if ((rx < DBL_EPSILON) || (ry < DBL_EPSILON)) {
+        rsvg_path_builder_line_to (builder, x2, y2);
         return;
-      }
-
-    if(rx < 0)rx = -rx;
-    if(ry < 0)ry = -ry;
+    }
 
-    k1 = (x1 - x2)/2;
-    k2 = (y1 - y2)/2;
+    k1 = (x1 - x2) / 2;
+    k2 = (y1 - y2) / 2;
 
     x1_ = cosf * k1 + sinf * k2;
     y1_ = -sinf * k1 + cosf * k2;
 
-    gamma = (x1_*x1_)/(rx*rx) + (y1_*y1_)/(ry*ry);
-    if(gamma > 1)
-      {
-	rx *= sqrt(gamma);
-	ry *= sqrt(gamma);
-      }
+    gamma = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
+    if (gamma > 1) {
+        rx *= sqrt (gamma);
+        ry *= sqrt (gamma);
+    }
 
     /* Compute the center */
 
-    k1 = rx*rx*y1_*y1_ + ry*ry*x1_*x1_;
-    if(k1 == 0)    
-      return;
+    k1 = rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_;
+    if (k1 == 0)
+        return;
 
-    k1 = sqrt(fabs((rx*rx*ry*ry)/k1 - 1));
-    if(sweep_flag == large_arc_flag)
-      k1 = -k1;
+    k1 = sqrt (fabs ((rx * rx * ry * ry) / k1 - 1));
+    if (sweep_flag == large_arc_flag)
+        k1 = -k1;
 
-    cx_ = k1*rx*y1_/ry;
-    cy_ = -k1*ry*x1_/rx;
+    cx_ = k1 * rx * y1_ / ry;
+    cy_ = -k1 * ry * x1_ / rx;
     
-    cx = cosf*cx_ - sinf*cy_ + (x1+x2)/2;
-    cy = sinf*cx_ + cosf*cy_ + (y1+y2)/2;
+    cx = cosf * cx_ - sinf * cy_ + (x1 + x2) / 2;
+    cy = sinf * cx_ + cosf * cy_ + (y1 + y2) / 2;
 
     /* Compute start angle */
 
-    k1 = (x1_ - cx_)/rx;
-    k2 = (y1_ - cy_)/ry;
-    k3 = (-x1_ - cx_)/rx;
-    k4 = (-y1_ - cy_)/ry;
+    k1 = (x1_ - cx_) / rx;
+    k2 = (y1_ - cy_) / ry;
+    k3 = (-x1_ - cx_) / rx;
+    k4 = (-y1_ - cy_) / ry;
 
-    k5 = sqrt(fabs(k1*k1 + k2*k2));
-    if(k5 == 0)return;
+    k5 = sqrt (fabs (k1 * k1 + k2 * k2));
+    if (k5 == 0)
+        return;
 
-    k5 = k1/k5;
-    if(k5 < -1)k5 = -1;
-    else if(k5 > 1)k5 = 1;
-    theta1 = acos(k5);
-    if(k2 < 0)theta1 = -theta1;
+    k5 = k1 / k5;
+    k5 = CLAMP (k5, -1, 1);
+    theta1 = acos (k5);
+    if (k2 < 0)
+        theta1 = -theta1;
 
     /* Compute delta_theta */
 
-    k5 = sqrt(fabs((k1*k1 + k2*k2)*(k3*k3 + k4*k4)));
-    if(k5 == 0)return;
+    k5 = sqrt (fabs ((k1 * k1 + k2 * k2) * (k3 * k3 + k4 * k4)));
+    if (k5 == 0)
+        return;
 
-    k5 = (k1*k3 + k2*k4)/k5;
-    if(k5 < -1)k5 = -1;
-    else if(k5 > 1)k5 = 1;
-    delta_theta = acos(k5);
-    if(k1*k4 - k3*k2 < 0)delta_theta = -delta_theta;
+    k5 = (k1 * k3 + k2 * k4) / k5;
+    k5 = CLAMP (k5, -1, 1);
+    delta_theta = acos (k5);
+    if (k1 * k4 - k3 * k2 < 0)
+        delta_theta = -delta_theta;
 
-    if(sweep_flag && delta_theta < 0)
-      delta_theta += M_PI*2;
-    else if(!sweep_flag && delta_theta > 0)
-      delta_theta -= M_PI*2;
+    if (sweep_flag && delta_theta < 0)
+        delta_theta += M_PI * 2;
+    else if (!sweep_flag && delta_theta > 0)
+        delta_theta -= M_PI * 2;
    
     /* Now draw the arc */
 
     n_segs = ceil (fabs (delta_theta / (M_PI * 0.5 + 0.001)));
 
     for (i = 0; i < n_segs; i++)
-      rsvg_path_arc_segment (ctx, cx, cy,
-			     theta1 + i * delta_theta / n_segs,
-			     theta1 + (i + 1) * delta_theta / n_segs,
-			     rx, ry, x_axis_rotation);
-
-    ctx->cpx = x;
-    ctx->cpy = y;
+        rsvg_path_arc_segment (builder, cx, cy,
+                               theta1 + i * delta_theta / n_segs,
+                               theta1 + (i + 1) * delta_theta / n_segs,
+                               rx, ry, x_axis_rotation);
 }
 
 
@@ -230,14 +346,14 @@ rsvg_parse_path_default_xy (RSVGParsePathCtx * ctx, int n_params)
 
     if (ctx->rel) {
         for (i = ctx->param; i < n_params; i++) {
-            if (i > 2)
-                ctx->params[i] = ctx->params[i - 2];
+            /* we shouldn't get 0 here (usually ctx->param > 0 as
+               precondition) */
+            if (i == 0)
+                ctx->params[i] = ctx->cp.point.x;
             else if (i == 1)
-                ctx->params[i] = ctx->cpy;
-            else if (i == 0)
-                /* we shouldn't get here (usually ctx->param > 0 as
-                   precondition) */
-                ctx->params[i] = ctx->cpx;
+                ctx->params[i] = ctx->cp.point.y;
+            else
+                ctx->params[i] = ctx->params[i - 2];
         }
     } else {
         for (i = ctx->param; i < n_params; i++)
@@ -255,20 +371,20 @@ rsvg_parse_path_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
         /* moveto */
         if (ctx->param == 2 || final) {
             rsvg_parse_path_default_xy (ctx, 2);
-            rsvg_bpath_def_moveto (ctx->bpath, ctx->params[0], ctx->params[1]);
-            ctx->cpx = ctx->rpx = ctx->params[0];
-            ctx->cpy = ctx->rpy = ctx->params[1];
+            rsvg_path_builder_move_to (&ctx->builder, ctx->params[0], ctx->params[1]);
+            ctx->cp.point.x = ctx->rp.point.x = ctx->params[0];
+            ctx->cp.point.y = ctx->rp.point.y = ctx->params[1];
             ctx->param = 0;
-	    ctx->cmd = 'l'; /* implicit linetos after a moveto */
+            ctx->cmd = 'l'; /* implicit linetos after a moveto */
         }
         break;
     case 'l':
         /* lineto */
         if (ctx->param == 2 || final) {
             rsvg_parse_path_default_xy (ctx, 2);
-            rsvg_bpath_def_lineto (ctx->bpath, ctx->params[0], ctx->params[1]);
-            ctx->cpx = ctx->rpx = ctx->params[0];
-            ctx->cpy = ctx->rpy = ctx->params[1];
+            rsvg_path_builder_line_to (&ctx->builder, ctx->params[0], ctx->params[1]);
+            ctx->cp.point.x = ctx->rp.point.x = ctx->params[0];
+            ctx->cp.point.y = ctx->rp.point.y = ctx->params[1];
             ctx->param = 0;
         }
         break;
@@ -282,11 +398,11 @@ rsvg_parse_path_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
             y2 = ctx->params[3];
             x3 = ctx->params[4];
             y3 = ctx->params[5];
-            rsvg_bpath_def_curveto (ctx->bpath, x1, y1, x2, y2, x3, y3);
-            ctx->rpx = x2;
-            ctx->rpy = y2;
-            ctx->cpx = x3;
-            ctx->cpy = y3;
+            rsvg_path_builder_curve_to (&ctx->builder, x1, y1, x2, y2, x3, y3);
+            ctx->rp.point.x = x2;
+            ctx->rp.point.y = y2;
+            ctx->cp.point.x = x3;
+            ctx->cp.point.y = y3;
             ctx->param = 0;
         }
         break;
@@ -294,33 +410,35 @@ rsvg_parse_path_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
         /* smooth curveto */
         if (ctx->param == 4 || final) {
             rsvg_parse_path_default_xy (ctx, 4);
-            x1 = 2 * ctx->cpx - ctx->rpx;
-            y1 = 2 * ctx->cpy - ctx->rpy;
+            x1 = 2 * ctx->cp.point.x - ctx->rp.point.x;
+            y1 = 2 * ctx->cp.point.y - ctx->rp.point.y;
             x2 = ctx->params[0];
             y2 = ctx->params[1];
             x3 = ctx->params[2];
             y3 = ctx->params[3];
-            rsvg_bpath_def_curveto (ctx->bpath, x1, y1, x2, y2, x3, y3);
-            ctx->rpx = x2;
-            ctx->rpy = y2;
-            ctx->cpx = x3;
-            ctx->cpy = y3;
+            rsvg_path_builder_curve_to (&ctx->builder, x1, y1, x2, y2, x3, y3);
+            ctx->rp.point.x = x2;
+            ctx->rp.point.y = y2;
+            ctx->cp.point.x = x3;
+            ctx->cp.point.y = y3;
             ctx->param = 0;
         }
         break;
     case 'h':
         /* horizontal lineto */
         if (ctx->param == 1) {
-            rsvg_bpath_def_lineto (ctx->bpath, ctx->params[0], ctx->cpy);
-            ctx->cpx = ctx->rpx = ctx->params[0];
+            rsvg_path_builder_line_to (&ctx->builder, ctx->params[0], ctx->cp.point.y);
+            ctx->cp.point.x = ctx->rp.point.x = ctx->params[0];
+            ctx->rp.point.y = ctx->cp.point.y;
             ctx->param = 0;
         }
         break;
     case 'v':
         /* vertical lineto */
         if (ctx->param == 1) {
-            rsvg_bpath_def_lineto (ctx->bpath, ctx->cpx, ctx->params[0]);
-            ctx->cpy = ctx->rpy = ctx->params[0];
+            rsvg_path_builder_line_to (&ctx->builder, ctx->cp.point.x, ctx->params[0]);
+            ctx->rp.point.x = ctx->cp.point.x;
+            ctx->cp.point.y = ctx->rp.point.y = ctx->params[0];
             ctx->param = 0;
         }
         break;
@@ -333,17 +451,17 @@ rsvg_parse_path_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
         if (ctx->param == 4 || final) {
             rsvg_parse_path_default_xy (ctx, 4);
             /* raise quadratic bezier to cubic */
-            x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
-            y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+            x1 = (ctx->cp.point.x + 2 * ctx->params[0]) * (1.0 / 3.0);
+            y1 = (ctx->cp.point.y + 2 * ctx->params[1]) * (1.0 / 3.0);
             x3 = ctx->params[2];
             y3 = ctx->params[3];
             x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
             y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
-            rsvg_bpath_def_curveto (ctx->bpath, x1, y1, x2, y2, x3, y3);
-            ctx->rpx = ctx->params[0];
-            ctx->rpy = ctx->params[1];
-            ctx->cpx = x3;
-            ctx->cpy = y3;
+            rsvg_path_builder_curve_to (&ctx->builder, x1, y1, x2, y2, x3, y3);
+            ctx->rp.point.x = ctx->params[0];
+            ctx->rp.point.y = ctx->params[1];
+            ctx->cp.point.x = x3;
+            ctx->cp.point.y = y3;
             ctx->param = 0;
         }
         break;
@@ -352,50 +470,79 @@ rsvg_parse_path_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
         if (ctx->param == 2 || final) {
             double xc, yc;      /* quadratic control point */
 
-            xc = 2 * ctx->cpx - ctx->rpx;
-            yc = 2 * ctx->cpy - ctx->rpy;
+            xc = 2 * ctx->cp.point.x - ctx->rp.point.x;
+            yc = 2 * ctx->cp.point.y - ctx->rp.point.y;
             /* generate a quadratic bezier with control point = xc, yc */
-            x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
-            y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
+            x1 = (ctx->cp.point.x + 2 * xc) * (1.0 / 3.0);
+            y1 = (ctx->cp.point.y + 2 * yc) * (1.0 / 3.0);
             x3 = ctx->params[0];
             y3 = ctx->params[1];
             x2 = (x3 + 2 * xc) * (1.0 / 3.0);
             y2 = (y3 + 2 * yc) * (1.0 / 3.0);
-            rsvg_bpath_def_curveto (ctx->bpath, x1, y1, x2, y2, x3, y3);
-            ctx->rpx = xc;
-            ctx->rpy = yc;
-            ctx->cpx = x3;
-            ctx->cpy = y3;
+            rsvg_path_builder_curve_to (&ctx->builder, x1, y1, x2, y2, x3, y3);
+            ctx->rp.point.x = xc;
+            ctx->rp.point.y = yc;
+            ctx->cp.point.x = x3;
+            ctx->cp.point.y = y3;
             ctx->param = 0;
         } else if (final) {
             if (ctx->param > 2) {
                 rsvg_parse_path_default_xy (ctx, 4);
                 /* raise quadratic bezier to cubic */
-                x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
-                y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+                x1 = (ctx->cp.point.x + 2 * ctx->params[0]) * (1.0 / 3.0);
+                y1 = (ctx->cp.point.y + 2 * ctx->params[1]) * (1.0 / 3.0);
                 x3 = ctx->params[2];
                 y3 = ctx->params[3];
                 x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
                 y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
-                rsvg_bpath_def_curveto (ctx->bpath, x1, y1, x2, y2, x3, y3);
-                ctx->rpx = ctx->params[0];
-                ctx->rpy = ctx->params[1];
-                ctx->cpx = x3;
-                ctx->cpy = y3;
+                rsvg_path_builder_curve_to (&ctx->builder, x1, y1, x2, y2, x3, y3);
+                ctx->rp.point.x = ctx->params[0];
+                ctx->rp.point.y = ctx->params[1];
+                ctx->cp.point.x = x3;
+                ctx->cp.point.y = y3;
             } else {
                 rsvg_parse_path_default_xy (ctx, 2);
-                rsvg_bpath_def_lineto (ctx->bpath, ctx->params[0], ctx->params[1]);
-                ctx->cpx = ctx->rpx = ctx->params[0];
-                ctx->cpy = ctx->rpy = ctx->params[1];
+                rsvg_path_builder_line_to (&ctx->builder, ctx->params[0], ctx->params[1]);
+                ctx->cp.point.x = ctx->rp.point.x = ctx->params[0];
+                ctx->cp.point.y = ctx->rp.point.y = ctx->params[1];
             }
             ctx->param = 0;
         }
         break;
     case 'a':
         if (ctx->param == 7 || final) {
-            rsvg_path_arc (ctx,
-                           ctx->params[0], ctx->params[1], ctx->params[2],
-                           ctx->params[3], ctx->params[4], ctx->params[5], ctx->params[6]);
+            double x1, y1;
+            double rx, ry;
+            double x_axis_rotation;
+            gboolean large_arc_flag;
+            gboolean sweep_flag;
+            double x2, y2;
+
+            x1 = ctx->cp.point.x;
+            y1 = ctx->cp.point.y;
+
+            rx = ctx->params[0];
+            ry = ctx->params[1];
+
+            x_axis_rotation = ctx->params[2];
+
+            large_arc_flag = (ctx->params[3] == 0 ? FALSE : TRUE);
+            sweep_flag = (ctx->params[4] == 0 ? FALSE : TRUE);
+
+            x2 = ctx->params[5];
+            y2 = ctx->params[6];
+
+            rsvg_path_builder_arc (&ctx->builder,
+                                   x1, y1,
+                                   rx, ry,
+                                   x_axis_rotation,
+                                   large_arc_flag,
+                                   sweep_flag,
+                                   x2, y2);
+
+            ctx->rp.point.x = ctx->cp.point.x = x2;
+            ctx->rp.point.y = ctx->cp.point.y = y2;
+
             ctx->param = 0;
         }
         break;
@@ -409,148 +556,174 @@ rsvg_path_end_of_number (RSVGParsePathCtx * ctx, double val, int sign, int exp_s
 {
     val *= sign * pow (10, exp_sign * exp);
     if (ctx->rel) {
-      /* Handle relative coordinates. This switch statement attempts
-	 to determine _what_ the coords are relative to. This is
-	 underspecified in the 12 Apr working draft. */
-      switch (ctx->cmd) {
-      case 'l':
-      case 'm':
-      case 'c':
-      case 's':
-      case 'q':
-      case 't':
-#ifndef RSVGV_RELATIVE
-	/* rule: even-numbered params are x-relative, odd-numbered
-	   are y-relative */
-	if ((ctx->param & 1) == 0)
-	  val += ctx->cpx;
-	else if ((ctx->param & 1) == 1)
-	  val += ctx->cpy;
-	break;
-#else
-	/* rule: even-numbered params are x-relative, odd-numbered
-	   are y-relative */
-	if (ctx->param == 0 || (ctx->param % 2 == 0))
-	  val += ctx->cpx;
-	else
-	  val += ctx->cpy;
-	break;
-#endif
-      case 'a':
-	/* rule: sixth and seventh are x and y, rest are not
-	   relative */
-	if (ctx->param == 5)
-	  val += ctx->cpx;
-	else if (ctx->param == 6)
-	  val += ctx->cpy;
-	break;
-      case 'h':
-	/* rule: x-relative */
-	val += ctx->cpx;
-	break;
-      case 'v':
-	/* rule: y-relative */
-	val += ctx->cpy;
-	break;
-      }
+        /* Handle relative coordinates. This switch statement attempts
+           to determine _what_ the coords are relative to. This is
+           underspecified in the 12 Apr working draft. */
+        switch (ctx->cmd) {
+        case 'l':
+        case 'm':
+        case 'c':
+        case 's':
+        case 'q':
+        case 't':
+            /* rule: even-numbered params are x-relative, odd-numbered
+               are y-relative */
+            if ((ctx->param & 1) == 0)
+                val += ctx->cp.point.x;
+            else if ((ctx->param & 1) == 1)
+                val += ctx->cp.point.y;
+            break;
+        case 'a':
+            /* rule: sixth and seventh are x and y, rest are not
+               relative */
+            if (ctx->param == 5)
+                val += ctx->cp.point.x;
+            else if (ctx->param == 6)
+                val += ctx->cp.point.y;
+            break;
+        case 'h':
+            /* rule: x-relative */
+            val += ctx->cp.point.x;
+            break;
+        case 'v':
+            /* rule: y-relative */
+            val += ctx->cp.point.y;
+            break;
+        }
     }
     ctx->params[ctx->param++] = val;
     rsvg_parse_path_do_cmd (ctx, FALSE);    
 }
 
+#define RSVGN_IN_PREINTEGER  0
+#define RSVGN_IN_INTEGER     1
+#define RSVGN_IN_FRACTION    2
+#define RSVGN_IN_PREEXPONENT 3
+#define RSVGN_IN_EXPONENT    4
+
+#define RSVGN_GOT_SIGN          0x1
+#define RSVGN_GOT_EXPONENT_SIGN 0x2
+
+/* Returns the length of the number parsed, so it can be skipped
+ * in rsvg_parse_path_data. Calls rsvg_path_end_number to have the number
+ * processed in its command.
+ */
+static int
+rsvg_parse_number (RSVGParsePathCtx * ctx, const char *data)
+{
+    int length = 0;
+    int in = RSVGN_IN_PREINTEGER; /* Current location within the number */
+    int got = 0x0; /* [bitfield] Having 2 of each of these is an error */
+    gboolean end = FALSE; /* Set to true if the number should end after a char */
+    gboolean error = FALSE; /* Set to true if the number ended due to an error */
+
+    double value = 0.0;
+    double fraction = 1.0;
+    int sign = +1; /* Presume the INTEGER is positive if it has no sign */
+    int exponent = 0;
+    int exponent_sign = +1; /* Presume the EXPONENT is positive if it has no sign */
+
+    while (data[length] != '\0' && !end && !error) {
+        char c = data[length];
+        switch (in) {
+            case RSVGN_IN_PREINTEGER: /* No numbers yet, we're just starting out */
+                /* LEGAL: + - .->FRACTION DIGIT->INTEGER */
+                if (c == '+' || c == '-') {
+                    if (got & RSVGN_GOT_SIGN) {
+                        error = TRUE; /* Two signs: not allowed */
+                    } else {
+                        sign = c == '+' ? +1 : -1;
+                        got |= RSVGN_GOT_SIGN;
+                    }
+                } else if (c == '.') {
+                    in = RSVGN_IN_FRACTION;
+                } else if (c >= '0' && c <= '9') {
+                    value = c - '0';
+                    in = RSVGN_IN_INTEGER;
+                }
+                break;
+            case RSVGN_IN_INTEGER: /* Previous character(s) was/were digit(s) */
+                /* LEGAL: DIGIT .->FRACTION E->PREEXPONENT */
+                if (c >= '0' && c <= '9') {
+                    value = value * 10 + (c - '0');
+                }
+                else if (c == '.') {
+                    in = RSVGN_IN_FRACTION;
+                }
+                else if (c == 'e' || c == 'E') {
+                    in = RSVGN_IN_PREEXPONENT;
+                }
+                else {
+                    end = TRUE;
+                }
+                break;
+            case RSVGN_IN_FRACTION: /* Previously, digit(s) in the fractional part */
+                /* LEGAL: DIGIT E->PREEXPONENT */
+                if (c >= '0' && c <= '9') {
+                    fraction *= 0.1;
+                    value += fraction * (c - '0');
+                }
+                else if (c == 'e' || c == 'E') {
+                    in = RSVGN_IN_PREEXPONENT;
+                }
+                else {
+                    end = TRUE;
+                }
+                break;
+            case RSVGN_IN_PREEXPONENT: /* Right after E */
+                /* LEGAL: + - DIGIT->EXPONENT */
+                if (c == '+' || c == '-') {
+                    if (got & RSVGN_GOT_EXPONENT_SIGN) {
+                        error = TRUE; /* Two signs: not allowed */
+                    } else {
+                        exponent_sign = c == '+' ? +1 : -1;
+                        got |= RSVGN_GOT_EXPONENT_SIGN;
+                    }
+                } else if (c >= '0' && c <= '9') {
+                    exponent = c - '0';
+                    in = RSVGN_IN_EXPONENT;
+                }
+                break;
+            case RSVGN_IN_EXPONENT: /* After E and the sign, if applicable */
+                /* LEGAL: DIGIT */
+                if (c >= '0' && c <= '9') {
+                    exponent = exponent * 10 + (c - '0');
+                } else {
+                    end = TRUE;
+                }
+                break;
+        }
+        length++;
+    }
+
+    /* TODO? if (error) report_the_error_somehow(); */
+    rsvg_path_end_of_number(ctx, value, sign, exponent_sign, exponent);
+    return end /* && !error */ ? length - 1 : length;
+}
+
 static void
 rsvg_parse_path_data (RSVGParsePathCtx * ctx, const char *data)
 {
     int i = 0;
-    double val = 0;
     char c = 0;
-    gboolean in_num = FALSE;
-    gboolean in_frac = FALSE;
-    gboolean in_exp = FALSE;
-    gboolean exp_wait_sign = FALSE;
-    int sign = 0;
-    int exp = 0;
-    int exp_sign = 0;
-    double frac = 0.0;
-
-    in_num = FALSE;
-    for (i = 0;; i++) {
+
+    for (i = 0; data[i] != '\0'; i++) {
         c = data[i];
-        if (c >= '0' && c <= '9') {
+        if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.') {
             /* digit */
-            if (in_num) {
-                if (in_exp) {
-                    exp = (exp * 10) + c - '0';
-                    exp_wait_sign = FALSE;
-                } else if (in_frac)
-                    val += (frac *= 0.1) * (c - '0');
-                else
-                    val = (val * 10) + c - '0';
-            } else {
-                in_num = TRUE;
-                in_frac = FALSE;
-                in_exp = FALSE;
-                exp = 0;
-                exp_sign = 1;
-                exp_wait_sign = FALSE;
-                val = c - '0';
-                sign = 1;
-            }
-        } else if (c == '.') {
-	    if (in_frac) {
-	      rsvg_path_end_of_number(ctx, val, sign, exp_sign, exp);
-	      in_num = TRUE;
-	      in_frac = FALSE;
-	      in_exp = FALSE;
-	      exp = 0;
-	      exp_sign = 1;
-	      exp_wait_sign = FALSE;
-	      val = 0;
-	      sign = 1;
-	    }
-	    else if (!in_num) {
-                in_num = TRUE;
-                val = 0;
-            }
-            in_frac = TRUE;
-            frac = 1;
-        } else if ((c == 'E' || c == 'e') && in_num) {
-            in_exp = TRUE;
-            exp_wait_sign = TRUE;
-            exp = 0;
-            exp_sign = 1;
-        } else if ((c == '+' || c == '-') && in_exp) {
-            exp_sign = c == '+' ? 1 : -1;
-        } else if (in_num) {
-            /* end of number */
-	    rsvg_path_end_of_number(ctx, val, sign, exp_sign, exp);
-	    in_num = FALSE;
-        }
-
-        if (c == '\0')
-            break;
-        else if ((c == '+' || c == '-') && !exp_wait_sign) {
-            sign = c == '+' ? 1 : -1;
-            val = 0;
-            in_num = TRUE;
-            in_frac = FALSE;
-            in_exp = FALSE;
-            exp = 0;
-            exp_sign = 1;
-            exp_wait_sign = FALSE;
+            i += rsvg_parse_number(ctx, data + i) - 1;
         } else if (c == 'z' || c == 'Z') {
             if (ctx->param)
                 rsvg_parse_path_do_cmd (ctx, TRUE);
-            rsvg_bpath_def_closepath (ctx->bpath);
+            rsvg_path_builder_close_path (&ctx->builder);
 
-            ctx->cpx = ctx->rpx = ctx->bpath->bpath[ctx->bpath->n_bpath - 1].x3;
-            ctx->cpy = ctx->rpy = ctx->bpath->bpath[ctx->bpath->n_bpath - 1].y3;
-        } else if (c >= 'A' && c <= 'Z' && c != 'E') {
+            ctx->cp = ctx->rp = g_array_index (ctx->builder.path_data, cairo_path_data_t, ctx->builder.path_data->len - 1);
+        } else if (c >= 'A' && c < 'Z' && c != 'E') {
             if (ctx->param)
                 rsvg_parse_path_do_cmd (ctx, TRUE);
             ctx->cmd = c + 'a' - 'A';
             ctx->rel = FALSE;
-        } else if (c >= 'a' && c <= 'z' && c != 'e') {
+        } else if (c >= 'a' && c < 'z' && c != 'e') {
             if (ctx->param)
                 rsvg_parse_path_do_cmd (ctx, TRUE);
             ctx->cmd = c;
@@ -560,14 +733,17 @@ rsvg_parse_path_data (RSVGParsePathCtx * ctx, const char *data)
     }
 }
 
-RsvgBpathDef *
+cairo_path_t *
 rsvg_parse_path (const char *path_str)
 {
     RSVGParsePathCtx ctx;
 
-    ctx.bpath = rsvg_bpath_def_new ();
-    ctx.cpx = 0.0;
-    ctx.cpy = 0.0;
+    rsvg_path_builder_init (&ctx.builder, 32);
+
+    ctx.cp.point.x = 0.0;
+    ctx.cp.point.y = 0.0;
+    ctx.rp.point.x = 0.0;
+    ctx.rp.point.y = 0.0;
     ctx.cmd = 0;
     ctx.param = 0;
 
@@ -576,5 +752,15 @@ rsvg_parse_path (const char *path_str)
     if (ctx.param)
         rsvg_parse_path_do_cmd (&ctx, TRUE);
 
-    return ctx.bpath;
+    return rsvg_path_builder_finish (&ctx.builder);
+}
+
+void
+rsvg_cairo_path_destroy (cairo_path_t *path)
+{
+    if (path == NULL)
+        return;
+
+    g_free (path->data);
+    g_free (path);
 }
diff --git a/rsvg-path.h b/rsvg-path.h
index 7560107..4d151f0 100644
--- a/rsvg-path.h
+++ b/rsvg-path.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-path.h: Draw SVG paths
 
@@ -26,12 +27,53 @@
 #ifndef RSVG_PATH_H
 #define RSVG_PATH_H
 
-#include "rsvg-bpath-util.h"
+#include <glib.h>
+#include <cairo.h>
 
 G_BEGIN_DECLS 
 
-RsvgBpathDef *rsvg_parse_path (const char *path_str);
+typedef struct {
+    GArray *path_data;
+    int     last_move_to_index;
+} RsvgPathBuilder;
+
+G_GNUC_INTERNAL
+void rsvg_path_builder_init (RsvgPathBuilder *builder,
+                             int n_elements);
+G_GNUC_INTERNAL
+void rsvg_path_builder_move_to (RsvgPathBuilder *builder,
+                                double x,
+                                double y);
+G_GNUC_INTERNAL
+void rsvg_path_builder_line_to (RsvgPathBuilder *builder,
+                                double x,
+                                double y);
+G_GNUC_INTERNAL
+void rsvg_path_builder_curve_to (RsvgPathBuilder *builder,
+                                 double x1,
+                                 double y1,
+                                 double x2,
+                                 double y2,
+                                 double x3,
+                                 double y3);
+
+G_GNUC_INTERNAL
+void rsvg_path_builder_arc (RsvgPathBuilder *builder,
+                            double x1, double y1,
+                            double rx, double ry,
+                            double x_axis_rotation,
+                            gboolean large_arc_flag, gboolean sweep_flag,
+                            double x2, double y2);
+
+G_GNUC_INTERNAL
+void rsvg_path_builder_close_path (RsvgPathBuilder *builder);
+G_GNUC_INTERNAL
+cairo_path_t *rsvg_path_builder_finish (RsvgPathBuilder *builder);
+G_GNUC_INTERNAL
+cairo_path_t *rsvg_parse_path (const char *path_str);
+G_GNUC_INTERNAL
+void rsvg_cairo_path_destroy (cairo_path_t *path);
 
 G_END_DECLS
 
-#endif                          /* RSVG_PATH_H */
+#endif /* RSVG_PATH_H */
diff --git a/rsvg-private.h b/rsvg-private.h
index 93fd046..35b4607 100644
--- a/rsvg-private.h
+++ b/rsvg-private.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-private.h: Internals of RSVG
 
@@ -26,8 +27,9 @@
 #ifndef RSVG_PRIVATE_H
 #define RSVG_PRIVATE_H
 
+#include <cairo.h>
+
 #include "rsvg.h"
-#include "rsvg-bpath-util.h"
 
 #include <libxml/SAX.h>
 #include <libxml/xmlmemory.h>
@@ -40,18 +42,21 @@
 # include <float.h>
 #endif
 
+#if !(GLIB_CHECK_VERSION (2, 24, 0))
+#include <gsf/gsf-output.h>
+#endif
+
 G_BEGIN_DECLS 
 
 typedef struct RsvgSaxHandler RsvgSaxHandler;
 typedef struct RsvgDrawingCtx RsvgDrawingCtx;
 typedef struct RsvgRender RsvgRender;
-typedef struct _RsvgPropertyBag RsvgPropertyBag;
+typedef GHashTable RsvgPropertyBag;
 typedef struct _RsvgState RsvgState;
 typedef struct _RsvgDefs RsvgDefs;
 typedef struct _RsvgNode RsvgNode;
 typedef struct _RsvgFilter RsvgFilter;
 typedef struct _RsvgNodeChars RsvgNodeChars;
-typedef struct _RsvgIRect RsvgIRect;
 
 /* prepare for gettext */
 #ifndef _
@@ -114,8 +119,6 @@ typedef struct _RsvgIRect RsvgIRect;
  */
 #define RSVG_ONE_MINUS_EPSILON (1.0 - DBL_EPSILON)
 
-GType _rsvg_register_types (GTypeModule * module);
-
 struct RsvgSaxHandler {
     void (*free) (RsvgSaxHandler * self);
     void (*start_element) (RsvgSaxHandler * self, const char *name, RsvgPropertyBag * atts);
@@ -123,7 +126,17 @@ struct RsvgSaxHandler {
     void (*characters) (RsvgSaxHandler * self, const char *ch, int len);
 };
 
+typedef enum {
+    RSVG_LOAD_POLICY_STRICT
+} RsvgLoadPolicy;
+
+#define RSVG_LOAD_POLICY_DEFAULT (RSVG_LOAD_POLICY_STRICT)
+
 struct RsvgHandlePrivate {
+    RsvgHandleFlags flags;
+
+    RsvgLoadPolicy load_policy;
+
     gboolean is_disposed;
     gboolean is_closed;
 
@@ -152,6 +165,7 @@ struct RsvgHandlePrivate {
 
     xmlParserCtxtPtr ctxt;
     GError **error;
+    GCancellable *cancellable;
 
     double dpi_x;
     double dpi_y;
@@ -161,108 +175,164 @@ struct RsvgHandlePrivate {
     GString *metadata;
 
     gchar *base_uri;
+    GFile *base_gfile;
 
     gboolean finished;
 
-    gboolean first_write;
-    gboolean is_gzipped;
-    void *gzipped_data;         /* really a GsfOutput */
     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 */
+#else
+    GsfOutput *gzipped_data;
+#endif
 };
 
 typedef struct {
+    cairo_rectangle_t rect;
     gboolean active;
-    double x, y, w, h;
 } RsvgViewBox;
 
 /*Contextual information for the drawing phase*/
 
 struct RsvgDrawingCtx {
     RsvgRender *render;
-    GSList *state;
+    RsvgState *state;
     GError **error;
     RsvgDefs *defs;
-    gchar *base_uri;
     PangoContext *pango_context;
     double dpi_x, dpi_y;
     RsvgViewBox vb;
     GSList *vb_stack;
     GSList *drawsub_stack;
-    GSList *ptrs;
+    GSList *acquired_nodes;
 };
 
 /*Abstract base class for context for our backends (one as yet)*/
 
+typedef enum {
+  RSVG_RENDER_TYPE_INVALID,
+
+  RSVG_RENDER_TYPE_BASE,
+
+  RSVG_RENDER_TYPE_CAIRO = 8,
+  RSVG_RENDER_TYPE_CAIRO_CLIP
+} RsvgRenderType;
+
 struct RsvgRender {
+    RsvgRenderType type;
+
     void (*free) (RsvgRender * self);
 
-    PangoContext    *(*create_pango_context)	(RsvgDrawingCtx * ctx);
-    void	     (*render_pango_layout)	(RsvgDrawingCtx * ctx, PangoLayout *layout, 
-						 double x, double y);
-    void	     (*render_path)		(RsvgDrawingCtx * ctx, const RsvgBpathDef * path);
-    void	     (*render_image)		(RsvgDrawingCtx * ctx, const GdkPixbuf * pixbuf,
-						 double x, double y, double w, double h);
-    void	     (*pop_discrete_layer)	(RsvgDrawingCtx * ctx);
-    void	     (*push_discrete_layer)	(RsvgDrawingCtx * ctx);
-    void	     (*add_clipping_rect)	(RsvgDrawingCtx * ctx, double x, double y, 
-						 double w, double h);
-    GdkPixbuf	    *(*get_image_of_node)	(RsvgDrawingCtx * ctx, RsvgNode * drawable, 
-						 double w, double h);
+    PangoContext    *(*create_pango_context)    (RsvgDrawingCtx * ctx);
+    void             (*render_pango_layout)	    (RsvgDrawingCtx * ctx, PangoLayout *layout,
+                                                 double x, double y);
+    void             (*render_path)             (RsvgDrawingCtx * ctx, const cairo_path_t *path);
+    void             (*render_surface)          (RsvgDrawingCtx * ctx, cairo_surface_t *surface,
+                                                 double x, double y, double w, double h);
+    void             (*pop_discrete_layer)      (RsvgDrawingCtx * ctx);
+    void             (*push_discrete_layer)     (RsvgDrawingCtx * ctx);
+    void             (*add_clipping_rect)       (RsvgDrawingCtx * ctx, double x, double y,
+                                                 double w, double h);
+    cairo_surface_t *(*get_surface_of_node)     (RsvgDrawingCtx * ctx, RsvgNode * drawable,
+                                                 double w, double h);
 };
 
+static inline RsvgRender *
+_rsvg_render_check_type (RsvgRender *render,
+                         RsvgRenderType type)
+{
+  g_assert ((render->type & type) == type);
+  return render;
+}
+
+#define _RSVG_RENDER_CIC(render, render_type, RenderCType) \
+  ((RenderCType*) _rsvg_render_check_type ((render), (render_type)))
 
 typedef struct {
     double length;
     char factor;
 } RsvgLength;
 
-struct _RsvgIRect {
-    int x0, y0, x1, y1;
-};
-
 typedef struct {
-    gdouble x, y, w, h;
+    cairo_rectangle_t rect;
+    cairo_matrix_t affine;
     gboolean virgin;
-    double affine[6];
 } RsvgBbox;
 
 typedef enum {
-    RSVG_SIZE_ZOOM,
-    RSVG_SIZE_WH,
-    RSVG_SIZE_WH_MAX,
-    RSVG_SIZE_ZOOM_MAX
-} RsvgSizeType;
-
-typedef enum {
     objectBoundingBox, userSpaceOnUse
 } RsvgCoordUnits;
 
-struct RsvgSizeCallbackData {
-    RsvgSizeType type;
-    double x_zoom;
-    double y_zoom;
-    gint width;
-    gint height;
-
-    gboolean keep_aspect_ratio;
-};
-
-void _rsvg_size_callback (int *width, int *height, gpointer data);
-
-struct _RsvgPropertyBag {
-    GHashTable *props;
-};
+typedef enum {
+    RSVG_NODE_TYPE_INVALID = 0,
+
+    RSVG_NODE_TYPE_CHARS,
+    RSVG_NODE_TYPE_CIRCLE,
+    RSVG_NODE_TYPE_CLIP_PATH,
+    RSVG_NODE_TYPE_COMPONENT_TRANFER_FUNCTION,
+    RSVG_NODE_TYPE_DEFS,
+    RSVG_NODE_TYPE_ELLIPSE,
+    RSVG_NODE_TYPE_FILTER,
+    RSVG_NODE_TYPE_GROUP,
+    RSVG_NODE_TYPE_IMAGE,
+    RSVG_NODE_TYPE_LIGHT_SOURCE,
+    RSVG_NODE_TYPE_LINE,
+    RSVG_NODE_TYPE_LINEAR_GRADIENT,
+    RSVG_NODE_TYPE_MARKER,
+    RSVG_NODE_TYPE_MASK,
+    RSVG_NODE_TYPE_PATH,
+    RSVG_NODE_TYPE_PATTERN,
+    RSVG_NODE_TYPE_POLYGON,
+    RSVG_NODE_TYPE_POLYLINE,
+    RSVG_NODE_TYPE_RADIAL_GRADIENT,
+    RSVG_NODE_TYPE_RECT,
+    RSVG_NODE_TYPE_STOP,
+    RSVG_NODE_TYPE_SVG,
+    RSVG_NODE_TYPE_SWITCH,
+    RSVG_NODE_TYPE_SYMBOL,
+    RSVG_NODE_TYPE_TEXT,
+    RSVG_NODE_TYPE_TREF,
+    RSVG_NODE_TYPE_TSPAN,
+    RSVG_NODE_TYPE_USE,
+
+    /* Filter primitives */
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE = 64,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_BLEND,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_COLOR_MATRIX,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_COMPONENT_TRANSFER,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_COMPOSITE,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_CONVOLVE_MATRIX,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_DIFFUSE_LIGHTING,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_DISPLACEMENT_MAP,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_ERODE,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_FLOOD,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_GAUSSIAN_BLUR,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_IMAGE,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_MERGE_NODE,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_OFFSET,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_SPECULAR_LIGHTING,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_TILE,
+    RSVG_NODE_TYPE_FILTER_PRIMITIVE_TURBULENCE,
+
+} RsvgNodeType;
 
 struct _RsvgNode {
     RsvgState *state;
     RsvgNode *parent;
-    GString *type;
     GPtrArray *children;
+    RsvgNodeType type;
+    const char *name; /* owned by the xmlContext, invalid after parsing! */
     void (*free) (RsvgNode * self);
     void (*draw) (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate);
     void (*set_atts) (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag *);
 };
 
+#define RSVG_NODE_TYPE(node)                ((node)->type)
+#define RSVG_NODE_IS_FILTER_PRIMITIVE(node) (RSVG_NODE_TYPE((node)) & RSVG_NODE_TYPE_FILTER_PRIMITIVE)
+
 struct _RsvgNodeChars {
     RsvgNode super;
     GString *contents;
@@ -270,112 +340,110 @@ struct _RsvgNodeChars {
 
 typedef void (*RsvgPropertyBagEnumFunc) (const char *key, const char *value, gpointer user_data);
 
-RsvgPropertyBag	    *rsvg_property_bag_new	(const char **atts);
-void		     rsvg_property_bag_free	(RsvgPropertyBag * bag);
-G_CONST_RETURN char *rsvg_property_bag_lookup	(RsvgPropertyBag * bag, const char *key);
-guint		     rsvg_property_bag_size	(RsvgPropertyBag * bag);
-void		     rsvg_property_bag_enumerate (RsvgPropertyBag * bag, RsvgPropertyBagEnumFunc func, 
-						  gpointer user_data);
-
+G_GNUC_INTERNAL
+RsvgPropertyBag	    *rsvg_property_bag_new       (const char **atts);
+G_GNUC_INTERNAL
+RsvgPropertyBag	    *rsvg_property_bag_dup       (RsvgPropertyBag * bag);
+G_GNUC_INTERNAL
+void                 rsvg_property_bag_free      (RsvgPropertyBag * bag);
+G_GNUC_INTERNAL
+const char          *rsvg_property_bag_lookup    (RsvgPropertyBag * bag, const char *key);
+G_GNUC_INTERNAL
+guint                rsvg_property_bag_size	     (RsvgPropertyBag * bag);
+G_GNUC_INTERNAL
+void                 rsvg_property_bag_enumerate (RsvgPropertyBag * bag, RsvgPropertyBagEnumFunc func,
+                                                  gpointer user_data);
+/* for some reason this one's public... */
 GdkPixbuf *rsvg_pixbuf_from_data_with_size_data (const guchar * buff,
                                                  size_t len,
-                                                 struct RsvgSizeCallbackData *data,
+                                                 gpointer data,
                                                  const char *base_uri, GError ** error);
-
+G_GNUC_INTERNAL
 gboolean     rsvg_eval_switch_attributes	(RsvgPropertyBag * atts, gboolean * p_has_cond);
-GdkPixbuf   *_rsvg_pixbuf_new_cleared		(GdkColorspace colorspace, gboolean has_alpha,
-						 int bits_per_sample, int width, int height);
-
-gchar	    *rsvg_get_base_uri_from_filename	(const gchar * file_name);
-GByteArray  *_rsvg_acquire_xlink_href_resource	(const char *href,
-						 const char *base_uri, GError ** err);
-
-void rsvg_pop_discrete_layer	(RsvgDrawingCtx * ctx);
-void rsvg_push_discrete_layer	(RsvgDrawingCtx * ctx);
-void rsvg_render_path		(RsvgDrawingCtx * ctx, const char *d);
-void rsvg_render_image		(RsvgDrawingCtx * ctx, GdkPixbuf * pb,
-				 double x, double y, double w, double h);
-void rsvg_render_free		(RsvgRender * render);
-void rsvg_add_clipping_rect	(RsvgDrawingCtx * ctx, double x, double y, double w, double h);
-GdkPixbuf *rsvg_get_image_of_node (RsvgDrawingCtx * ctx, RsvgNode * drawable, double w, double h);
-
-
-void _rsvg_affine_invert (double dst_affine[6], const double src_affine[6]);
-
-/* flip the matrix, FALSE, FALSE is a simple copy operation, and
-   TRUE, TRUE equals a rotation by 180 degrees */
-void _rsvg_affine_flip (double dst_affine[6], const double src_affine[6], int horz, int vert);
-
-void _rsvg_affine_multiply (double dst[6], const double src1[6], const double src2[6]);
-
-/* set up the identity matrix */
-void _rsvg_affine_identity (double dst[6]);
-
-/* set up a scaling matrix */
-void _rsvg_affine_scale (double dst[6], double sx, double sy);
-
-/* set up a rotation matrix; theta is given in degrees */
-void _rsvg_affine_rotate (double dst[6], double theta);
-
-/* set up a shearing matrix; theta is given in degrees */
-void _rsvg_affine_shear (double dst[6], double theta);
-
-/* set up a translation matrix */
-void _rsvg_affine_translate (double dst[6], double tx, double ty);
-
-
-/* find the affine's "expansion factor", i.e. the scale amount */
-double _rsvg_affine_expansion (const double src[6]);
-
-/* Determine whether the affine transformation is rectilinear,
-   i.e. whether a rectangle aligned to the grid is transformed into
-   another rectangle aligned to the grid. */
-int _rsvg_affine_rectilinear (const double src[6]);
-
-/* Determine whether two affine transformations are equal within grid allignment */
-int _rsvg_affine_equal (double matrix1[6], double matrix2[6]);
-
+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
+void rsvg_push_discrete_layer   (RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
+RsvgNode *rsvg_acquire_node     (RsvgDrawingCtx * ctx, const char *url);
+G_GNUC_INTERNAL
+void rsvg_release_node          (RsvgDrawingCtx * ctx, RsvgNode *node);
+G_GNUC_INTERNAL
+void rsvg_render_path           (RsvgDrawingCtx * ctx, const cairo_path_t *path);
+G_GNUC_INTERNAL
+void rsvg_render_surface        (RsvgDrawingCtx * ctx, cairo_surface_t *surface,
+                                 double x, double y, double w, double h);
+G_GNUC_INTERNAL
+void rsvg_render_free           (RsvgRender * render);
+G_GNUC_INTERNAL
+void rsvg_add_clipping_rect     (RsvgDrawingCtx * ctx, double x, double y, double w, double h);
+G_GNUC_INTERNAL
+cairo_surface_t *rsvg_cairo_surface_from_pixbuf (const GdkPixbuf *pixbuf);
+G_GNUC_INTERNAL
+GdkPixbuf *rsvg_cairo_surface_to_pixbuf (cairo_surface_t *surface);
+G_GNUC_INTERNAL
+cairo_surface_t *rsvg_get_surface_of_node (RsvgDrawingCtx * ctx, RsvgNode * drawable, double w, double h);
+G_GNUC_INTERNAL
 void rsvg_node_set_atts (RsvgNode * node, RsvgHandle * ctx, RsvgPropertyBag * atts);
-
+G_GNUC_INTERNAL
 void rsvg_drawing_ctx_free (RsvgDrawingCtx * handle);
-
-void rsvg_bbox_init	(RsvgBbox * self, double *affine);
-void rsvg_bbox_insert	(RsvgBbox * dst, RsvgBbox * src);
-void rsvg_bbox_clip	(RsvgBbox * dst, RsvgBbox * src);
-
-double _rsvg_css_normalize_length	(const RsvgLength * in, RsvgDrawingCtx * ctx, char dir);
-double _rsvg_css_hand_normalize_length	(const RsvgLength * in, gdouble pixels_per_inch,
-					 gdouble width_or_height, gdouble font_size);
-double _rsvg_css_hand_normalize_length_sub	(const RsvgLength * in, gdouble length,
-						 gdouble pixels_per_inch, gdouble width_or_height, 
-						 gdouble font_size);
-
+G_GNUC_INTERNAL
+void rsvg_bbox_init     (RsvgBbox * self, cairo_matrix_t *matrix);
+G_GNUC_INTERNAL
+void rsvg_bbox_insert   (RsvgBbox * dst, RsvgBbox * src);
+G_GNUC_INTERNAL
+void rsvg_bbox_clip     (RsvgBbox * dst, RsvgBbox * src);
+G_GNUC_INTERNAL
+double _rsvg_css_normalize_length       (const RsvgLength * in, RsvgDrawingCtx * ctx, char dir);
+G_GNUC_INTERNAL
+double _rsvg_css_hand_normalize_length  (const RsvgLength * in, gdouble pixels_per_inch,
+                                         gdouble width_or_height, gdouble font_size);
+double _rsvg_css_normalize_font_size    (RsvgState * state, RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
+double _rsvg_css_accumulate_baseline_shift (RsvgState * state, RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
 RsvgLength _rsvg_css_parse_length (const char *str);
-
+G_GNUC_INTERNAL
 void _rsvg_push_view_box    (RsvgDrawingCtx * ctx, double w, double h);
+G_GNUC_INTERNAL
 void _rsvg_pop_view_box	    (RsvgDrawingCtx * ctx);
-
+G_GNUC_INTERNAL
 void rsvg_SAX_handler_struct_init (void);
-
+G_GNUC_INTERNAL
 char *rsvg_get_url_string (const char *str);
-
+G_GNUC_INTERNAL
 void rsvg_return_if_fail_warning (const char *pretty_function,
                                   const char *expression, GError ** error);
 
-#define rsvg_return_if_fail(expr, error)		G_STMT_START{			\
-     if G_LIKELY(expr) { } else       					\
-       {								\
-	 rsvg_return_if_fail_warning (G_STRFUNC,			\
-		                   #expr, error);			\
-	 return;							\
+G_GNUC_INTERNAL
+char *_rsvg_handle_acquire_data (RsvgHandle *handle,
+                                 const char *uri,
+                                 char **content_type,
+                                 gsize *len,
+                                 GError **error);
+G_GNUC_INTERNAL
+GInputStream *_rsvg_handle_acquire_stream (RsvgHandle *handle,
+                                           const char *uri,
+                                           char **content_type,
+                                           GError **error);
+
+
+#define rsvg_return_if_fail(expr, error)    G_STMT_START{			\
+     if G_LIKELY(expr) { } else                                     \
+       {                                                            \
+           rsvg_return_if_fail_warning (G_STRFUNC,                  \
+                                        #expr, error);              \
+           return;                                                  \
        };				}G_STMT_END
 
-#define rsvg_return_val_if_fail(expr,val,error)	G_STMT_START{			\
-     if G_LIKELY(expr) { } else						\
-       {								\
-	 rsvg_return_if_fail_warning (G_STRFUNC,	    		\
-		                   #expr, error);			\
-	 return (val);							\
+#define rsvg_return_val_if_fail(expr,val,error)	G_STMT_START{       \
+     if G_LIKELY(expr) { } else                                     \
+       {                                                            \
+           rsvg_return_if_fail_warning (G_STRFUNC,                  \
+                                        #expr, error);              \
+           return (val);                                            \
        };				}G_STMT_END
 
 G_END_DECLS
diff --git a/rsvg-shapes.c b/rsvg-shapes.c
index 912bf17..df8641f 100644
--- a/rsvg-shapes.c
+++ b/rsvg-shapes.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-shapes.c: Draw SVG shapes
 
@@ -34,6 +35,7 @@
 #include "rsvg-shapes.h"
 #include "rsvg-css.h"
 #include "rsvg-defs.h"
+#include "rsvg-path.h"
 
 /* 4/3 * (1-cos 45)/sin 45 = 4/3 * sqrt(2) - 1 */
 #define RSVG_ARC_MAGIC ((double) 0.5522847498)
@@ -41,23 +43,24 @@
 static void
 rsvg_node_path_free (RsvgNode * self)
 {
-    RsvgNodePath *z = (RsvgNodePath *) self;
-    if (z->d)
-        g_free (z->d);
-    _rsvg_node_finalize (&z->super);
-    g_free (z);
+    RsvgNodePath *path = (RsvgNodePath *) self;
+    if (path->path)
+        rsvg_cairo_path_destroy (path->path);
+    _rsvg_node_finalize (&path->super);
+    g_free (path);
 }
 
 static void
 rsvg_node_path_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
     RsvgNodePath *path = (RsvgNodePath *) self;
-    if (!path->d)
+
+    if (!path->path)
         return;
 
     rsvg_state_reinherit_top (ctx, self->state, dominate);
 
-    rsvg_render_path (ctx, path->d);
+    rsvg_render_path (ctx, path->path);
 }
 
 static void
@@ -68,9 +71,9 @@ rsvg_node_path_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * at
 
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "d"))) {
-            if (path->d)
-                g_free (path->d);
-            path->d = g_strdup (value);
+            if (path->path)
+                rsvg_cairo_path_destroy (path->path);
+            path->path = rsvg_parse_path (value);
         }
         if ((value = rsvg_property_bag_lookup (atts, "class")))
             klazz = value;
@@ -88,8 +91,8 @@ rsvg_new_path (void)
 {
     RsvgNodePath *path;
     path = g_new (RsvgNodePath, 1);
-    _rsvg_node_init (&path->super);
-    path->d = NULL;
+    _rsvg_node_init (&path->super, RSVG_NODE_TYPE_PATH);
+    path->path = NULL;
     path->super.free = rsvg_node_path_free;
     path->super.draw = rsvg_node_path_draw;
     path->super.set_atts = rsvg_node_path_set_atts;
@@ -99,13 +102,15 @@ rsvg_new_path (void)
 
 struct _RsvgNodePoly {
     RsvgNode super;
-    gdouble *pointlist;
-    gboolean is_polyline;
-    guint pointlist_len;
+    cairo_path_t *path;
 };
 
 typedef struct _RsvgNodePoly RsvgNodePoly;
 
+static cairo_path_t *
+_rsvg_node_poly_build_path (const char *value,
+                            gboolean close_path);
+
 static void
 _rsvg_node_poly_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
@@ -116,7 +121,10 @@ _rsvg_node_poly_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
         /* support for svg < 1.0 which used verts */
         if ((value = rsvg_property_bag_lookup (atts, "verts"))
             || (value = rsvg_property_bag_lookup (atts, "points"))) {
-            poly->pointlist = rsvg_css_parse_number_list (value, &poly->pointlist_len);
+            if (poly->path)
+                rsvg_cairo_path_destroy (poly->path);
+            poly->path = _rsvg_node_poly_build_path (value,
+                                                     RSVG_NODE_TYPE (self) == RSVG_NODE_TYPE_POLYGON);
         }
         if ((value = rsvg_property_bag_lookup (atts, "class")))
             klazz = value;
@@ -125,85 +133,115 @@ _rsvg_node_poly_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
             rsvg_defs_register_name (ctx->priv->defs, value, self);
         }
 
-        rsvg_parse_style_attrs (ctx, self->state, (poly->is_polyline ? "polyline" : "polygon"),
+        rsvg_parse_style_attrs (ctx, self->state,
+                                RSVG_NODE_TYPE (self) == RSVG_NODE_TYPE_POLYLINE ? "polyline" : "polygon",
                                 klazz, id, atts);
     }
 
 }
 
-static void
-_rsvg_node_poly_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
+static cairo_path_t *
+_rsvg_node_poly_build_path (const char *value,
+                            gboolean close_path)
 {
-    RsvgNodePoly *poly = (RsvgNodePoly *) self;
-    gsize i;
-    GString *d;
-    char buf[G_ASCII_DTOSTR_BUF_SIZE];
+    double *pointlist;
+    guint pointlist_len, i;
+    RsvgPathBuilder builder;
+    cairo_path_t *path;
+
+    pointlist = rsvg_css_parse_number_list (value, &pointlist_len);
+    if (pointlist == NULL)
+        return NULL;
+
+    if (pointlist_len < 2) {
+        g_free (pointlist);
+        return NULL;
+    }
 
-    /* represent as a "moveto, lineto*, close" path */
-    if (poly->pointlist_len < 2)
-        return;
+    /* Calculate the number of cairo_path_data_t we'll need:
+     *
+     *     pointlist_len / 2 -> number of commands
+     *     pointlist_len / 2 -> number of points
+     * +   1                 -> closepath
+     * ---------------------------------------------
+     *     pointlist_len + 1 -> total
+     */
+    rsvg_path_builder_init (&builder, pointlist_len + 1);
+
+    rsvg_path_builder_move_to (&builder, pointlist[0], pointlist[1]);
+
+    for (i = 2; i < pointlist_len; i += 2) {
+        double x, y;
+
+        x = pointlist[i];
+
+        /* We expect points to come in coordinate pairs.  But if there is a
+         * missing part of one pair in a corrupt SVG, we'll have an incomplete
+         * list.  In that case, we reuse the last-known Y coordinate.
+         */
+        if (i + 1 < pointlist_len)
+            y = pointlist[i + 1];
+        else
+            y = pointlist[i - 1];
+
+        rsvg_path_builder_line_to (&builder, x, y);
+    }
 
-    d = g_string_new (NULL);
+    if (close_path)
+        rsvg_path_builder_close_path (&builder);
 
-    /*      "M %f %f " */
-    g_string_append (d, " M ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), poly->pointlist[0]));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), poly->pointlist[1]));
+    path = rsvg_path_builder_finish (&builder);
+    g_free (pointlist);
 
-    /* "L %f %f " */
-    for (i = 2; i < poly->pointlist_len; i += 2) {
-        g_string_append (d, " L ");
-        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), poly->pointlist[i]));
-        g_string_append_c (d, ' ');
-        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), poly->pointlist[i + 1]));
-    }
+    return path;
+}
 
-    if (!poly->is_polyline)
-        g_string_append (d, " Z");
+static void
+_rsvg_node_poly_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
+{
+    RsvgNodePoly *poly = (RsvgNodePoly *) self;
+
+    if (poly->path == NULL)
+        return;
 
     rsvg_state_reinherit_top (ctx, self->state, dominate);
-    rsvg_render_path (ctx, d->str);
 
-    g_string_free (d, TRUE);
+    rsvg_render_path (ctx, poly->path);
 }
 
 static void
 _rsvg_node_poly_free (RsvgNode * self)
 {
-    RsvgNodePoly *z = (RsvgNodePoly *) self;
-    if (z->pointlist)
-        g_free (z->pointlist);
-    _rsvg_node_finalize (&z->super);
-    g_free (z);
+    RsvgNodePoly *poly = (RsvgNodePoly *) self;
+    if (poly->path)
+        rsvg_cairo_path_destroy (poly->path);
+    _rsvg_node_finalize (&poly->super);
+    g_free (poly);
 }
 
-
 static RsvgNode *
-rsvg_new_any_poly (gboolean is_polyline)
+rsvg_new_any_poly (RsvgNodeType type)
 {
     RsvgNodePoly *poly;
     poly = g_new (RsvgNodePoly, 1);
-    _rsvg_node_init (&poly->super);
+    _rsvg_node_init (&poly->super, type);
     poly->super.free = _rsvg_node_poly_free;
     poly->super.draw = _rsvg_node_poly_draw;
     poly->super.set_atts = _rsvg_node_poly_set_atts;
-    poly->pointlist = NULL;
-    poly->is_polyline = is_polyline;
-    poly->pointlist_len = 0;
+    poly->path = NULL;
     return &poly->super;
 }
 
 RsvgNode *
 rsvg_new_polygon (void)
 {
-    return rsvg_new_any_poly (FALSE);
+    return rsvg_new_any_poly (RSVG_NODE_TYPE_POLYGON);
 }
 
 RsvgNode *
 rsvg_new_polyline (void)
 {
-    return rsvg_new_any_poly (TRUE);
+    return rsvg_new_any_poly (RSVG_NODE_TYPE_POLYLINE);
 }
 
 
@@ -243,30 +281,27 @@ _rsvg_node_line_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
 static void
 _rsvg_node_line_draw (RsvgNode * overself, RsvgDrawingCtx * ctx, int dominate)
 {
-    GString *d;
-    char buf[G_ASCII_DTOSTR_BUF_SIZE];
+    cairo_path_t *path;
+    RsvgPathBuilder builder;
     RsvgNodeLine *self = (RsvgNodeLine *) overself;
+    double x1, y1, x2, y2;
+
+    rsvg_path_builder_init (&builder, 4);
+
+    x1 = _rsvg_css_normalize_length (&self->x1, ctx, 'h');
+    y1 = _rsvg_css_normalize_length (&self->y1, ctx, 'v');
+    x2 = _rsvg_css_normalize_length (&self->x2, ctx, 'h');
+    y2 = _rsvg_css_normalize_length (&self->y2, ctx, 'v');
 
-    /* emulate a line using a path */
-    /* ("M %f %f L %f %f", x1, y1, x2, y2) */
-    d = g_string_new ("M ");
-
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf),
-                                        _rsvg_css_normalize_length (&self->x1, ctx, 'h')));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf),
-                                        _rsvg_css_normalize_length (&self->y1, ctx, 'v')));
-    g_string_append (d, " L ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf),
-                                        _rsvg_css_normalize_length (&self->x2, ctx, 'h')));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf),
-                                        _rsvg_css_normalize_length (&self->y2, ctx, 'v')));
+    rsvg_path_builder_move_to (&builder, x1, y1);
+    rsvg_path_builder_line_to (&builder, x2, y2);
+
+    path = rsvg_path_builder_finish (&builder);
 
     rsvg_state_reinherit_top (ctx, overself->state, dominate);
-    rsvg_render_path (ctx, d->str);
 
-    g_string_free (d, TRUE);
+    rsvg_render_path (ctx, path);
+    rsvg_cairo_path_destroy (path);
 }
 
 RsvgNode *
@@ -274,7 +309,7 @@ rsvg_new_line (void)
 {
     RsvgNodeLine *line;
     line = g_new (RsvgNodeLine, 1);
-    _rsvg_node_init (&line->super);
+    _rsvg_node_init (&line->super, RSVG_NODE_TYPE_LINE);
     line->super.draw = _rsvg_node_line_draw;
     line->super.set_atts = _rsvg_node_line_set_atts;
     line->x1 = line->x2 = line->y1 = line->y2 = _rsvg_css_parse_length ("0");
@@ -295,6 +330,7 @@ _rsvg_node_rect_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
     const char *klazz = NULL, *id = NULL, *value;
     RsvgNodeRect *rect = (RsvgNodeRect *) self;
 
+    /* FIXME: negative w/h/rx/ry is an error, per http://www.w3.org/TR/SVG11/shapes.html#RectElement */
     if (rsvg_property_bag_size (atts)) {
         if ((value = rsvg_property_bag_lookup (atts, "x")))
             rect->x = _rsvg_css_parse_length (value);
@@ -327,119 +363,142 @@ static void
 _rsvg_node_rect_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
     double x, y, w, h, rx, ry;
-    GString *d = NULL;
+    double half_w, half_h;
+    RsvgPathBuilder builder;
+    cairo_path_t *path;
     RsvgNodeRect *rect = (RsvgNodeRect *) self;
-    char buf[G_ASCII_DTOSTR_BUF_SIZE];
 
     x = _rsvg_css_normalize_length (&rect->x, ctx, 'h');
     y = _rsvg_css_normalize_length (&rect->y, ctx, 'v');
-    w = _rsvg_css_normalize_length (&rect->w, ctx, 'h');
-    h = _rsvg_css_normalize_length (&rect->h, ctx, 'v');
-    rx = _rsvg_css_normalize_length (&rect->rx, ctx, 'h');
-    ry = _rsvg_css_normalize_length (&rect->ry, ctx, 'v');
+
+    /* FIXME: negative w/h/rx/ry is an error, per http://www.w3.org/TR/SVG11/shapes.html#RectElement
+     * For now we'll just take the absolute value.
+     */
+    w = fabs (_rsvg_css_normalize_length (&rect->w, ctx, 'h'));
+    h = fabs (_rsvg_css_normalize_length (&rect->h, ctx, 'v'));
+    rx = fabs (_rsvg_css_normalize_length (&rect->rx, ctx, 'h'));
+    ry = fabs (_rsvg_css_normalize_length (&rect->ry, ctx, 'v'));
+
+    if (w == 0. || h == 0.)
+        return;
 
     if (rect->got_rx)
         rx = rx;
     else
         rx = ry;
+
     if (rect->got_ry)
         ry = ry;
     else
         ry = rx;
 
-    if (rx > fabs (w / 2.))
-        rx = fabs (w / 2.);
-    if (ry > fabs (h / 2.))
-        ry = fabs (h / 2.);
+    half_w = w / 2;
+    half_h = h / 2;
+
+    if (rx > half_w)
+        rx = half_w;
+
+    if (ry > half_h)
+        ry = half_h;
 
     if (rx == 0)
         ry = 0;
     else if (ry == 0)
         rx = 0;
 
-    /* emulate a rect using a path */
-    d = g_string_new ("M ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y));
-
-    g_string_append (d, " H ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + w - rx));
-
-    g_string_append (d, " A");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + w));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y + ry));
-
-    g_string_append (d, " V ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y + h - ry));
-
-    g_string_append (d, " A");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + w - rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y + h));
-
-    g_string_append (d, " H ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + rx));
-
-    g_string_append (d, " A");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y + h - ry));
-
-    g_string_append (d, " V ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y + ry));
-
-    g_string_append (d, " A");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y));
-
-    g_string_append (d, " Z");
+    if (rx == 0) {
+        /* Easy case, no rounded corners */
+
+        rsvg_path_builder_init (&builder, 11);
+
+        rsvg_path_builder_move_to (&builder, x, y);
+        rsvg_path_builder_line_to (&builder, x + w, y);
+        rsvg_path_builder_line_to (&builder, x + w, y + h);
+        rsvg_path_builder_line_to (&builder, x, y + h);
+        rsvg_path_builder_line_to (&builder, x, y);
+        rsvg_path_builder_close_path (&builder);
+    } else {
+        double top_x1, top_x2, top_y;
+        double bottom_x1, bottom_x2, bottom_y;
+        double left_x, left_y1, left_y2;
+        double right_x, right_y1, right_y2;
+
+        /* Hard case, rounded corners
+         *
+         *      (top_x1, top_y)                   (top_x2, top_y)
+         *     *--------------------------------*
+         *    /                                  \
+         *   * (left_x, left_y1)                  * (right_x, right_y1)
+         *   |                                    |
+         *   |                                    |
+         *   |                                    |
+         *   |                                    |
+         *   |                                    |
+         *   |                                    |
+         *   |                                    |
+         *   |                                    |
+         *   |                                    |
+         *   * (left_x, left_y2)                  * (right_x, right_y2)
+         *    \                                  /
+         *     *--------------------------------*
+         *      (bottom_x1, bottom_y)            (bottom_x2, bottom_y)
+         */
+
+        top_x1 = x + rx;
+        top_x2 = x + w - rx;
+        top_y  = y;
+
+        bottom_x1 = top_x1;
+        bottom_x2 = top_x2;
+        bottom_y  = y + h;
+
+        left_x = x;
+        left_y1 = y + ry;
+        left_y2 = y + h - ry;
+
+        right_x = x + w;
+        right_y1 = left_y1;
+        right_y2 = left_y2;
+
+        rsvg_path_builder_init (&builder, 32); /* an estimate; the arc segments may grow the array anyway */
+
+        rsvg_path_builder_move_to (&builder, top_x1, top_y);
+        rsvg_path_builder_line_to (&builder, top_x2, top_y);
+
+        rsvg_path_builder_arc (&builder,
+                               top_x2, top_y,
+                               rx, ry, 0, FALSE, TRUE,
+                               right_x, right_y1);
+
+        rsvg_path_builder_line_to (&builder, right_x, right_y2);
+
+        rsvg_path_builder_arc (&builder,
+                               right_x, right_y2,
+                               rx, ry, 0, FALSE, TRUE,
+                               bottom_x2, bottom_y);
+
+        rsvg_path_builder_line_to (&builder, bottom_x1, bottom_y);
+
+        rsvg_path_builder_arc (&builder,
+                               bottom_x1, bottom_y,
+                               rx, ry, 0, FALSE, TRUE,
+                               left_x, left_y2);
+
+        rsvg_path_builder_line_to (&builder, left_x, left_y1);
+
+        rsvg_path_builder_arc (&builder,
+                               left_x, left_y1,
+                               rx, ry, 0, FALSE, TRUE,
+                               top_x1, top_y);
+
+        rsvg_path_builder_close_path (&builder);
+    }
+
+    path = rsvg_path_builder_finish (&builder);
 
     rsvg_state_reinherit_top (ctx, self->state, dominate);
-    rsvg_render_path (ctx, d->str);
-    g_string_free (d, TRUE);
+    rsvg_render_path (ctx, path);
+    rsvg_cairo_path_destroy (path);
 }
 
 RsvgNode *
@@ -447,7 +506,7 @@ rsvg_new_rect (void)
 {
     RsvgNodeRect *rect;
     rect = g_new (RsvgNodeRect, 1);
-    _rsvg_node_init (&rect->super);
+    _rsvg_node_init (&rect->super, RSVG_NODE_TYPE_RECT);
     rect->super.draw = _rsvg_node_rect_draw;
     rect->super.set_atts = _rsvg_node_rect_set_atts;
     rect->x = rect->y = rect->w = rect->h = rect->rx = rect->ry = _rsvg_css_parse_length ("0");
@@ -489,10 +548,10 @@ _rsvg_node_circle_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag *
 static void
 _rsvg_node_circle_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
-    GString *d = NULL;
+    cairo_path_t *path;
     RsvgNodeCircle *circle = (RsvgNodeCircle *) self;
-    char buf[G_ASCII_DTOSTR_BUF_SIZE];
     double cx, cy, r;
+    RsvgPathBuilder builder;
 
     cx = _rsvg_css_normalize_length (&circle->cx, ctx, 'h');
     cy = _rsvg_css_normalize_length (&circle->cy, ctx, 'v');
@@ -503,69 +562,37 @@ _rsvg_node_circle_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
     /* approximate a circle using 4 bezier curves */
 
-    d = g_string_new ("M ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r * RSVG_ARC_MAGIC));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
-
-    g_string_append (d, " Z");
+    rsvg_path_builder_init (&builder, 19);
 
-    rsvg_state_reinherit_top (ctx, self->state, dominate);
-    rsvg_render_path (ctx, d->str);
+    rsvg_path_builder_move_to (&builder, cx + r, cy);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx + r, cy + r * RSVG_ARC_MAGIC,
+                                cx + r * RSVG_ARC_MAGIC, cy + r,
+                                cx, cy + r);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx - r * RSVG_ARC_MAGIC, cy + r,
+                                cx - r, cy + r * RSVG_ARC_MAGIC,
+                                cx - r, cy);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx - r, cy - r * RSVG_ARC_MAGIC,
+                                cx - r * RSVG_ARC_MAGIC, cy - r,
+                                cx, cy - r);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx + r * RSVG_ARC_MAGIC, cy - r,
+                                cx + r, cy - r * RSVG_ARC_MAGIC,
+                                cx + r, cy);
 
-    g_string_free (d, TRUE);
+    rsvg_path_builder_close_path (&builder);
+
+    path = rsvg_path_builder_finish (&builder);
+
+    rsvg_state_reinherit_top (ctx, self->state, dominate);
+    rsvg_render_path (ctx, path);
+    rsvg_cairo_path_destroy (path);
 }
 
 RsvgNode *
@@ -573,7 +600,7 @@ rsvg_new_circle (void)
 {
     RsvgNodeCircle *circle;
     circle = g_new (RsvgNodeCircle, 1);
-    _rsvg_node_init (&circle->super);
+    _rsvg_node_init (&circle->super, RSVG_NODE_TYPE_CIRCLE);
     circle->super.draw = _rsvg_node_circle_draw;
     circle->super.set_atts = _rsvg_node_circle_set_atts;
     circle->cx = circle->cy = circle->r = _rsvg_css_parse_length ("0");
@@ -617,9 +644,9 @@ static void
 _rsvg_node_ellipse_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
     RsvgNodeEllipse *ellipse = (RsvgNodeEllipse *) self;
-    GString *d = NULL;
-    char buf[G_ASCII_DTOSTR_BUF_SIZE];
+    cairo_path_t *path;
     double cx, cy, rx, ry;
+    RsvgPathBuilder builder;
 
     cx = _rsvg_css_normalize_length (&ellipse->cx, ctx, 'h');
     cy = _rsvg_css_normalize_length (&ellipse->cy, ctx, 'v');
@@ -628,70 +655,40 @@ _rsvg_node_ellipse_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
     if (rx <= 0 || ry <= 0)
         return;
+
     /* approximate an ellipse using 4 bezier curves */
 
-    d = g_string_new ("M ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - RSVG_ARC_MAGIC * ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + RSVG_ARC_MAGIC * rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - ry));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - RSVG_ARC_MAGIC * rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - RSVG_ARC_MAGIC * ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + RSVG_ARC_MAGIC * ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - RSVG_ARC_MAGIC * rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + ry));
-
-    g_string_append (d, " C ");
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + RSVG_ARC_MAGIC * rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + RSVG_ARC_MAGIC * ry));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
-    g_string_append_c (d, ' ');
-    g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
-
-    g_string_append (d, " Z");
+    rsvg_path_builder_init (&builder, 19);
+
+    rsvg_path_builder_move_to (&builder, cx + rx, cy);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx + rx, cy - RSVG_ARC_MAGIC * ry,
+                                cx + RSVG_ARC_MAGIC * rx, cy - ry,
+                                cx, cy - ry);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx - RSVG_ARC_MAGIC * rx, cy - ry,
+                                cx - rx, cy - RSVG_ARC_MAGIC * ry,
+                                cx - rx, cy);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx - rx, cy + RSVG_ARC_MAGIC * ry,
+                                cx - RSVG_ARC_MAGIC * rx, cy + ry,
+                                cx, cy + ry);
+
+    rsvg_path_builder_curve_to (&builder,
+                                cx + RSVG_ARC_MAGIC * rx, cy + ry,
+                                cx + rx, cy + RSVG_ARC_MAGIC * ry,
+                                cx + rx, cy);
+
+    rsvg_path_builder_close_path (&builder);
+
+    path = rsvg_path_builder_finish (&builder);
 
     rsvg_state_reinherit_top (ctx, self->state, dominate);
-    rsvg_render_path (ctx, d->str);
-    g_string_free (d, TRUE);
+    rsvg_render_path (ctx, path);
+    rsvg_cairo_path_destroy (path);
 }
 
 RsvgNode *
@@ -699,7 +696,7 @@ rsvg_new_ellipse (void)
 {
     RsvgNodeEllipse *ellipse;
     ellipse = g_new (RsvgNodeEllipse, 1);
-    _rsvg_node_init (&ellipse->super);
+    _rsvg_node_init (&ellipse->super, RSVG_NODE_TYPE_ELLIPSE);
     ellipse->super.draw = _rsvg_node_ellipse_draw;
     ellipse->super.set_atts = _rsvg_node_ellipse_set_atts;
     ellipse->cx = ellipse->cy = ellipse->rx = ellipse->ry = _rsvg_css_parse_length ("0");
diff --git a/rsvg-shapes.h b/rsvg-shapes.h
index b4ed0b3..66c6e34 100644
--- a/rsvg-shapes.h
+++ b/rsvg-shapes.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-shapes.h: Draw SVG shapes
 
@@ -29,24 +30,32 @@
 #ifndef RSVG_SHAPES_H
 #define RSVG_SHAPES_H
 
+#include <cairo.h>
+
 #include "rsvg-structure.h"
 
 G_BEGIN_DECLS 
 
-RsvgNode * rsvg_new_path (void);
+G_GNUC_INTERNAL
+RsvgNode *rsvg_new_path (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_polygon (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_polyline (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_line (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_rect (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_circle (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_ellipse (void);
 
-
 typedef struct _RsvgNodePath RsvgNodePath;
 
 struct _RsvgNodePath {
     RsvgNode super;
-    char *d;
+    cairo_path_t *path;
 };
 
 G_END_DECLS
diff --git a/rsvg-size-callback.c b/rsvg-size-callback.c
new file mode 100644
index 0000000..d8a114f
--- /dev/null
+++ b/rsvg-size-callback.c
@@ -0,0 +1,103 @@
+/*
+   Copyright (C) 2000 Eazel, Inc.
+   Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Raph Levien <raph@artofcode.com>
+*/
+
+#include "config.h"
+
+#include "rsvg-size-callback.h"
+
+#include <math.h>
+
+void
+_rsvg_size_callback (int *width, int *height, gpointer data)
+{
+    struct RsvgSizeCallbackData *real_data = (struct RsvgSizeCallbackData *) data;
+    double zoomx, zoomy, zoom;
+
+    int in_width, in_height;
+
+    in_width = *width;
+    in_height = *height;
+
+    switch (real_data->type) {
+    case RSVG_SIZE_ZOOM:
+        if (*width < 0 || *height < 0)
+            return;
+
+        *width = floor (real_data->x_zoom * *width + 0.5);
+        *height = floor (real_data->y_zoom * *height + 0.5);
+        break;
+
+    case RSVG_SIZE_ZOOM_MAX:
+        if (*width < 0 || *height < 0)
+            return;
+
+        *width = floor (real_data->x_zoom * *width + 0.5);
+        *height = floor (real_data->y_zoom * *height + 0.5);
+
+        if (*width > real_data->width || *height > real_data->height) {
+            zoomx = (double) real_data->width / *width;
+            zoomy = (double) real_data->height / *height;
+            zoom = MIN (zoomx, zoomy);
+
+            *width = floor (zoom * *width + 0.5);
+            *height = floor (zoom * *height + 0.5);
+        }
+        break;
+
+    case RSVG_SIZE_WH_MAX:
+        if (*width < 0 || *height < 0)
+            return;
+
+        zoomx = (double) real_data->width / *width;
+        zoomy = (double) real_data->height / *height;
+        if (zoomx < 0)
+            zoom = zoomy;
+        else if (zoomy < 0)
+            zoom = zoomx;
+        else
+            zoom = MIN (zoomx, zoomy);
+
+        *width = floor (zoom * *width + 0.5);
+        *height = floor (zoom * *height + 0.5);
+        break;
+
+    case RSVG_SIZE_WH:
+        if (real_data->width != -1)
+            *width = real_data->width;
+        if (real_data->height != -1)
+            *height = real_data->height;
+        break;
+
+    default:
+        g_assert_not_reached ();
+    }
+
+    if (real_data->keep_aspect_ratio) {
+        int out_min = MIN (*width, *height);
+
+        if (out_min == *width) {
+            *height = in_height * ((double) *width / (double) in_width);
+        } else {
+            *width = in_width * ((double) *height / (double) in_height);
+        }
+    }
+}
diff --git a/rsvg-size-callback.h b/rsvg-size-callback.h
new file mode 100644
index 0000000..70507d0
--- /dev/null
+++ b/rsvg-size-callback.h
@@ -0,0 +1,52 @@
+/*
+   Copyright (C) 2000 Eazel, Inc.
+   Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Raph Levien <raph@artofcode.com>
+*/
+
+#ifndef RSVG_SIZE_CALLBACK_H
+#define RSVG_SIZE_CALLBACK_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+    RSVG_SIZE_ZOOM,
+    RSVG_SIZE_WH,
+    RSVG_SIZE_WH_MAX,
+    RSVG_SIZE_ZOOM_MAX
+} RsvgSizeType;
+
+struct RsvgSizeCallbackData {
+    RsvgSizeType type;
+    double x_zoom;
+    double y_zoom;
+    gint width;
+    gint height;
+
+    gboolean keep_aspect_ratio;
+};
+
+G_GNUC_INTERNAL
+void _rsvg_size_callback (int *width, int *height, gpointer data);
+
+G_END_DECLS
+
+#endif /* RSVG_SIZE_CALLBACK_H */
diff --git a/rsvg-structure.c b/rsvg-structure.c
index 4841494..4ab4473 100644
--- a/rsvg-structure.c
+++ b/rsvg-structure.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-structure.c: Rsvg's structual elements
 
@@ -50,25 +51,8 @@ rsvg_node_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
     if (!state->visible)
         return;
 
-    if (g_slist_find(ctx->ptrs, self) != NULL)
-    {
-        /*
-         * 5.3.1 of the SVG 1.1 spec (http://www.w3.org/TR/SVG11/struct.html#HeadOverview)
-         * seems to indicate ("URI references that directly or indirectly reference
-         * themselves are treated as invalid circular references") that circular
-         * references are invalid, and so we can drop them to avoid infinite recursion.
-         * 
-         * See also http://bugzilla.gnome.org/show_bug.cgi?id=518640
-         */
-        g_warning("Circular SVG reference noticed, dropping");
-        return;
-    }
-    ctx->ptrs = g_slist_append(ctx->ptrs, self);
-
     self->draw (self, ctx, dominate);
     ctx->drawsub_stack = stacksave;
-
-    ctx->ptrs = g_slist_remove(ctx->ptrs, self);
 }
 
 /* generic function for drawing all of the children of a particular node */
@@ -102,16 +86,17 @@ _rsvg_node_dont_set_atts (RsvgNode * node, RsvgHandle * ctx, RsvgPropertyBag * a
 }
 
 void
-_rsvg_node_init (RsvgNode * self)
+_rsvg_node_init (RsvgNode * self,
+                 RsvgNodeType type)
 {
-	self->parent = NULL;
+    self->type = type;
+    self->parent = NULL;
     self->children = g_ptr_array_new ();
     self->state = g_new (RsvgState, 1);
     rsvg_state_init (self->state);
     self->free = _rsvg_node_free;
     self->draw = _rsvg_node_draw_nothing;
     self->set_atts = _rsvg_node_dont_set_atts;
-    self->type = NULL;
 }
 
 void
@@ -123,9 +108,6 @@ _rsvg_node_finalize (RsvgNode * self)
     }
     if (self->children != NULL)
         g_ptr_array_free (self->children, TRUE);
-    if (self->type != NULL)
-        g_string_free (self->type, TRUE);
-
 }
 
 void
@@ -157,7 +139,7 @@ rsvg_new_group (void)
 {
     RsvgNodeGroup *group;
     group = g_new (RsvgNodeGroup, 1);
-    _rsvg_node_init (&group->super);
+    _rsvg_node_init (&group->super, RSVG_NODE_TYPE_GROUP);
     group->super.draw = _rsvg_node_draw_children;
     group->super.set_atts = rsvg_node_group_set_atts;
     return &group->super;
@@ -166,8 +148,8 @@ rsvg_new_group (void)
 void
 rsvg_pop_def_group (RsvgHandle * ctx)
 {
-    if (ctx->priv->currentnode != NULL)
-        ctx->priv->currentnode = ctx->priv->currentnode->parent;
+    g_assert (ctx->priv->currentnode != NULL);
+    ctx->priv->currentnode = ctx->priv->currentnode->parent;
 }
 
 void
@@ -200,7 +182,7 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
     RsvgNodeUse *use = (RsvgNodeUse *) self;
     RsvgNode *child;
     RsvgState *state;
-    double affine[6];
+    cairo_matrix_t affine;
     double x, y, w, h;
     x = _rsvg_css_normalize_length (&use->x, ctx, 'h');
     y = _rsvg_css_normalize_length (&use->y, ctx, 'v');
@@ -209,18 +191,20 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
     rsvg_state_reinherit_top (ctx, self->state, dominate);
 
-    child = use->link;
-
-    /* If it can find nothing to draw... draw nothing */
+    if (use->link == NULL)
+      return;
+    child = rsvg_acquire_node (ctx, use->link);
     if (!child)
         return;
-    else if (rsvg_node_is_ancestor (child, self))       /* or, if we're <use>'ing ourself */
+    else if (rsvg_node_is_ancestor (child, self)) {     /* or, if we're <use>'ing ourself */
+        rsvg_release_node (ctx, child);
         return;
+    }
 
-    state = rsvg_state_current (ctx);
-    if (strcmp (child->type->str, "symbol")) {
-        _rsvg_affine_translate (affine, x, y);
-        _rsvg_affine_multiply (state->affine, affine, state->affine);
+    state = rsvg_current_state (ctx);
+    if (RSVG_NODE_TYPE (child) != RSVG_NODE_TYPE_SYMBOL) {
+        cairo_matrix_init_translate (&affine, x, y);
+        cairo_matrix_multiply (&state->affine, &affine, &state->affine);
 
         rsvg_push_discrete_layer (ctx);
         rsvg_state_push (ctx);
@@ -232,23 +216,27 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
         if (symbol->vbox.active) {
             rsvg_preserve_aspect_ratio
-                (symbol->preserve_aspect_ratio, symbol->vbox.w, symbol->vbox.h, &w, &h, &x, &y);
+                (symbol->preserve_aspect_ratio,
+                 symbol->vbox.rect.width, symbol->vbox.rect.height,
+                 &w, &h, &x, &y);
+
+            cairo_matrix_init_translate (&affine, x, y);
+            cairo_matrix_multiply (&state->affine, &affine, &state->affine);
 
-            _rsvg_affine_translate (affine, x, y);
-            _rsvg_affine_multiply (state->affine, affine, state->affine);
-            _rsvg_affine_scale (affine, w / symbol->vbox.w, h / symbol->vbox.h);
-            _rsvg_affine_multiply (state->affine, affine, state->affine);
-            _rsvg_affine_translate (affine, -symbol->vbox.x, -symbol->vbox.y);
-            _rsvg_affine_multiply (state->affine, affine, state->affine);
+            cairo_matrix_init_scale (&affine, w / symbol->vbox.rect.width, h / symbol->vbox.rect.height);
+            cairo_matrix_multiply (&state->affine, &affine, &state->affine);
 
-            _rsvg_push_view_box (ctx, symbol->vbox.w, symbol->vbox.h);
+            cairo_matrix_init_translate (&affine, -symbol->vbox.rect.x, -symbol->vbox.rect.y);
+            cairo_matrix_multiply (&state->affine, &affine, &state->affine);
+
+            _rsvg_push_view_box (ctx, symbol->vbox.rect.width, symbol->vbox.rect.height);
             rsvg_push_discrete_layer (ctx);
             if (!state->overflow || (!state->has_overflow && child->state->overflow))
-                rsvg_add_clipping_rect (ctx, symbol->vbox.x, symbol->vbox.y,
-                                        symbol->vbox.w, symbol->vbox.h);
+                rsvg_add_clipping_rect (ctx, symbol->vbox.rect.x, symbol->vbox.rect.y,
+                                        symbol->vbox.rect.width, symbol->vbox.rect.height);
         } else {
-            _rsvg_affine_translate (affine, x, y);
-            _rsvg_affine_multiply (state->affine, affine, state->affine);
+            cairo_matrix_init_translate (&affine, x, y);
+            cairo_matrix_multiply (&state->affine, &affine, &state->affine);
             rsvg_push_discrete_layer (ctx);
         }
 
@@ -260,6 +248,7 @@ rsvg_node_use_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
             _rsvg_pop_view_box (ctx);
     }
 
+    rsvg_release_node (ctx, child);
 }
 
 static void
@@ -267,7 +256,7 @@ rsvg_node_svg_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
     RsvgNodeSvg *sself;
     RsvgState *state;
-    gdouble affine[6], affine_old[6], affine_new[6];
+    cairo_matrix_t affine, affine_old, affine_new;
     guint i;
     double nx, ny, nw, nh;
     sself = (RsvgNodeSvg *) self;
@@ -279,46 +268,40 @@ rsvg_node_svg_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 
     rsvg_state_reinherit_top (ctx, self->state, dominate);
 
-    state = rsvg_state_current (ctx);
+    state = rsvg_current_state (ctx);
 
-    for (i = 0; i < 6; i++)
-        affine_old[i] = state->affine[i];
+    affine_old = state->affine;
 
     if (sself->vbox.active) {
         double x = nx, y = ny, w = nw, h = nh;
         rsvg_preserve_aspect_ratio (sself->preserve_aspect_ratio,
-                                    sself->vbox.w, sself->vbox.h, &w, &h, &x, &y);
-        affine[0] = w / sself->vbox.w;
-        affine[1] = 0;
-        affine[2] = 0;
-        affine[3] = h / sself->vbox.h;
-        affine[4] = x - sself->vbox.x * w / sself->vbox.w;
-        affine[5] = y - sself->vbox.y * h / sself->vbox.h;
-        _rsvg_affine_multiply (state->affine, affine, state->affine);
-        _rsvg_push_view_box (ctx, sself->vbox.w, sself->vbox.h);
+                                    sself->vbox.rect.width, sself->vbox.rect.height,
+                                    &w, &h, &x, &y);
+        cairo_matrix_init (&affine,
+                           w / sself->vbox.rect.width,
+                           0,
+                           0,
+                           h / sself->vbox.rect.height,
+                           x - sself->vbox.rect.x * w / sself->vbox.rect.width,
+                           y - sself->vbox.rect.y * h / sself->vbox.rect.height);
+        cairo_matrix_multiply (&state->affine, &affine, &state->affine);
+        _rsvg_push_view_box (ctx, sself->vbox.rect.width, sself->vbox.rect.height);
     } else {
-        affine[0] = 1;
-        affine[1] = 0;
-        affine[2] = 0;
-        affine[3] = 1;
-        affine[4] = nx;
-        affine[5] = ny;
-        _rsvg_affine_multiply (state->affine, affine, state->affine);
+        cairo_matrix_init_translate (&affine, nx, ny);
+        cairo_matrix_multiply (&state->affine, &affine, &state->affine);
         _rsvg_push_view_box (ctx, nw, nh);
     }
-    for (i = 0; i < 6; i++)
-        affine_new[i] = state->affine[i];
+
+    affine_new = state->affine;
 
     rsvg_push_discrete_layer (ctx);
 
     /* Bounding box addition must be AFTER the discrete layer push, 
        which must be AFTER the transformation happens. */
     if (!state->overflow && self->parent) {
-        for (i = 0; i < 6; i++)
-            state->affine[i] = affine_old[i];
+        state->affine = affine_old;
         rsvg_add_clipping_rect (ctx, nx, ny, nw, nh);
-        for (i = 0; i < 6; i++)
-            state->affine[i] = affine_new[i];
+        state->affine = affine_new;
     }
 
     for (i = 0; i < self->children->len; i++) {
@@ -334,7 +317,7 @@ rsvg_node_svg_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 static void
 rsvg_node_svg_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
-    const char *id = NULL, *klazz = NULL, *value;
+    const char *value;
     RsvgNodeSvg *svg = (RsvgNodeSvg *) self;
 
     if (rsvg_property_bag_size (atts)) {
@@ -345,32 +328,51 @@ rsvg_node_svg_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * att
             svg->preserve_aspect_ratio = rsvg_css_parse_aspect_ratio (value);
         if ((value = rsvg_property_bag_lookup (atts, "width")))
             svg->w = _rsvg_css_parse_length (value);
-	else {
-	    svg->w.length = -1;
-	    svg->w.factor = '\0';
-	}
         if ((value = rsvg_property_bag_lookup (atts, "height")))
             svg->h = _rsvg_css_parse_length (value);
-	else {
-	    svg->h.length = -1;
-	    svg->h.factor = '\0';
-	}
-		/* 
-		 * x & y attributes have no effect on outermost svg
-		 * http://www.w3.org/TR/SVG/struct.html#SVGElement 
-		 */
+        /* 
+         * x & y attributes have no effect on outermost svg
+         * http://www.w3.org/TR/SVG/struct.html#SVGElement 
+         */
         if (self->parent && (value = rsvg_property_bag_lookup (atts, "x")))
             svg->x = _rsvg_css_parse_length (value);
         if (self->parent && (value = rsvg_property_bag_lookup (atts, "y")))
             svg->y = _rsvg_css_parse_length (value);
-        if ((value = rsvg_property_bag_lookup (atts, "class")))
-            klazz = value;
         if ((value = rsvg_property_bag_lookup (atts, "id"))) {
-            id = value;
             rsvg_defs_register_name (ctx->priv->defs, value, &svg->super);
         }
-        rsvg_parse_style_attrs (ctx, self->state, "svg", klazz, id, atts);
+        /*
+         * style element is not loaded yet here, so we need to store those attribues
+         * to be applied later.
+         */
+        svg->atts = rsvg_property_bag_dup(atts);
+    }
+}
+
+void
+_rsvg_node_svg_apply_atts (RsvgNodeSvg * self, RsvgHandle * ctx)
+{
+    const char *id = NULL, *klazz = NULL, *value;
+    if (self->atts && rsvg_property_bag_size (self->atts)) {
+        if ((value = rsvg_property_bag_lookup (self->atts, "class")))
+            klazz = value;
+        if ((value = rsvg_property_bag_lookup (self->atts, "id")))
+            id = value;
+        rsvg_parse_style_attrs (ctx, ((RsvgNode *)self)->state, "svg", klazz, id, self->atts);
+    }
+}
+
+static void
+_rsvg_svg_free (RsvgNode * self)
+{
+    RsvgNodeSvg *svg = (RsvgNodeSvg *) self;
+
+    if (svg->atts) {
+        rsvg_property_bag_free (svg->atts);
+        svg->atts = NULL;
     }
+
+    _rsvg_node_free (self);
 }
 
 RsvgNode *
@@ -378,7 +380,7 @@ rsvg_new_svg (void)
 {
     RsvgNodeSvg *svg;
     svg = g_new (RsvgNodeSvg, 1);
-    _rsvg_node_init (&svg->super);
+    _rsvg_node_init (&svg->super, RSVG_NODE_TYPE_SVG);
     svg->vbox.active = FALSE;
     svg->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
     svg->x = _rsvg_css_parse_length ("0");
@@ -386,11 +388,21 @@ rsvg_new_svg (void)
     svg->w = _rsvg_css_parse_length ("100%");
     svg->h = _rsvg_css_parse_length ("100%");
     svg->super.draw = rsvg_node_svg_draw;
+    svg->super.free = _rsvg_svg_free;
     svg->super.set_atts = rsvg_node_svg_set_atts;
+    svg->atts = NULL;
     return &svg->super;
 }
 
 static void
+rsvg_node_use_free (RsvgNode * node)
+{
+    RsvgNodeUse *use = (RsvgNodeUse *) node;
+    g_free (use->link);
+    _rsvg_node_free (node);
+}
+
+static void
 rsvg_node_use_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
     const char *value = NULL, *klazz = NULL, *id = NULL;
@@ -412,20 +424,23 @@ rsvg_node_use_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * att
             id = value;
             rsvg_defs_register_name (ctx->priv->defs, value, &use->super);
         }
-        if ((value = rsvg_property_bag_lookup (atts, "xlink:href")))
-            rsvg_defs_add_resolver (ctx->priv->defs, &use->link, value);
+        if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) {
+            g_free (use->link);
+            use->link = g_strdup (value);
+        }
         rsvg_parse_style_attrs (ctx, self->state, "use", klazz, id, atts);
     }
 
 }
 
 RsvgNode *
-rsvg_new_use ()
+rsvg_new_use (void)
 {
     RsvgNodeUse *use;
     use = g_new (RsvgNodeUse, 1);
-    _rsvg_node_init (&use->super);
+    _rsvg_node_init (&use->super, RSVG_NODE_TYPE_USE);
     use->super.draw = rsvg_node_use_draw;
+    use->super.free = rsvg_node_use_free;
     use->super.set_atts = rsvg_node_use_set_atts;
     use->x = _rsvg_css_parse_length ("0");
     use->y = _rsvg_css_parse_length ("0");
@@ -465,7 +480,7 @@ rsvg_new_symbol (void)
 {
     RsvgNodeSymbol *symbol;
     symbol = g_new (RsvgNodeSymbol, 1);
-    _rsvg_node_init (&symbol->super);
+    _rsvg_node_init (&symbol->super, RSVG_NODE_TYPE_SYMBOL);
     symbol->vbox.active = FALSE;
     symbol->preserve_aspect_ratio = RSVG_ASPECT_RATIO_XMID_YMID;
     symbol->super.draw = _rsvg_node_draw_nothing;
@@ -474,11 +489,11 @@ rsvg_new_symbol (void)
 }
 
 RsvgNode *
-rsvg_new_defs ()
+rsvg_new_defs (void)
 {
     RsvgNodeGroup *group;
     group = g_new (RsvgNodeGroup, 1);
-    _rsvg_node_init (&group->super);
+    _rsvg_node_init (&group->super, RSVG_NODE_TYPE_DEFS);
     group->super.draw = _rsvg_node_draw_nothing;
     group->super.set_atts = rsvg_node_group_set_atts;
     return &group->super;
@@ -513,7 +528,7 @@ rsvg_new_switch (void)
 {
     RsvgNodeGroup *group;
     group = g_new (RsvgNodeGroup, 1);
-    _rsvg_node_init (&group->super);
+    _rsvg_node_init (&group->super, RSVG_NODE_TYPE_SWITCH);
     group->super.draw = _rsvg_node_switch_draw;
     group->super.set_atts = rsvg_node_group_set_atts;
     return &group->super;
diff --git a/rsvg-structure.h b/rsvg-structure.h
index ac372e0..601a273 100644
--- a/rsvg-structure.h
+++ b/rsvg-structure.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-structure.h: Rsvg's structual elements
 
@@ -35,11 +36,17 @@
 
 G_BEGIN_DECLS 
 
-RsvgNode * rsvg_new_use (void);
+G_GNUC_INTERNAL
+RsvgNode *rsvg_new_use (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_symbol (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_svg (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_defs (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_group (void);
+G_GNUC_INTERNAL
 RsvgNode *rsvg_new_switch (void);
 
 typedef struct _RsvgNodeGroup RsvgNodeGroup;
@@ -49,6 +56,7 @@ typedef struct _RsvgNodeSvg RsvgNodeSvg;
 
 struct _RsvgNodeGroup {
     RsvgNode super;
+    char *name;
 };
 
 struct _RsvgNodeSymbol {
@@ -59,7 +67,7 @@ struct _RsvgNodeSymbol {
 
 struct _RsvgNodeUse {
     RsvgNode super;
-    RsvgNode *link;
+    char *link;
     RsvgLength x, y, w, h;
 };
 
@@ -68,17 +76,25 @@ struct _RsvgNodeSvg {
     gint preserve_aspect_ratio;
     RsvgLength x, y, w, h;
     RsvgViewBox vbox;
-    GdkPixbuf *img;
+    RsvgPropertyBag *atts;
 };
 
-void rsvg_pop_def_group		(RsvgHandle * ctx);
-void rsvg_node_group_pack	(RsvgNode * self, RsvgNode * child);
-
-void rsvg_node_draw		(RsvgNode * self, RsvgDrawingCtx * ctx, int dominate);
-void _rsvg_node_draw_children	(RsvgNode * self, RsvgDrawingCtx * ctx, int dominate);
-void _rsvg_node_finalize	(RsvgNode * self);
-void _rsvg_node_free		(RsvgNode * self);
-void _rsvg_node_init		(RsvgNode * self);
+G_GNUC_INTERNAL
+void rsvg_pop_def_group     (RsvgHandle * ctx);
+G_GNUC_INTERNAL
+void rsvg_node_group_pack   (RsvgNode * self, RsvgNode * child);
+G_GNUC_INTERNAL
+void rsvg_node_draw         (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate);
+G_GNUC_INTERNAL
+void _rsvg_node_draw_children   (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate);
+G_GNUC_INTERNAL
+void _rsvg_node_finalize    (RsvgNode * self);
+G_GNUC_INTERNAL
+void _rsvg_node_free        (RsvgNode * self);
+G_GNUC_INTERNAL
+void _rsvg_node_init        (RsvgNode * self, RsvgNodeType type);
+G_GNUC_INTERNAL
+void _rsvg_node_svg_apply_atts  (RsvgNodeSvg * self, RsvgHandle * ctx);
 
 G_END_DECLS
 
diff --git a/rsvg-styles.c b/rsvg-styles.c
index 0e6095c..6836894 100644
--- a/rsvg-styles.c
+++ b/rsvg-styles.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-styles.c: Handle SVG styles
 
@@ -22,6 +23,8 @@
 
    Author: Raph Levien <raph@artofcode.com>
 */
+#include "config.h"
+
 #include <string.h>
 #include <math.h>
 
@@ -34,8 +37,65 @@
 #include "rsvg-mask.h"
 #include "rsvg-marker.h"
 
+#include <libcroco/libcroco.h>
+
 #define RSVG_DEFAULT_FONT "Times New Roman"
 
+enum {
+  SHAPE_RENDERING_AUTO = CAIRO_ANTIALIAS_DEFAULT,
+  SHAPE_RENDERING_OPTIMIZE_SPEED = CAIRO_ANTIALIAS_NONE,
+  SHAPE_RENDERING_CRISP_EDGES = CAIRO_ANTIALIAS_NONE,
+  SHAPE_RENDERING_GEOMETRIC_PRECISION = CAIRO_ANTIALIAS_DEFAULT
+};
+
+enum {
+  TEXT_RENDERING_AUTO = CAIRO_ANTIALIAS_DEFAULT,
+  TEXT_RENDERING_OPTIMIZE_SPEED = CAIRO_ANTIALIAS_NONE,
+  TEXT_RENDERING_OPTIMIZE_LEGIBILITY = CAIRO_ANTIALIAS_DEFAULT,
+  TEXT_RENDERING_GEOMETRIC_PRECISION = CAIRO_ANTIALIAS_DEFAULT
+};
+
+typedef struct _StyleValueData {
+    gchar *value;
+    gboolean important;
+} StyleValueData;
+
+/*
+ * _rsvg_cairo_matrix_init_shear: Set up a shearing matrix.
+ * @dst: Where to store the resulting affine transform.
+ * @theta: Shear angle in degrees.
+ *
+ * Sets up a shearing matrix. In the standard libart coordinate system
+ * and a small value for theta, || becomes \\. Horizontal lines remain
+ * unchanged.
+ **/
+static void
+_rsvg_cairo_matrix_init_shear (cairo_matrix_t *dst, double theta)
+{
+  cairo_matrix_init (dst, 1., 0., tan (theta * M_PI / 180.0), 1., 0., 0);
+}
+
+static StyleValueData *
+style_value_data_new (const gchar *value, gboolean important)
+{
+    StyleValueData *ret;
+
+    ret = g_new (StyleValueData, 1);
+    ret->value = g_strdup (value);
+    ret->important = important;
+
+    return ret;
+}
+
+static void
+style_value_data_free (StyleValueData *value)
+{
+    if (!value)
+        return;
+    g_free (value->value);
+    g_free (value);
+}
+
 gdouble
 rsvg_viewport_percentage (gdouble width, gdouble height)
 {
@@ -53,23 +113,28 @@ rsvg_state_init (RsvgState * state)
 {
     memset (state, 0, sizeof (RsvgState));
 
-    _rsvg_affine_identity (state->affine);
-    _rsvg_affine_identity (state->personal_affine);
+    state->parent = NULL;
+    cairo_matrix_init_identity (&state->affine);
+    cairo_matrix_init_identity (&state->personal_affine);
     state->mask = NULL;
     state->opacity = 0xff;
-    state->adobe_blend = 0;
-    state->fill = rsvg_paint_server_parse (NULL, NULL, "#000", 0);
+    state->baseline_shift = 0.;
+    state->current_color = 0xff000000; /* See bgo#764808; we don't inherit CSS
+                                        * from the public API, so start off with
+                                        * opaque black instead of transparent.
+                                        */
+    state->fill = rsvg_paint_server_parse (NULL, "#000");
     state->fill_opacity = 0xff;
     state->stroke_opacity = 0xff;
     state->stroke_width = _rsvg_css_parse_length ("1");
     state->miter_limit = 4;
-    state->cap = RSVG_PATH_STROKE_CAP_BUTT;
-    state->join = RSVG_PATH_STROKE_JOIN_MITER;
+    state->cap = CAIRO_LINE_CAP_BUTT;
+    state->join = CAIRO_LINE_JOIN_MITER;
     state->stop_opacity = 0xff;
-    state->fill_rule = FILL_RULE_NONZERO;
-    state->clip_rule = FILL_RULE_NONZERO;
+    state->fill_rule = CAIRO_FILL_RULE_WINDING;
+    state->clip_rule = CAIRO_FILL_RULE_WINDING;
     state->enable_background = RSVG_ENABLE_BACKGROUND_ACCUMULATE;
-    state->comp_op = RSVG_COMP_OP_SRC_OVER;
+    state->comp_op = CAIRO_OPERATOR_OVER;
     state->overflow = FALSE;
     state->flood_color = 0;
     state->flood_opacity = 255;
@@ -81,17 +146,19 @@ rsvg_state_init (RsvgState * state)
     state->font_weight = PANGO_WEIGHT_NORMAL;
     state->font_stretch = PANGO_STRETCH_NORMAL;
     state->text_dir = PANGO_DIRECTION_LTR;
+    state->text_gravity = PANGO_GRAVITY_SOUTH;
     state->unicode_bidi = UNICODE_BIDI_NORMAL;
     state->text_anchor = TEXT_ANCHOR_START;
     state->letter_spacing = _rsvg_css_parse_length ("0.0");
     state->visible = TRUE;
     state->cond_true = TRUE;
     state->filter = NULL;
-    state->clip_path_ref = NULL;
+    state->clip_path = NULL;
     state->startMarker = NULL;
     state->middleMarker = NULL;
     state->endMarker = NULL;
 
+    state->has_baseline_shift = FALSE;
     state->has_current_color = FALSE;
     state->has_flood_color = FALSE;
     state->has_flood_opacity = FALSE;
@@ -120,6 +187,7 @@ rsvg_state_init (RsvgState * state)
     state->has_font_stretch = FALSE;
     state->has_font_decor = FALSE;
     state->has_text_dir = FALSE;
+    state->has_text_gravity = FALSE;
     state->has_unicode_bidi = FALSE;
     state->has_text_anchor = FALSE;
     state->has_letter_spacing = FALSE;
@@ -128,10 +196,22 @@ rsvg_state_init (RsvgState * state)
     state->has_endMarker = FALSE;
     state->has_overflow = FALSE;
 
-	state->shape_rendering_type = SHAPE_RENDERING_AUTO;
-	state->has_shape_rendering_type = FALSE;
-	state->text_rendering_type = TEXT_RENDERING_AUTO;
-	state->has_text_rendering_type = FALSE;
+    state->shape_rendering_type = SHAPE_RENDERING_AUTO;
+    state->has_shape_rendering_type = FALSE;
+    state->text_rendering_type = TEXT_RENDERING_AUTO;
+    state->has_text_rendering_type = FALSE;
+
+    state->styles = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                           g_free, (GDestroyNotify) style_value_data_free);
+}
+
+void
+rsvg_state_reinit (RsvgState * state)
+{
+    RsvgState *parent = state->parent;
+    rsvg_state_finalize (state);
+    rsvg_state_init (state);
+    state->parent = parent;
 }
 
 typedef int (*InheritanceFunction) (int dst, int src);
@@ -140,15 +220,25 @@ void
 rsvg_state_clone (RsvgState * dst, const RsvgState * src)
 {
     gint i;
+    RsvgState *parent = dst->parent;
 
     rsvg_state_finalize (dst);
 
     *dst = *src;
+    dst->parent = parent;
+    dst->filter = g_strdup (src->filter);
+    dst->mask = g_strdup (src->mask);
+    dst->clip_path = g_strdup (src->clip_path);
     dst->font_family = g_strdup (src->font_family);
     dst->lang = g_strdup (src->lang);
+    dst->startMarker = g_strdup (src->startMarker);
+    dst->middleMarker = g_strdup (src->middleMarker);
+    dst->endMarker = g_strdup (src->endMarker);
     rsvg_paint_server_ref (dst->fill);
     rsvg_paint_server_ref (dst->stroke);
 
+    dst->styles = g_hash_table_ref (src->styles);
+
     if (src->dash.n_dash > 0) {
         dst->dash.dash = g_new (gdouble, src->dash.n_dash);
         for (i = 0; i < src->dash.n_dash; i++)
@@ -169,6 +259,8 @@ rsvg_state_inherit_run (RsvgState * dst, const RsvgState * src,
 {
     gint i;
 
+    if (function (dst->has_baseline_shift, src->has_baseline_shift))
+        dst->baseline_shift = src->baseline_shift;
     if (function (dst->has_current_color, src->has_current_color))
         dst->current_color = src->current_color;
     if (function (dst->has_flood_color, src->has_flood_color))
@@ -225,33 +317,41 @@ rsvg_state_inherit_run (RsvgState * dst, const RsvgState * src,
         dst->font_decor = src->font_decor;
     if (function (dst->has_text_dir, src->has_text_dir))
         dst->text_dir = src->text_dir;
+    if (function (dst->has_text_gravity, src->has_text_gravity))
+        dst->text_gravity = src->text_gravity;
     if (function (dst->has_unicode_bidi, src->has_unicode_bidi))
         dst->unicode_bidi = src->unicode_bidi;
     if (function (dst->has_text_anchor, src->has_text_anchor))
         dst->text_anchor = src->text_anchor;
     if (function (dst->has_letter_spacing, src->has_letter_spacing))
-	dst->letter_spacing = src->letter_spacing;
-    if (function (dst->has_startMarker, src->has_startMarker))
-        dst->startMarker = src->startMarker;
-    if (function (dst->has_middleMarker, src->has_middleMarker))
-        dst->middleMarker = src->middleMarker;
-    if (function (dst->has_endMarker, src->has_endMarker))
-        dst->endMarker = src->endMarker;
-	if (function (dst->has_shape_rendering_type, src->has_shape_rendering_type))
-		dst->shape_rendering_type = src->shape_rendering_type;
-	if (function (dst->has_text_rendering_type, src->has_text_rendering_type))
-		dst->text_rendering_type = src->text_rendering_type;
+        dst->letter_spacing = src->letter_spacing;
+    if (function (dst->has_startMarker, src->has_startMarker)) {
+        g_free (dst->startMarker);
+        dst->startMarker = g_strdup (src->startMarker);
+    }
+    if (function (dst->has_middleMarker, src->has_middleMarker)) {
+        g_free (dst->middleMarker);
+        dst->middleMarker = g_strdup (src->middleMarker);
+    }
+    if (function (dst->has_endMarker, src->has_endMarker)) {
+        g_free (dst->endMarker);
+        dst->endMarker = g_strdup (src->endMarker);
+    }
+    if (function (dst->has_shape_rendering_type, src->has_shape_rendering_type))
+            dst->shape_rendering_type = src->shape_rendering_type;
+    if (function (dst->has_text_rendering_type, src->has_text_rendering_type))
+            dst->text_rendering_type = src->text_rendering_type;
 
     if (function (dst->has_font_family, src->has_font_family)) {
         g_free (dst->font_family);      /* font_family is always set to something */
         dst->font_family = g_strdup (src->font_family);
     }
 
-	if (function (dst->has_space_preserve, src->has_space_preserve))
-		dst->space_preserve = src->space_preserve;
+    if (function (dst->has_space_preserve, src->has_space_preserve))
+        dst->space_preserve = src->space_preserve;
 
-	if (function (dst->has_visible, src->has_visible))
-		dst->visible = src->visible;
+    if (function (dst->has_visible, src->has_visible))
+        dst->visible = src->visible;
 
     if (function (dst->has_lang, src->has_lang)) {
         if (dst->has_lang)
@@ -274,12 +374,14 @@ rsvg_state_inherit_run (RsvgState * dst, const RsvgState * src,
     }
 
     if (inherituninheritables) {
-        dst->clip_path_ref = src->clip_path_ref;
-        dst->mask = src->mask;
+        g_free (dst->clip_path);
+        dst->clip_path = g_strdup (src->clip_path);
+        g_free (dst->mask);
+        dst->mask = g_strdup (src->mask);
+        g_free (dst->filter);
+        dst->filter = g_strdup (src->filter);
         dst->enable_background = src->enable_background;
-        dst->adobe_blend = src->adobe_blend;
         dst->opacity = src->opacity;
-        dst->filter = src->filter;
         dst->comp_op = src->comp_op;
     }
 }
@@ -363,350 +465,353 @@ rsvg_state_inherit (RsvgState * dst, const RsvgState * src)
 void
 rsvg_state_finalize (RsvgState * state)
 {
+    g_free (state->filter);
+    g_free (state->mask);
+    g_free (state->clip_path);
     g_free (state->font_family);
     g_free (state->lang);
+    g_free (state->startMarker);
+    g_free (state->middleMarker);
+    g_free (state->endMarker);
     rsvg_paint_server_unref (state->fill);
     rsvg_paint_server_unref (state->stroke);
 
     if (state->dash.n_dash != 0)
         g_free (state->dash.dash);
+
+    if (state->styles) {
+        g_hash_table_unref (state->styles);
+        state->styles = NULL;
+    }
 }
 
 /* Parse a CSS2 style argument, setting the SVG context attributes. */
 static void
-rsvg_parse_style_arg (RsvgHandle * ctx, RsvgState * state, const char *str)
+rsvg_parse_style_pair (RsvgHandle * ctx,
+                       RsvgState * state,
+                       const gchar * name,
+                       const gchar * value,
+                       gboolean important)
 {
-    int arg_off;
-
-    arg_off = rsvg_css_param_arg_offset (str);
-
-    if (rsvg_css_param_match (str, "color"))
-        state->current_color = rsvg_css_parse_color (str + arg_off, &state->has_current_color);
-    else if (rsvg_css_param_match (str, "opacity"))
-        state->opacity = rsvg_css_parse_opacity (str + arg_off);
-    else if (rsvg_css_param_match (str, "flood-color"))
-        state->flood_color = rsvg_css_parse_color (str + arg_off, &state->has_flood_color);
-    else if (rsvg_css_param_match (str, "flood-opacity")) {
-        state->flood_opacity = rsvg_css_parse_opacity (str + arg_off);
+    StyleValueData *data;
+
+    data = g_hash_table_lookup (state->styles, name);
+    if (data && data->important && !important)
+        return;
+
+    if (name == NULL || value == NULL)
+        return;
+
+    g_hash_table_insert (state->styles,
+                         (gpointer) g_strdup (name),
+                         (gpointer) style_value_data_new (value, important));
+
+    if (g_str_equal (name, "color"))
+        state->current_color = rsvg_css_parse_color (value, &state->has_current_color);
+    else if (g_str_equal (name, "opacity"))
+        state->opacity = rsvg_css_parse_opacity (value);
+    else if (g_str_equal (name, "flood-color"))
+        state->flood_color = rsvg_css_parse_color (value, &state->has_flood_color);
+    else if (g_str_equal (name, "flood-opacity")) {
+        state->flood_opacity = rsvg_css_parse_opacity (value);
         state->has_flood_opacity = TRUE;
-    } else if (rsvg_css_param_match (str, "filter"))
-        state->filter = rsvg_filter_parse (ctx->priv->defs, str + arg_off);
-    else if (rsvg_css_param_match (str, "a:adobe-blending-mode")) {
-        if (!strcmp (str + arg_off, "normal"))
-            state->adobe_blend = 0;
-        else if (!strcmp (str + arg_off, "multiply"))
-            state->adobe_blend = 1;
-        else if (!strcmp (str + arg_off, "screen"))
-            state->adobe_blend = 2;
-        else if (!strcmp (str + arg_off, "darken"))
-            state->adobe_blend = 3;
-        else if (!strcmp (str + arg_off, "lighten"))
-            state->adobe_blend = 4;
-        else if (!strcmp (str + arg_off, "softlight"))
-            state->adobe_blend = 5;
-        else if (!strcmp (str + arg_off, "hardlight"))
-            state->adobe_blend = 6;
-        else if (!strcmp (str + arg_off, "colordodge"))
-            state->adobe_blend = 7;
-        else if (!strcmp (str + arg_off, "colorburn"))
-            state->adobe_blend = 8;
-        else if (!strcmp (str + arg_off, "overlay"))
-            state->adobe_blend = 9;
-        else if (!strcmp (str + arg_off, "exclusion"))
-            state->adobe_blend = 10;
-        else if (!strcmp (str + arg_off, "difference"))
-            state->adobe_blend = 11;
-        else
-            state->adobe_blend = 0;
-    } else if (rsvg_css_param_match (str, "mask"))
-        state->mask = rsvg_mask_parse (ctx->priv->defs, str + arg_off);
-    else if (rsvg_css_param_match (str, "clip-path")) {
-        state->clip_path_ref = rsvg_clip_path_parse (ctx->priv->defs, str + arg_off);
-    } else if (rsvg_css_param_match (str, "overflow")) {
-        if (strcmp (str + arg_off, "inherit")) {
-            state->overflow = rsvg_css_parse_overflow (str + arg_off, &state->has_overflow);
+    } else if (g_str_equal (name, "filter")) {
+        g_free (state->filter);
+        state->filter = rsvg_get_url_string (value);
+    } else if (g_str_equal (name, "mask")) {
+        g_free (state->mask);
+        state->mask = rsvg_get_url_string (value);
+    } else if (g_str_equal (name, "baseline-shift")) {
+        /* These values come from Inkscape's SP_CSS_BASELINE_SHIFT_(SUB/SUPER/BASELINE);
+         * see sp_style_merge_baseline_shift_from_parent()
+         */
+        if (g_str_equal (value, "sub")) {
+           state->has_baseline_shift = TRUE;
+           state->baseline_shift = -0.2;
+        } else if (g_str_equal (value, "super")) {
+           state->has_baseline_shift = TRUE;
+           state->baseline_shift = 0.4;
+        } else if (g_str_equal (value, "baseline")) {
+           state->has_baseline_shift = TRUE;
+           state->baseline_shift = 0.;
+        } else {
+          g_warning ("value \'%s\' for attribute \'baseline-shift\' is not supported; only 'sub', 'super', and 'baseline' are supported\n", value);
+        }
+    } else if (g_str_equal (name, "clip-path")) {
+        g_free (state->clip_path);
+        state->clip_path = rsvg_get_url_string (value);
+    } else if (g_str_equal (name, "overflow")) {
+        if (!g_str_equal (value, "inherit")) {
+            state->overflow = rsvg_css_parse_overflow (value, &state->has_overflow);
         }
-    } else if (rsvg_css_param_match (str, "enable-background")) {
-        if (!strcmp (str + arg_off, "new"))
+    } else if (g_str_equal (name, "enable-background")) {
+        if (g_str_equal (value, "new"))
             state->enable_background = RSVG_ENABLE_BACKGROUND_NEW;
         else
             state->enable_background = RSVG_ENABLE_BACKGROUND_ACCUMULATE;
-    } else if (rsvg_css_param_match (str, "comp-op")) {
-        if (!strcmp (str + arg_off, "clear"))
-            state->comp_op = RSVG_COMP_OP_CLEAR;
-        else if (!strcmp (str + arg_off, "src"))
-            state->comp_op = RSVG_COMP_OP_SRC;
-        else if (!strcmp (str + arg_off, "dst"))
-            state->comp_op = RSVG_COMP_OP_DST;
-        else if (!strcmp (str + arg_off, "src-over"))
-            state->comp_op = RSVG_COMP_OP_SRC_OVER;
-        else if (!strcmp (str + arg_off, "dst-over"))
-            state->comp_op = RSVG_COMP_OP_DST_OVER;
-        else if (!strcmp (str + arg_off, "src-in"))
-            state->comp_op = RSVG_COMP_OP_SRC_IN;
-        else if (!strcmp (str + arg_off, "dst-in"))
-            state->comp_op = RSVG_COMP_OP_DST_IN;
-        else if (!strcmp (str + arg_off, "src-out"))
-            state->comp_op = RSVG_COMP_OP_SRC_OUT;
-        else if (!strcmp (str + arg_off, "dst-out"))
-            state->comp_op = RSVG_COMP_OP_DST_OUT;
-        else if (!strcmp (str + arg_off, "src-atop"))
-            state->comp_op = RSVG_COMP_OP_SRC_ATOP;
-        else if (!strcmp (str + arg_off, "dst-atop"))
-            state->comp_op = RSVG_COMP_OP_DST_ATOP;
-        else if (!strcmp (str + arg_off, "xor"))
-            state->comp_op = RSVG_COMP_OP_XOR;
-        else if (!strcmp (str + arg_off, "plus"))
-            state->comp_op = RSVG_COMP_OP_PLUS;
-        else if (!strcmp (str + arg_off, "multiply"))
-            state->comp_op = RSVG_COMP_OP_MULTIPLY;
-        else if (!strcmp (str + arg_off, "screen"))
-            state->comp_op = RSVG_COMP_OP_SCREEN;
-        else if (!strcmp (str + arg_off, "overlay"))
-            state->comp_op = RSVG_COMP_OP_OVERLAY;
-        else if (!strcmp (str + arg_off, "darken"))
-            state->comp_op = RSVG_COMP_OP_DARKEN;
-        else if (!strcmp (str + arg_off, "lighten"))
-            state->comp_op = RSVG_COMP_OP_LIGHTEN;
-        else if (!strcmp (str + arg_off, "color-dodge"))
-            state->comp_op = RSVG_COMP_OP_COLOR_DODGE;
-        else if (!strcmp (str + arg_off, "color-burn"))
-            state->comp_op = RSVG_COMP_OP_COLOR_BURN;
-        else if (!strcmp (str + arg_off, "hard-light"))
-            state->comp_op = RSVG_COMP_OP_HARD_LIGHT;
-        else if (!strcmp (str + arg_off, "soft-light"))
-            state->comp_op = RSVG_COMP_OP_SOFT_LIGHT;
-        else if (!strcmp (str + arg_off, "difference"))
-            state->comp_op = RSVG_COMP_OP_DIFFERENCE;
-        else if (!strcmp (str + arg_off, "exclusion"))
-            state->comp_op = RSVG_COMP_OP_EXCLUSION;
+    } else if (g_str_equal (name, "comp-op")) {
+        if (g_str_equal (value, "clear"))
+            state->comp_op = CAIRO_OPERATOR_CLEAR;
+        else if (g_str_equal (value, "src"))
+            state->comp_op = CAIRO_OPERATOR_SOURCE;
+        else if (g_str_equal (value, "dst"))
+            state->comp_op = CAIRO_OPERATOR_DEST;
+        else if (g_str_equal (value, "src-over"))
+            state->comp_op = CAIRO_OPERATOR_OVER;
+        else if (g_str_equal (value, "dst-over"))
+            state->comp_op = CAIRO_OPERATOR_DEST_OVER;
+        else if (g_str_equal (value, "src-in"))
+            state->comp_op = CAIRO_OPERATOR_IN;
+        else if (g_str_equal (value, "dst-in"))
+            state->comp_op = CAIRO_OPERATOR_DEST_IN;
+        else if (g_str_equal (value, "src-out"))
+            state->comp_op = CAIRO_OPERATOR_OUT;
+        else if (g_str_equal (value, "dst-out"))
+            state->comp_op = CAIRO_OPERATOR_DEST_OUT;
+        else if (g_str_equal (value, "src-atop"))
+            state->comp_op = CAIRO_OPERATOR_ATOP;
+        else if (g_str_equal (value, "dst-atop"))
+            state->comp_op = CAIRO_OPERATOR_DEST_ATOP;
+        else if (g_str_equal (value, "xor"))
+            state->comp_op = CAIRO_OPERATOR_XOR;
+        else if (g_str_equal (value, "plus"))
+            state->comp_op = CAIRO_OPERATOR_ADD;
         else
-            state->comp_op = RSVG_COMP_OP_SRC_OVER;
-    } else if (rsvg_css_param_match (str, "display")) {
+            state->comp_op = CAIRO_OPERATOR_OVER;
+    } else if (g_str_equal (name, "display")) {
         state->has_visible = TRUE;
-        if (!strcmp (str + arg_off, "none"))
+        if (g_str_equal (value, "none"))
             state->visible = FALSE;
-        else if (strcmp (str + arg_off, "inherit") != 0)
+        else if (!g_str_equal (value, "inherit") != 0)
             state->visible = TRUE;
         else
             state->has_visible = FALSE;
-	} else if (rsvg_css_param_match (str, "xml:space")) {
+	} else if (g_str_equal (name, "xml:space")) {
         state->has_space_preserve = TRUE;
-        if (!strcmp (str + arg_off, "default"))
+        if (g_str_equal (value, "default"))
             state->space_preserve = FALSE;
-        else if (strcmp (str + arg_off, "preserve") == 0)
+        else if (!g_str_equal (value, "preserve") == 0)
             state->space_preserve = TRUE;
         else
             state->space_preserve = FALSE;
-    } else if (rsvg_css_param_match (str, "visibility")) {
+    } else if (g_str_equal (name, "visibility")) {
         state->has_visible = TRUE;
-        if (!strcmp (str + arg_off, "visible"))
+        if (g_str_equal (value, "visible"))
             state->visible = TRUE;
-        else if (strcmp (str + arg_off, "inherit") != 0)
+        else if (!g_str_equal (value, "inherit") != 0)
             state->visible = FALSE;     /* collapse or hidden */
         else
             state->has_visible = FALSE;
-    } else if (rsvg_css_param_match (str, "fill")) {
+    } else if (g_str_equal (name, "fill")) {
         RsvgPaintServer *fill = state->fill;
         state->fill =
-            rsvg_paint_server_parse (&state->has_fill_server, ctx->priv->defs, str + arg_off, 0);
+            rsvg_paint_server_parse (&state->has_fill_server, value);
         rsvg_paint_server_unref (fill);
-    } else if (rsvg_css_param_match (str, "fill-opacity")) {
-        state->fill_opacity = rsvg_css_parse_opacity (str + arg_off);
+    } else if (g_str_equal (name, "fill-opacity")) {
+        state->fill_opacity = rsvg_css_parse_opacity (value);
         state->has_fill_opacity = TRUE;
-    } else if (rsvg_css_param_match (str, "fill-rule")) {
+    } else if (g_str_equal (name, "fill-rule")) {
         state->has_fill_rule = TRUE;
-        if (!strcmp (str + arg_off, "nonzero"))
-            state->fill_rule = FILL_RULE_NONZERO;
-        else if (!strcmp (str + arg_off, "evenodd"))
-            state->fill_rule = FILL_RULE_EVENODD;
+        if (g_str_equal (value, "nonzero"))
+            state->fill_rule = CAIRO_FILL_RULE_WINDING;
+        else if (g_str_equal (value, "evenodd"))
+            state->fill_rule = CAIRO_FILL_RULE_EVEN_ODD;
         else
             state->has_fill_rule = FALSE;
-    } else if (rsvg_css_param_match (str, "clip-rule")) {
+    } else if (g_str_equal (name, "clip-rule")) {
         state->has_clip_rule = TRUE;
-        if (!strcmp (str + arg_off, "nonzero"))
-            state->clip_rule = FILL_RULE_NONZERO;
-        else if (!strcmp (str + arg_off, "evenodd"))
-            state->clip_rule = FILL_RULE_EVENODD;
+        if (g_str_equal (value, "nonzero"))
+            state->clip_rule = CAIRO_FILL_RULE_WINDING;
+        else if (g_str_equal (value, "evenodd"))
+            state->clip_rule = CAIRO_FILL_RULE_EVEN_ODD;
         else
             state->has_clip_rule = FALSE;
-    } else if (rsvg_css_param_match (str, "stroke")) {
+    } else if (g_str_equal (name, "stroke")) {
         RsvgPaintServer *stroke = state->stroke;
 
         state->stroke =
-            rsvg_paint_server_parse (&state->has_stroke_server, ctx->priv->defs, str + arg_off, 0);
+            rsvg_paint_server_parse (&state->has_stroke_server, value);
 
         rsvg_paint_server_unref (stroke);
-    } else if (rsvg_css_param_match (str, "stroke-width")) {
-        state->stroke_width = _rsvg_css_parse_length (str + arg_off);
+    } else if (g_str_equal (name, "stroke-width")) {
+        state->stroke_width = _rsvg_css_parse_length (value);
         state->has_stroke_width = TRUE;
-    } else if (rsvg_css_param_match (str, "stroke-linecap")) {
+    } else if (g_str_equal (name, "stroke-linecap")) {
         state->has_cap = TRUE;
-        if (!strcmp (str + arg_off, "butt"))
-            state->cap = RSVG_PATH_STROKE_CAP_BUTT;
-        else if (!strcmp (str + arg_off, "round"))
-            state->cap = RSVG_PATH_STROKE_CAP_ROUND;
-        else if (!strcmp (str + arg_off, "square"))
-            state->cap = RSVG_PATH_STROKE_CAP_SQUARE;
+        if (g_str_equal (value, "butt"))
+            state->cap = CAIRO_LINE_CAP_BUTT;
+        else if (g_str_equal (value, "round"))
+            state->cap = CAIRO_LINE_CAP_ROUND;
+        else if (g_str_equal (value, "square"))
+            state->cap = CAIRO_LINE_CAP_SQUARE;
         else
-            g_warning (_("unknown line cap style %s\n"), str + arg_off);
-    } else if (rsvg_css_param_match (str, "stroke-opacity")) {
-        state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off);
+            g_warning (_("unknown line cap style %s\n"), value);
+    } else if (g_str_equal (name, "stroke-opacity")) {
+        state->stroke_opacity = rsvg_css_parse_opacity (value);
         state->has_stroke_opacity = TRUE;
-    } else if (rsvg_css_param_match (str, "stroke-linejoin")) {
+    } else if (g_str_equal (name, "stroke-linejoin")) {
         state->has_join = TRUE;
-        if (!strcmp (str + arg_off, "miter"))
-            state->join = RSVG_PATH_STROKE_JOIN_MITER;
-        else if (!strcmp (str + arg_off, "round"))
-            state->join = RSVG_PATH_STROKE_JOIN_ROUND;
-        else if (!strcmp (str + arg_off, "bevel"))
-            state->join = RSVG_PATH_STROKE_JOIN_BEVEL;
+        if (g_str_equal (value, "miter"))
+            state->join = CAIRO_LINE_JOIN_MITER;
+        else if (g_str_equal (value, "round"))
+            state->join = CAIRO_LINE_JOIN_ROUND;
+        else if (g_str_equal (value, "bevel"))
+            state->join = CAIRO_LINE_JOIN_BEVEL;
         else
-            g_warning (_("unknown line join style %s\n"), str + arg_off);
-    } else if (rsvg_css_param_match (str, "font-size")) {
-        state->font_size = _rsvg_css_parse_length (str + arg_off);
+            g_warning (_("unknown line join style %s\n"), value);
+    } else if (g_str_equal (name, "font-size")) {
+        state->font_size = _rsvg_css_parse_length (value);
         state->has_font_size = TRUE;
-    } else if (rsvg_css_param_match (str, "font-family")) {
-        char *save = g_strdup (rsvg_css_parse_font_family (str + arg_off, &state->has_font_family));
+    } else if (g_str_equal (name, "font-family")) {
+        char *save = g_strdup (rsvg_css_parse_font_family (value, &state->has_font_family));
         g_free (state->font_family);
         state->font_family = save;
-    } else if (rsvg_css_param_match (str, "xml:lang")) {
-        char *save = g_strdup (str + arg_off);
+    } else if (g_str_equal (name, "xml:lang")) {
+        char *save = g_strdup (value);
         g_free (state->lang);
         state->lang = save;
         state->has_lang = TRUE;
-    } else if (rsvg_css_param_match (str, "font-style")) {
-        state->font_style = rsvg_css_parse_font_style (str + arg_off, &state->has_font_style);
-    } else if (rsvg_css_param_match (str, "font-variant")) {
-        state->font_variant = rsvg_css_parse_font_variant (str + arg_off, &state->has_font_variant);
-    } else if (rsvg_css_param_match (str, "font-weight")) {
-        state->font_weight = rsvg_css_parse_font_weight (str + arg_off, &state->has_font_weight);
-    } else if (rsvg_css_param_match (str, "font-stretch")) {
-        state->font_stretch = rsvg_css_parse_font_stretch (str + arg_off, &state->has_font_stretch);
-    } else if (rsvg_css_param_match (str, "text-decoration")) {
-        if (!strcmp (str + arg_off, "inherit")) {
+    } else if (g_str_equal (name, "font-style")) {
+        state->font_style = rsvg_css_parse_font_style (value, &state->has_font_style);
+    } else if (g_str_equal (name, "font-variant")) {
+        state->font_variant = rsvg_css_parse_font_variant (value, &state->has_font_variant);
+    } else if (g_str_equal (name, "font-weight")) {
+        state->font_weight = rsvg_css_parse_font_weight (value, &state->has_font_weight);
+    } else if (g_str_equal (name, "font-stretch")) {
+        state->font_stretch = rsvg_css_parse_font_stretch (value, &state->has_font_stretch);
+    } else if (g_str_equal (name, "text-decoration")) {
+        if (g_str_equal (value, "inherit")) {
             state->has_font_decor = FALSE;
             state->font_decor = TEXT_NORMAL;
         } else {
-            if (strstr (str + arg_off, "underline"))
+            if (strstr (value, "underline"))
                 state->font_decor |= TEXT_UNDERLINE;
-            if (strstr (str + arg_off, "overline"))
+            if (strstr (value, "overline"))
                 state->font_decor |= TEXT_OVERLINE;
-            if (strstr (str + arg_off, "strike") || strstr (str + arg_off, "line-through"))     /* strike though or line-through */
+            if (strstr (value, "strike") || strstr (value, "line-through"))     /* strike though or line-through */
                 state->font_decor |= TEXT_STRIKE;
             state->has_font_decor = TRUE;
         }
-    } else if (rsvg_css_param_match (str, "direction")) {
+    } else if (g_str_equal (name, "direction")) {
         state->has_text_dir = TRUE;
-        if (!strcmp (str + arg_off, "inherit")) {
+        if (g_str_equal (value, "inherit")) {
             state->text_dir = PANGO_DIRECTION_LTR;
             state->has_text_dir = FALSE;
-        } else if (!strcmp (str + arg_off, "rtl"))
+        } else if (g_str_equal (value, "rtl"))
             state->text_dir = PANGO_DIRECTION_RTL;
         else                    /* ltr */
             state->text_dir = PANGO_DIRECTION_LTR;
-    } else if (rsvg_css_param_match (str, "unicode-bidi")) {
+    } else if (g_str_equal (name, "unicode-bidi")) {
         state->has_unicode_bidi = TRUE;
-        if (!strcmp (str + arg_off, "inherit")) {
-            state->unicode_bidi = PANGO_DIRECTION_LTR;
+        if (g_str_equal (value, "inherit")) {
+            state->unicode_bidi = UNICODE_BIDI_NORMAL;
             state->has_unicode_bidi = FALSE;
-        } else if (!strcmp (str + arg_off, "embed"))
+        } else if (g_str_equal (value, "embed"))
             state->unicode_bidi = UNICODE_BIDI_EMBED;
-        else if (!strcmp (str + arg_off, "bidi-override"))
+        else if (g_str_equal (value, "bidi-override"))
             state->unicode_bidi = UNICODE_BIDI_OVERRIDE;
         else                    /* normal */
             state->unicode_bidi = UNICODE_BIDI_NORMAL;
-    } else if (rsvg_css_param_match (str, "writing-mode")) {
+    } else if (g_str_equal (name, "writing-mode")) {
         /* TODO: these aren't quite right... */
 
         state->has_text_dir = TRUE;
-        if (!strcmp (str + arg_off, "inherit")) {
+        state->has_text_gravity = TRUE;
+        if (g_str_equal (value, "inherit")) {
             state->text_dir = PANGO_DIRECTION_LTR;
             state->has_text_dir = FALSE;
-        } else if (!strcmp (str + arg_off, "lr-tb") || !strcmp (str + arg_off, "tb"))
-            state->text_dir = PANGO_DIRECTION_TTB_LTR;
-        else if (!strcmp (str + arg_off, "rl"))
+            state->text_gravity = PANGO_GRAVITY_SOUTH;
+            state->has_text_gravity = FALSE;
+        } else if (g_str_equal (value, "lr-tb") || g_str_equal (value, "lr")) {
+            state->text_dir = PANGO_DIRECTION_LTR;
+            state->text_gravity = PANGO_GRAVITY_SOUTH;
+        } else if (g_str_equal (value, "rl-tb") || g_str_equal (value, "rl")) {
             state->text_dir = PANGO_DIRECTION_RTL;
-        else if (!strcmp (str + arg_off, "tb-rl") || !strcmp (str + arg_off, "rl-tb"))
-            state->text_dir = PANGO_DIRECTION_TTB_RTL;
-        else
+            state->text_gravity = PANGO_GRAVITY_SOUTH;
+        } else if (g_str_equal (value, "tb-rl") || g_str_equal (value, "tb")) {
             state->text_dir = PANGO_DIRECTION_LTR;
-    } else if (rsvg_css_param_match (str, "text-anchor")) {
+            state->text_gravity = PANGO_GRAVITY_EAST;
+        }
+    } else if (g_str_equal (name, "text-anchor")) {
         state->has_text_anchor = TRUE;
-        if (!strcmp (str + arg_off, "inherit")) {
+        if (g_str_equal (value, "inherit")) {
             state->text_anchor = TEXT_ANCHOR_START;
             state->has_text_anchor = FALSE;
         } else {
-            if (strstr (str + arg_off, "start"))
+            if (strstr (value, "start"))
                 state->text_anchor = TEXT_ANCHOR_START;
-            else if (strstr (str + arg_off, "middle"))
+            else if (strstr (value, "middle"))
                 state->text_anchor = TEXT_ANCHOR_MIDDLE;
-            else if (strstr (str + arg_off, "end"))
+            else if (strstr (value, "end"))
                 state->text_anchor = TEXT_ANCHOR_END;
         }
-    } else if (rsvg_css_param_match (str, "letter-spacing")) {
+    } else if (g_str_equal (name, "letter-spacing")) {
 	state->has_letter_spacing = TRUE;
-	state->letter_spacing = _rsvg_css_parse_length (str + arg_off);
-    } else if (rsvg_css_param_match (str, "stop-color")) {
-        if (strcmp (str + arg_off, "inherit")) {
-            state->stop_color = rsvg_css_parse_color (str + arg_off, &state->has_stop_color);
+	state->letter_spacing = _rsvg_css_parse_length (value);
+    } else if (g_str_equal (name, "stop-color")) {
+        if (!g_str_equal (value, "inherit")) {
+            state->stop_color = rsvg_css_parse_color (value, &state->has_stop_color);
         }
-    } else if (rsvg_css_param_match (str, "stop-opacity")) {
-        if (strcmp (str + arg_off, "inherit")) {
+    } else if (g_str_equal (name, "stop-opacity")) {
+        if (!g_str_equal (value, "inherit")) {
             state->has_stop_opacity = TRUE;
-            state->stop_opacity = rsvg_css_parse_opacity (str + arg_off);
+            state->stop_opacity = rsvg_css_parse_opacity (value);
         }
-    } else if (rsvg_css_param_match (str, "marker-start")) {
-        state->startMarker = rsvg_marker_parse (ctx->priv->defs, str + arg_off);
+    } else if (g_str_equal (name, "marker-start")) {
+        g_free (state->startMarker);
+        state->startMarker = rsvg_get_url_string (value);
         state->has_startMarker = TRUE;
-    } else if (rsvg_css_param_match (str, "marker-mid")) {
-        state->middleMarker = rsvg_marker_parse (ctx->priv->defs, str + arg_off);
+    } else if (g_str_equal (name, "marker-mid")) {
+        g_free (state->middleMarker);
+        state->middleMarker = rsvg_get_url_string (value);
         state->has_middleMarker = TRUE;
-    } else if (rsvg_css_param_match (str, "marker-end")) {
-        state->endMarker = rsvg_marker_parse (ctx->priv->defs, str + arg_off);
+    } else if (g_str_equal (name, "marker-end")) {
+        g_free (state->endMarker);
+        state->endMarker = rsvg_get_url_string (value);
         state->has_endMarker = TRUE;
-    } else if (rsvg_css_param_match (str, "stroke-miterlimit")) {
+    } else if (g_str_equal (name, "stroke-miterlimit")) {
         state->has_miter_limit = TRUE;
-        state->miter_limit = g_ascii_strtod (str + arg_off, NULL);
-    } else if (rsvg_css_param_match (str, "stroke-dashoffset")) {
+        state->miter_limit = g_ascii_strtod (value, NULL);
+    } else if (g_str_equal (name, "stroke-dashoffset")) {
         state->has_dashoffset = TRUE;
-        state->dash.offset = _rsvg_css_parse_length (str + arg_off);
+        state->dash.offset = _rsvg_css_parse_length (value);
         if (state->dash.offset.length < 0.)
             state->dash.offset.length = 0.;
-	} else if (rsvg_css_param_match (str, "shape-rendering")) {
-		state->has_shape_rendering_type = TRUE;
-
-        if (!strcmp (str + arg_off, "auto") || !strcmp (str + arg_off, "default"))
-			state->shape_rendering_type = SHAPE_RENDERING_AUTO;
-        else if (!strcmp (str + arg_off, "optimizeSpeed"))
-			state->shape_rendering_type = SHAPE_RENDERING_OPTIMIZE_SPEED;
-        else if (!strcmp (str + arg_off, "crispEdges"))
-			state->shape_rendering_type = SHAPE_RENDERING_CRISP_EDGES;
-        else if (!strcmp (str + arg_off, "geometricPrecision"))
-			state->shape_rendering_type = SHAPE_RENDERING_GEOMETRIC_PRECISION;
-
-	} else if (rsvg_css_param_match (str, "text-rendering")) {
-		state->has_text_rendering_type = TRUE;
-
-        if (!strcmp (str + arg_off, "auto") || !strcmp (str + arg_off, "default"))
-			state->text_rendering_type = TEXT_RENDERING_AUTO;
-        else if (!strcmp (str + arg_off, "optimizeSpeed"))
-			state->text_rendering_type = TEXT_RENDERING_OPTIMIZE_SPEED;
-        else if (!strcmp (str + arg_off, "optimizeLegibility"))
-			state->text_rendering_type = TEXT_RENDERING_OPTIMIZE_LEGIBILITY;
-        else if (!strcmp (str + arg_off, "geometricPrecision"))
-			state->text_rendering_type = TEXT_RENDERING_GEOMETRIC_PRECISION;
-
-    } else if (rsvg_css_param_match (str, "stroke-dasharray")) {
+    } else if (g_str_equal (name, "shape-rendering")) {
+        state->has_shape_rendering_type = TRUE;
+
+        if (g_str_equal (value, "auto") || g_str_equal (value, "default"))
+            state->shape_rendering_type = SHAPE_RENDERING_AUTO;
+        else if (g_str_equal (value, "optimizeSpeed"))
+            state->shape_rendering_type = SHAPE_RENDERING_OPTIMIZE_SPEED;
+        else if (g_str_equal (value, "crispEdges"))
+            state->shape_rendering_type = SHAPE_RENDERING_CRISP_EDGES;
+        else if (g_str_equal (value, "geometricPrecision"))
+            state->shape_rendering_type = SHAPE_RENDERING_GEOMETRIC_PRECISION;
+
+    } else if (g_str_equal (name, "text-rendering")) {
+        state->has_text_rendering_type = TRUE;
+
+        if (g_str_equal (value, "auto") || g_str_equal (value, "default"))
+            state->text_rendering_type = TEXT_RENDERING_AUTO;
+        else if (g_str_equal (value, "optimizeSpeed"))
+            state->text_rendering_type = TEXT_RENDERING_OPTIMIZE_SPEED;
+        else if (g_str_equal (value, "optimizeLegibility"))
+            state->text_rendering_type = TEXT_RENDERING_OPTIMIZE_LEGIBILITY;
+        else if (g_str_equal (value, "geometricPrecision"))
+            state->text_rendering_type = TEXT_RENDERING_GEOMETRIC_PRECISION;
+
+    } else if (g_str_equal (name, "stroke-dasharray")) {
         state->has_dash = TRUE;
-        if (!strcmp (str + arg_off, "none")) {
+        if (g_str_equal (value, "none")) {
             if (state->dash.n_dash != 0) {
                 /* free any cloned dash data */
                 g_free (state->dash.dash);
+                state->dash.dash = NULL;
                 state->dash.n_dash = 0;
             }
         } else {
-            gchar **dashes = g_strsplit (str + arg_off, ",", -1);
+            gchar **dashes = g_strsplit (value, ",", -1);
             if (NULL != dashes) {
                 gint n_dashes, i;
                 gboolean is_even = FALSE;
@@ -736,6 +841,7 @@ rsvg_parse_style_arg (RsvgHandle * ctx, RsvgState * state, const char *str)
                    be ignored */
                 if (total == 0) {
                     g_free (state->dash.dash);
+                    state->dash.dash = NULL;
                     state->dash.n_dash = 0;
                 }
             }
@@ -743,14 +849,6 @@ rsvg_parse_style_arg (RsvgHandle * ctx, RsvgState * state, const char *str)
     }
 }
 
-void
-rsvg_parse_style_pair (RsvgHandle * ctx, RsvgState * state, const char *key, const char *val)
-{
-    gchar *str = g_strdup_printf ("%s:%s", key, val);
-    rsvg_parse_style_arg (ctx, state, str);
-    g_free (str);
-}
-
 static void
 rsvg_lookup_parse_style_pair (RsvgHandle * ctx, RsvgState * state,
                               const char *key, RsvgPropertyBag * atts)
@@ -758,14 +856,14 @@ rsvg_lookup_parse_style_pair (RsvgHandle * ctx, RsvgState * state,
     const char *value;
 
     if ((value = rsvg_property_bag_lookup (atts, key)) != NULL)
-        rsvg_parse_style_pair (ctx, state, key, value);
+        rsvg_parse_style_pair (ctx, state, key, value, FALSE);
 }
 
 /* take a pair of the form (fill="#ff00ff") and parse it as a style */
 void
 rsvg_parse_style_pairs (RsvgHandle * ctx, RsvgState * state, RsvgPropertyBag * atts)
 {
-    rsvg_lookup_parse_style_pair (ctx, state, "a:adobe-blending-mode", atts);
+    rsvg_lookup_parse_style_pair (ctx, state, "baseline-shift", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "clip-path", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "clip-rule", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "color", atts);
@@ -791,6 +889,7 @@ rsvg_parse_style_pairs (RsvgHandle * ctx, RsvgState * state, RsvgPropertyBag * a
     rsvg_lookup_parse_style_pair (ctx, state, "marker-start", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "opacity", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "overflow", atts);
+    rsvg_lookup_parse_style_pair (ctx, state, "shape-rendering", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "stop-color", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "stop-opacity", atts);
     rsvg_lookup_parse_style_pair (ctx, state, "stroke", atts);
@@ -823,6 +922,32 @@ rsvg_parse_style_pairs (RsvgHandle * ctx, RsvgState * state, RsvgPropertyBag * a
     }
 }
 
+static gboolean
+parse_style_value (const gchar *string, gchar **value, gboolean *important)
+{
+    gchar **strings;
+
+    strings = g_strsplit (string, "!", 2);
+
+    if (strings == NULL || strings[0] == NULL) {
+        g_strfreev (strings);
+        return FALSE;
+    }
+
+    if (strings[1] != NULL && strings[2] == NULL &&
+        g_str_equal (g_strstrip (strings[1]), "important")) {
+        *important = TRUE;
+    } else {
+        *important = FALSE;
+    }
+
+    *value = g_strdup (g_strstrip (strings[0]));
+
+    g_strfreev (strings);
+
+    return TRUE;
+}
+
 /* Split a CSS2 style into individual style arguments, setting attributes
    in the SVG context.
    
@@ -832,56 +957,87 @@ rsvg_parse_style_pairs (RsvgHandle * ctx, RsvgState * state, RsvgPropertyBag * a
 void
 rsvg_parse_style (RsvgHandle * ctx, RsvgState * state, const char *str)
 {
-    int start, end;
-    char *arg;
-
-    start = 0;
-    while (str[start] != '\0') {
-        for (end = start; str[end] != '\0' && str[end] != ';'; end++);
-        arg = g_new (char, 1 + end - start);
-        memcpy (arg, str + start, end - start);
-        arg[end - start] = '\0';
-        rsvg_parse_style_arg (ctx, state, arg);
-        g_free (arg);
-        start = end;
-        if (str[start] == ';')
-            start++;
-        while (str[start] == ' ')
-            start++;
+    gchar **styles;
+    guint i;
+
+    styles = g_strsplit (str, ";", -1);
+    for (i = 0; i < g_strv_length (styles); i++) {
+        gchar **values;
+        values = g_strsplit (styles[i], ":", 2);
+        if (!values)
+            continue;
+
+        if (g_strv_length (values) == 2) {
+            gboolean important;
+            gchar *style_value = NULL;
+            gchar *first_value = values[0];
+            gchar *second_value = values[1];
+            gchar **split_list;
+
+            /* Just remove single quotes in a trivial way.  No handling for any
+             * special character inside the quotes is done.  This relates
+             * especially to font-family names but cases with special characters
+             * are rare.
+             *
+             * We need a real CSS parser, sigh.
+             */
+            split_list = g_strsplit (second_value, "'", -1);
+            second_value = g_strjoinv(NULL, split_list);
+            g_strfreev(split_list);
+
+            if (parse_style_value (second_value, &style_value, &important))
+                rsvg_parse_style_pair (ctx, state,
+                                       g_strstrip (first_value),
+                                       style_value,
+                                       important);
+            g_free (style_value);
+            g_free (second_value);
+        }
+        g_strfreev (values);
     }
+    g_strfreev (styles);
 }
 
 static void
-rsvg_css_define_style (RsvgHandle * ctx, const gchar * style_name, const char *style_def)
+rsvg_css_define_style (RsvgHandle * ctx,
+                       const gchar * selector,
+                       const gchar * style_name,
+                       const gchar * style_value,
+                       gboolean important)
 {
-    GString *str = g_string_new (style_def);
-    char *existing = NULL;
+    GHashTable *styles;
+    gboolean need_insert = FALSE;
 
     /* push name/style pair into HT */
-    existing = (char *) g_hash_table_lookup (ctx->priv->css_props, style_name);
-    if (existing != NULL)
-        g_string_append_len (str, existing, strlen (existing));
-
-    /* will destroy the existing key and value for us */
-    g_hash_table_insert (ctx->priv->css_props, (gpointer) g_strdup ((gchar *) style_name),
-                         (gpointer) str->str);
-    g_string_free (str, FALSE);
+    styles = g_hash_table_lookup (ctx->priv->css_props, selector);
+    if (styles == NULL) {
+        styles = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                        g_free, (GDestroyNotify) style_value_data_free);
+        g_hash_table_insert (ctx->priv->css_props, (gpointer) g_strdup (selector), styles);
+        need_insert = TRUE;
+    } else {
+        StyleValueData *current_value;
+        current_value = g_hash_table_lookup (styles, style_name);
+        if (current_value == NULL || !current_value->important)
+            need_insert = TRUE;
+    }
+    if (need_insert) {
+        g_hash_table_insert (styles,
+                             (gpointer) g_strdup (style_name),
+                             (gpointer) style_value_data_new (style_value, important));
+    }
 }
 
-#ifdef HAVE_LIBCROCO
-
-#include <libcroco/libcroco.h>
-
 typedef struct _CSSUserData {
     RsvgHandle *ctx;
-    GString *def;
+    CRSelector *selector;
 } CSSUserData;
 
 static void
 css_user_data_init (CSSUserData * user_data, RsvgHandle * ctx)
 {
     user_data->ctx = ctx;
-    user_data->def = NULL;
+    user_data->selector = NULL;
 }
 
 static void
@@ -892,54 +1048,56 @@ ccss_start_selector (CRDocHandler * a_handler, CRSelector * a_selector_list)
     g_return_if_fail (a_handler);
 
     user_data = (CSSUserData *) a_handler->app_data;
-    user_data->def = g_string_new (NULL);
+    cr_selector_ref (a_selector_list);
+    user_data->selector = a_selector_list;
 }
 
 static void
 ccss_end_selector (CRDocHandler * a_handler, CRSelector * a_selector_list)
 {
     CSSUserData *user_data;
-    CRSelector *cur;
 
     g_return_if_fail (a_handler);
 
     user_data = (CSSUserData *) a_handler->app_data;
 
-    if (a_selector_list)
-        for (cur = a_selector_list; cur; cur = cur->next) {
-            if (cur->simple_sel) {
-                gchar *style_name = (gchar *) cr_simple_sel_to_string (cur->simple_sel);
-                if (style_name) {
-                    rsvg_css_define_style (user_data->ctx, style_name, user_data->def->str);
-                    g_free (style_name);
-                }
-            }
-        }
-
-    g_string_free (user_data->def, TRUE);
+    cr_selector_unref (user_data->selector);
+    user_data->selector = NULL;
 }
 
 static void
 ccss_property (CRDocHandler * a_handler, CRString * a_name, CRTerm * a_expr, gboolean a_important)
 {
     CSSUserData *user_data;
-    gchar *expr = NULL, *name = NULL;
+    gchar *name = NULL;
     size_t len = 0;
 
     g_return_if_fail (a_handler);
 
     user_data = (CSSUserData *) a_handler->app_data;
 
-    if (a_name && a_expr && user_data->def) {
-        name = (gchar *) cr_string_peek_raw_str (a_name);
-        len = cr_string_peek_raw_str_len (a_name);
-
-        g_string_append_len (user_data->def, (gchar *) name, len);
-        g_string_append (user_data->def, ": ");
-        expr = (gchar *) cr_term_to_string (a_expr);
-        g_string_append_len (user_data->def, expr, strlen (expr));
-        g_free (expr);
-        g_string_append (user_data->def, "; ");
+    if (a_name && a_expr && user_data->selector) {
+        CRSelector *cur;
+        for (cur = user_data->selector; cur; cur = cur->next) {
+            if (cur->simple_sel) {
+                gchar *selector = (gchar *) cr_simple_sel_to_string (cur->simple_sel);
+                if (selector) {
+                    gchar *style_name, *style_value;
+                    name = (gchar *) cr_string_peek_raw_str (a_name);
+                    len = cr_string_peek_raw_str_len (a_name);
+                    style_name = g_strndup (name, len);
+                    style_value = (gchar *)cr_term_to_string (a_expr);
+                    rsvg_css_define_style (user_data->ctx,
+                                           selector,
+                                           style_name,
+                                           style_value,
+                                           a_important);
+                    g_free (selector);
+                    g_free (style_name);
+                    g_free (style_value);
+                }
+            }
+        }
     }
 }
 
@@ -986,14 +1144,16 @@ init_sac_handler (CRDocHandler * a_handler)
     a_handler->unrecoverable_error = ccss_unrecoverable_error;
 }
 
-static void
-rsvg_real_parse_cssbuffer (RsvgHandle * ctx, const char *buff, size_t buflen)
+void
+rsvg_parse_cssbuffer (RsvgHandle * ctx, const char *buff, size_t buflen)
 {
-    enum CRStatus status = CR_OK;
     CRParser *parser = NULL;
     CRDocHandler *css_handler = NULL;
     CSSUserData user_data;
 
+    if (buff == NULL || buflen == 0)
+        return;
+
     css_handler = cr_doc_handler_new ();
     init_sac_handler (css_handler);
 
@@ -1002,16 +1162,16 @@ rsvg_real_parse_cssbuffer (RsvgHandle * ctx, const char *buff, size_t buflen)
 
     /* TODO: fix libcroco to take in const strings */
     parser = cr_parser_new_from_buf ((guchar *) buff, (gulong) buflen, CR_UTF_8, FALSE);
-    status = cr_parser_set_sac_handler (parser, css_handler);
-
-    if (status != CR_OK) {
-        g_warning (_("Error setting CSS SAC handler\n"));
-        cr_parser_destroy (parser);
+    if (parser == NULL) {
+        cr_doc_handler_unref (css_handler);
         return;
     }
 
-    status = cr_parser_set_use_core_grammar (parser, FALSE);
-    status = cr_parser_parse (parser);
+    cr_parser_set_sac_handler (parser, css_handler);
+    cr_doc_handler_unref (css_handler);
+
+    cr_parser_set_use_core_grammar (parser, FALSE);
+    cr_parser_parse (parser);
 
     cr_parser_destroy (parser);
 }
@@ -1021,102 +1181,46 @@ ccss_import_style (CRDocHandler * a_this,
                    GList * a_media_list,
                    CRString * a_uri, CRString * a_uri_default_ns, CRParsingLocation * a_location)
 {
-    if (a_uri) {
-        GByteArray *stylesheet_data;
-        CSSUserData *user_data;
-
-        user_data = (CSSUserData *) a_this->app_data;
-
-        stylesheet_data =
-            _rsvg_acquire_xlink_href_resource ((gchar *) cr_string_peek_raw_str (a_uri),
-                                               rsvg_handle_get_base_uri (user_data->ctx), NULL);
-        if (stylesheet_data) {
-            rsvg_real_parse_cssbuffer (user_data->ctx, (const char *) stylesheet_data->data,
-                                       (size_t) stylesheet_data->len);
-            g_byte_array_free (stylesheet_data, TRUE);
-        }
-    }
-}
-
-#else                           /* !HAVE_LIBCROCO */
-
-static void
-rsvg_real_parse_cssbuffer (RsvgHandle * ctx, const char *buff, size_t buflen)
-{
-    /*
-     * Extremely poor man's CSS parser. Not robust. Not compliant.
-     * See also: http://www.w3.org/TR/REC-CSS2/syndata.html
-     */
-
-    size_t loc = 0;
-
-    while (loc < buflen) {
-        GString *style_name = g_string_new (NULL);
-        GString *style_props = g_string_new (NULL);
-
-        /* advance to the style's name */
-        while (loc < buflen && g_ascii_isspace (buff[loc]))
-            loc++;
-
-        while (loc < buflen && !g_ascii_isspace (buff[loc]))
-            g_string_append_c (style_name, buff[loc++]);
-
-        /* advance to the first { that defines the style's properties */
-        while (loc < buflen && buff[loc++] != '{');
-
-        while (loc < buflen && g_ascii_isspace (buff[loc]))
-            loc++;
+    CSSUserData *user_data = (CSSUserData *) a_this->app_data;
+    char *stylesheet_data;
+    gsize stylesheet_data_len;
+    char *mime_type = NULL;
 
-        while (loc < buflen && buff[loc] != '}') {
-            /* suck in and append our property */
-            while (loc < buflen && buff[loc] != ';' && buff[loc] != '}')
-                g_string_append_c (style_props, buff[loc++]);
-
-            if (loc == buflen || buff[loc] == '}')
-                break;
-            else {
-                g_string_append_c (style_props, ';');
-
-                /* advance to the next property */
-                loc++;
-                while (loc < buflen && g_ascii_isspace (buff[loc]))
-                    loc++;
-            }
-        }
-
-        rsvg_css_define_style (ctx, style_name->str, style_props->str);
-        g_string_free (style_name, TRUE);
-        g_string_free (style_props, TRUE);
+    if (a_uri == NULL)
+        return;
 
-        loc++;
-        while (loc < buflen && g_ascii_isspace (buff[loc]))
-            loc++;
+    stylesheet_data = _rsvg_handle_acquire_data (user_data->ctx,
+                                                 cr_string_peek_raw_str (a_uri),
+                                                 &mime_type,
+                                                 &stylesheet_data_len,
+                                                 NULL);
+    if (stylesheet_data == NULL || 
+        mime_type == NULL || 
+        strcmp (mime_type, "text/css") != 0) {
+        g_free (stylesheet_data);
+        g_free (mime_type);
+        return;
     }
-}
 
-#endif                          /* HAVE_LIBCROCO */
-
-void
-rsvg_parse_cssbuffer (RsvgHandle * ctx, const char *buff, size_t buflen)
-{
-    /* delegate off to the builtin or libcroco implementation */
-    rsvg_real_parse_cssbuffer (ctx, buff, buflen);
+    rsvg_parse_cssbuffer (user_data->ctx, stylesheet_data, stylesheet_data_len);
+    g_free (stylesheet_data);
+    g_free (mime_type);
 }
 
 /* Parse an SVG transform string into an affine matrix. Reference: SVG
    working draft dated 1999-07-06, section 8.5. Return TRUE on
    success. */
 gboolean
-rsvg_parse_transform (double dst[6], const char *src)
+rsvg_parse_transform (cairo_matrix_t *dst, const char *src)
 {
     int idx;
     char keyword[32];
     double args[6];
     int n_args;
     guint key_len;
-    double tmp_affine[6];
+    cairo_matrix_t affine;
 
-    _rsvg_affine_identity (dst);
+    cairo_matrix_init_identity (dst);
 
     idx = 0;
     while (src[idx]) {
@@ -1180,49 +1284,52 @@ rsvg_parse_transform (double dst[6], const char *src)
         if (!strcmp (keyword, "matrix")) {
             if (n_args != 6)
                 return FALSE;
-            _rsvg_affine_multiply (dst, args, dst);
+
+            cairo_matrix_init (&affine, args[0], args[1], args[2], args[3], args[4], args[5]);
+            cairo_matrix_multiply (dst, &affine, dst);
         } else if (!strcmp (keyword, "translate")) {
             if (n_args == 1)
                 args[1] = 0;
             else if (n_args != 2)
                 return FALSE;
-            _rsvg_affine_translate (tmp_affine, args[0], args[1]);
-            _rsvg_affine_multiply (dst, tmp_affine, dst);
+            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;
-            _rsvg_affine_scale (tmp_affine, args[0], args[1]);
-            _rsvg_affine_multiply (dst, tmp_affine, dst);
+            cairo_matrix_init_scale (&affine, args[0], args[1]);
+            cairo_matrix_multiply (dst, &affine, dst);
         } else if (!strcmp (keyword, "rotate")) {
             if (n_args == 1) {
-                _rsvg_affine_rotate (tmp_affine, args[0]);
-                _rsvg_affine_multiply (dst, tmp_affine, dst);
+
+                cairo_matrix_init_rotate (&affine, args[0] * M_PI / 180.);
+                cairo_matrix_multiply (dst, &affine, dst);
             } else if (n_args == 3) {
-                _rsvg_affine_translate (tmp_affine, args[1], args[2]);
-                _rsvg_affine_multiply (dst, tmp_affine, dst);
+                cairo_matrix_init_translate (&affine, args[1], args[2]);
+                cairo_matrix_multiply (dst, &affine, dst);
 
-                _rsvg_affine_rotate (tmp_affine, args[0]);
-                _rsvg_affine_multiply (dst, tmp_affine, dst);
+                cairo_matrix_init_rotate (&affine, args[0] * M_PI / 180.);
+                cairo_matrix_multiply (dst, &affine, dst);
 
-                _rsvg_affine_translate (tmp_affine, -args[1], -args[2]);
-                _rsvg_affine_multiply (dst, tmp_affine, dst);
+                cairo_matrix_init_translate (&affine, -args[1], -args[2]);
+                cairo_matrix_multiply (dst, &affine, dst);
             } else
                 return FALSE;
         } else if (!strcmp (keyword, "skewX")) {
             if (n_args != 1)
                 return FALSE;
-            _rsvg_affine_shear (tmp_affine, args[0]);
-            _rsvg_affine_multiply (dst, tmp_affine, dst);
+            _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;
-            _rsvg_affine_shear (tmp_affine, args[0]);
+            _rsvg_cairo_matrix_init_shear (&affine, args[0]);
             /* transpose the affine, given that we know [1] is zero */
-            tmp_affine[1] = tmp_affine[2];
-            tmp_affine[2] = 0;
-            _rsvg_affine_multiply (dst, tmp_affine, dst);
+            affine.yx = affine.xy;
+            affine.xy = 0.;
+            cairo_matrix_multiply (dst, &affine, dst);
         } else
             return FALSE;       /* unknown keyword */
     }
@@ -1230,7 +1337,7 @@ rsvg_parse_transform (double dst[6], const char *src)
 }
 
 /**
- * rsvg_parse_transform_attr: Parse transform attribute and apply to state.
+ * rsvg_parse_transform_attr:
  * @ctx: Rsvg context.
  * @state: State in which to apply the transform.
  * @str: String containing transform.
@@ -1240,32 +1347,50 @@ rsvg_parse_transform (double dst[6], const char *src)
 static void
 rsvg_parse_transform_attr (RsvgHandle * ctx, RsvgState * state, const char *str)
 {
-    double affine[6];
+    cairo_matrix_t affine;
 
-    if (rsvg_parse_transform (affine, str)) {
-        _rsvg_affine_multiply (state->personal_affine, affine, state->personal_affine);
-        _rsvg_affine_multiply (state->affine, affine, state->affine);
+    if (rsvg_parse_transform (&affine, str)) {
+        cairo_matrix_multiply (&state->personal_affine, &affine, &state->personal_affine);
+        cairo_matrix_multiply (&state->affine, &affine, &state->affine);
     }
 }
 
+typedef struct _StylesData {
+    RsvgHandle *ctx;
+    RsvgState *state;
+} StylesData;
+
+static void
+apply_style (const gchar *key, StyleValueData *value, gpointer user_data)
+{
+    StylesData *data = (StylesData *) user_data;
+    rsvg_parse_style_pair (data->ctx, data->state, key, value->value, value->important);
+}
+
 static gboolean
 rsvg_lookup_apply_css_style (RsvgHandle * ctx, const char *target, RsvgState * state)
 {
-    const char *value = (const char *) g_hash_table_lookup (ctx->priv->css_props, target);
+    GHashTable *styles;
+
+    styles = g_hash_table_lookup (ctx->priv->css_props, target);
 
-    if (value != NULL) {
-        rsvg_parse_style (ctx, state, value);
+    if (styles != NULL) {
+        StylesData *data = g_new (StylesData, 1);
+        data->ctx = ctx;
+        data->state = state;
+        g_hash_table_foreach (styles, (GHFunc) apply_style, data);
+        g_free (data);
         return TRUE;
     }
     return FALSE;
 }
 
 /**
- * rsvg_parse_style_attrs: Parse style attribute.
+ * rsvg_parse_style_attrs:
  * @ctx: Rsvg context.
  * @state: Rsvg state
- * @tag: The SVG tag we're processing (eg: circle, ellipse), optionally %NULL
- * @klazz: The space delimited class list, optionally %NULL
+ * @tag: (nullable): The SVG tag we're processing (eg: circle, ellipse), optionally %NULL
+ * @klazz: (nullable): The space delimited class list, optionally %NULL
  * @atts: Attributes in SAX style.
  *
  * Parses style and transform attributes and modifies state at top of
@@ -1281,6 +1406,9 @@ rsvg_parse_style_attrs (RsvgHandle * ctx,
     gboolean found = FALSE;
     GString *klazz_list = NULL;
 
+    if (rsvg_property_bag_size (atts) > 0)
+        rsvg_parse_style_pairs (ctx, state, atts);
+
     /* Try to properly support all of the following, including inheritance:
      * *
      * #id
@@ -1295,6 +1423,11 @@ rsvg_parse_style_attrs (RsvgHandle * ctx,
     /* * */
     rsvg_lookup_apply_css_style (ctx, "*", state);
 
+    /* tag */
+    if (tag != NULL) {
+        rsvg_lookup_apply_css_style (ctx, tag, state);
+    }
+
     if (klazz != NULL) {
         i = strlen (klazz);
         while (j < i) {
@@ -1307,16 +1440,23 @@ rsvg_parse_style_attrs (RsvgHandle * ctx,
             while (j < i && !g_ascii_isspace (klazz[j]))
                 g_string_append_c (klazz_list, klazz[j++]);
 
-            /* tag.class */
-            if (tag != NULL && klazz_list->len != 1) {
-                target = g_strdup_printf ("%s%s", tag, klazz_list->str);
+            /* tag.class#id */
+            if (tag != NULL && klazz_list->len != 1 && id != NULL) {
+                target = g_strdup_printf ("%s%s#%s", tag, klazz_list->str, id);
                 found = found || rsvg_lookup_apply_css_style (ctx, target, state);
                 g_free (target);
             }
 
-            /* tag.class#id */
-            if (tag != NULL && klazz_list->len != 1 && id != NULL) {
-                target = g_strdup_printf ("%s%s#%s", tag, klazz_list->str, id);
+            /* class#id */
+            if (klazz_list->len != 1 && id != NULL) {
+                target = g_strdup_printf ("%s#%s", klazz_list->str, id);
+                found = found || rsvg_lookup_apply_css_style (ctx, target, state);
+                g_free (target);
+            }
+
+            /* tag.class */
+            if (tag != NULL && klazz_list->len != 1) {
+                target = g_strdup_printf ("%s%s", tag, klazz_list->str);
                 found = found || rsvg_lookup_apply_css_style (ctx, target, state);
                 g_free (target);
             }
@@ -1329,29 +1469,23 @@ rsvg_parse_style_attrs (RsvgHandle * ctx,
         }
     }
 
-    /* tag#id */
-    if (tag != NULL && id != NULL && !found) {
-        target = g_strdup_printf ("%s#%s", tag, id);
+    /* #id */
+    if (id != NULL) {
+        target = g_strdup_printf ("#%s", id);
         rsvg_lookup_apply_css_style (ctx, target, state);
         g_free (target);
     }
 
-    /* #id */
-    if (id != NULL && !found) {
-        target = g_strdup_printf ("#%s", id);
-        found = rsvg_lookup_apply_css_style (ctx, target, state);
+    /* tag#id */
+    if (tag != NULL && id != NULL) {
+        target = g_strdup_printf ("%s#%s", tag, id);
+        rsvg_lookup_apply_css_style (ctx, target, state);
         g_free (target);
     }
 
-    /* tag */
-    if (tag != NULL && !found)
-        found = rsvg_lookup_apply_css_style (ctx, tag, state);
-
     if (rsvg_property_bag_size (atts) > 0) {
         const char *value;
 
-        rsvg_parse_style_pairs (ctx, state, atts);
-
         if ((value = rsvg_property_bag_lookup (atts, "style")) != NULL)
             rsvg_parse_style (ctx, state, value);
         if ((value = rsvg_property_bag_lookup (atts, "transform")) != NULL)
@@ -1360,58 +1494,103 @@ rsvg_parse_style_attrs (RsvgHandle * ctx,
 }
 
 RsvgState *
-rsvg_state_current (RsvgDrawingCtx * ctx)
+rsvg_current_state (RsvgDrawingCtx * ctx)
 {
-    return g_slist_nth_data (ctx->state, 0);
+    return ctx->state;
 }
 
 RsvgState *
-rsvg_state_parent (RsvgDrawingCtx * ctx)
+rsvg_state_parent (RsvgState * state)
 {
-    return g_slist_nth_data (ctx->state, 1);
+    return state->parent;
 }
 
+void
+rsvg_state_free_all (RsvgState * state)
+{
+    while (state) {
+        RsvgState *parent = state->parent;
+        rsvg_state_finalize (state);
+        g_slice_free (RsvgState, state);
+        state = parent;
+    }
+}
+
+/**
+ * rsvg_property_bag_new:
+ * @atts: (array zero-terminated=1): list of alternating attributes
+ *   and values
+ * 
+ * The property bag will NOT copy the attributes and values. If you need
+ * to store them for later, use rsvg_property_bag_dup().
+ * 
+ * Returns: (transfer full): a new property bag
+ */
 RsvgPropertyBag *
 rsvg_property_bag_new (const char **atts)
 {
     RsvgPropertyBag *bag;
     int i;
 
-    bag = g_new (RsvgPropertyBag, 1);
-    bag->props = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+    bag = g_hash_table_new (g_str_hash, g_str_equal);
 
     if (atts != NULL) {
         for (i = 0; atts[i] != NULL; i += 2)
-            g_hash_table_insert (bag->props, (gpointer) atts[i], (gpointer) atts[i + 1]);
+            g_hash_table_insert (bag, (gpointer) atts[i], (gpointer) atts[i + 1]);
     }
 
     return bag;
 }
 
+/**
+ * rsvg_property_bag_dup:
+ * @bag: property bag to duplicate
+ * 
+ * Returns a copy of @bag that owns the attributes and values.
+ * 
+ * Returns: (transfer full): a new property bag
+ */
+RsvgPropertyBag *
+rsvg_property_bag_dup (RsvgPropertyBag * bag)
+{
+    RsvgPropertyBag *dup;
+    GHashTableIter iter;
+    gpointer key, value;
+
+    dup = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+    g_hash_table_iter_init (&iter, bag);
+    while (g_hash_table_iter_next (&iter, &key, &value))
+      g_hash_table_insert (dup, 
+                           (gpointer) g_strdup ((char *) key),
+                           (gpointer) g_strdup ((char *) value));
+
+    return dup;
+}
+
 void
 rsvg_property_bag_free (RsvgPropertyBag * bag)
 {
-    g_hash_table_destroy (bag->props);
-    g_free (bag);
+    g_hash_table_unref (bag);
 }
 
-G_CONST_RETURN char *
+const char *
 rsvg_property_bag_lookup (RsvgPropertyBag * bag, const char *key)
 {
-    return (const char *) g_hash_table_lookup (bag->props, (gconstpointer) key);
+    return (const char *) g_hash_table_lookup (bag, (gconstpointer) key);
 }
 
 guint
 rsvg_property_bag_size (RsvgPropertyBag * bag)
 {
-    return g_hash_table_size (bag->props);
+    return g_hash_table_size (bag);
 }
 
 void
 rsvg_property_bag_enumerate (RsvgPropertyBag * bag, RsvgPropertyBagEnumFunc func,
                              gpointer user_data)
 {
-    g_hash_table_foreach (bag->props, (GHFunc) func, user_data);
+    g_hash_table_foreach (bag, (GHFunc) func, user_data);
 }
 
 void
@@ -1420,29 +1599,25 @@ rsvg_state_push (RsvgDrawingCtx * ctx)
     RsvgState *data;
     RsvgState *baseon;
 
-    baseon = (RsvgState *) g_slist_nth_data (ctx->state, 0);
+    baseon = ctx->state;
     data = g_slice_new (RsvgState);
+    rsvg_state_init (data);
 
     if (baseon) {
-        int i;
-        rsvg_state_init (data);
         rsvg_state_reinherit (data, baseon);
-        for (i = 0; i < 6; i++)
-            data->affine[i] = baseon->affine[i];
-    } else
-        rsvg_state_init (data);
+        data->affine = baseon->affine;
+        data->parent = baseon;
+    }
 
-    ctx->state = g_slist_prepend (ctx->state, data);
+    ctx->state = data;
 }
 
 void
 rsvg_state_pop (RsvgDrawingCtx * ctx)
 {
-    GSList *link = g_slist_nth (ctx->state, 0);
-    RsvgState *dead_state = (RsvgState *) link->data;
-
+    RsvgState *dead_state = ctx->state;
+    ctx->state = dead_state->parent;
     rsvg_state_finalize (dead_state);
-    ctx->state = g_slist_delete_link (ctx->state, link);
     g_slice_free (RsvgState, dead_state);
 }
 
@@ -1463,34 +1638,27 @@ rsvg_state_pop (RsvgDrawingCtx * ctx)
 void
 rsvg_state_reinherit_top (RsvgDrawingCtx * ctx, RsvgState * state, int dominate)
 {
+    RsvgState *current;
+
     if (dominate == 3)
         return;
 
+    current = rsvg_current_state (ctx);
     /*This is a special domination mode for patterns, the transform
        is simply left as is, wheras the style is totally overridden */
     if (dominate == 2) {
-        rsvg_state_override (rsvg_state_current (ctx), state);
-    } else if (dominate) {
-        RsvgState *parent;
-        rsvg_state_clone (rsvg_state_current (ctx), state);
-
-        parent = rsvg_state_parent (ctx);
-        if (parent) {
-            rsvg_state_dominate (rsvg_state_current (ctx), rsvg_state_parent (ctx));
-            _rsvg_affine_multiply (rsvg_state_current (ctx)->affine,
-                                   rsvg_state_current (ctx)->affine,
-                                   rsvg_state_parent (ctx)->affine);
-        }
+        rsvg_state_override (current, state);
     } else {
-        RsvgState *parent;
-        rsvg_state_clone (rsvg_state_current (ctx), state);
-
-        parent = rsvg_state_parent (ctx);
+        RsvgState *parent= rsvg_state_parent (current);
+        rsvg_state_clone (current, state);
         if (parent) {
-            rsvg_state_reinherit (rsvg_state_current (ctx), rsvg_state_parent (ctx));
-            _rsvg_affine_multiply (rsvg_state_current (ctx)->affine,
-                                   rsvg_state_current (ctx)->affine,
-                                   rsvg_state_parent (ctx)->affine);
+            if (dominate)
+                rsvg_state_dominate (current, parent);
+            else
+                rsvg_state_reinherit (current, parent);
+            cairo_matrix_multiply (&current->affine,
+                                   &current->affine,
+                                   &parent->affine);
         }
     }
 }
diff --git a/rsvg-styles.h b/rsvg-styles.h
index 7f91836..a044d3f 100644
--- a/rsvg-styles.h
+++ b/rsvg-styles.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-styles.h: Handle SVG styles
 
@@ -26,6 +27,7 @@
 #ifndef RSVG_STYLES_H
 #define RSVG_STYLES_H
 
+#include <cairo.h>
 #include "rsvg.h"
 #include "rsvg-paint-server.h"
 
@@ -49,11 +51,6 @@ typedef enum {
     TEXT_ANCHOR_END
 } TextAnchor;
 
-enum {
-    FILL_RULE_EVENODD = 0,
-    FILL_RULE_NONZERO = 1
-};
-
 typedef enum {
     UNICODE_BIDI_NORMAL = 0,
     UNICODE_BIDI_EMBED = 1,
@@ -61,65 +58,12 @@ typedef enum {
 } UnicodeBidi;
 
 typedef enum {
-    SHAPE_RENDERING_AUTO = 0,
-    SHAPE_RENDERING_OPTIMIZE_SPEED,
-    SHAPE_RENDERING_CRISP_EDGES,
-    SHAPE_RENDERING_GEOMETRIC_PRECISION
-} ShapeRenderingProperty;
-
-typedef enum {
-    TEXT_RENDERING_AUTO = 0,
-    TEXT_RENDERING_OPTIMIZE_SPEED,
-    TEXT_RENDERING_OPTIMIZE_LEGIBILITY,
-    TEXT_RENDERING_GEOMETRIC_PRECISION
-} TextRenderingProperty;
-
-typedef enum {
-    RSVG_COMP_OP_CLEAR,
-    RSVG_COMP_OP_SRC,
-    RSVG_COMP_OP_DST,
-    RSVG_COMP_OP_SRC_OVER,
-    RSVG_COMP_OP_DST_OVER,
-    RSVG_COMP_OP_SRC_IN,
-    RSVG_COMP_OP_DST_IN,
-    RSVG_COMP_OP_SRC_OUT,
-    RSVG_COMP_OP_DST_OUT,
-    RSVG_COMP_OP_SRC_ATOP,
-    RSVG_COMP_OP_DST_ATOP,
-    RSVG_COMP_OP_XOR,
-    RSVG_COMP_OP_PLUS,
-    RSVG_COMP_OP_MULTIPLY,
-    RSVG_COMP_OP_SCREEN,
-    RSVG_COMP_OP_OVERLAY,
-    RSVG_COMP_OP_DARKEN,
-    RSVG_COMP_OP_LIGHTEN,
-    RSVG_COMP_OP_COLOR_DODGE,
-    RSVG_COMP_OP_COLOR_BURN,
-    RSVG_COMP_OP_HARD_LIGHT,
-    RSVG_COMP_OP_SOFT_LIGHT,
-    RSVG_COMP_OP_DIFFERENCE,
-    RSVG_COMP_OP_EXCLUSION
-} RsvgCompOpType;
-
-typedef enum {
     RSVG_ENABLE_BACKGROUND_ACCUMULATE,
     RSVG_ENABLE_BACKGROUND_NEW
 } RsvgEnableBackgroundType;
 
 /* enums and data structures are ABI compatible with libart */
 
-typedef enum {
-    RSVG_PATH_STROKE_JOIN_MITER,
-    RSVG_PATH_STROKE_JOIN_ROUND,
-    RSVG_PATH_STROKE_JOIN_BEVEL
-} RsvgPathStrokeJoinType;
-
-typedef enum {
-    RSVG_PATH_STROKE_CAP_BUTT,
-    RSVG_PATH_STROKE_CAP_ROUND,
-    RSVG_PATH_STROKE_CAP_SQUARE
-} RsvgPathStrokeCapType;
-
 typedef struct _RsvgVpathDash RsvgVpathDash;
 
 struct _RsvgVpathDash {
@@ -131,14 +75,16 @@ struct _RsvgVpathDash {
 /* end libart theft... */
 
 struct _RsvgState {
-    double affine[6];
-    double personal_affine[6];
+    RsvgState *parent;
+    cairo_matrix_t affine;
+    cairo_matrix_t personal_affine;
 
-    RsvgFilter *filter;
-    void *mask;
-    void *clip_path_ref;
-    guint8 adobe_blend;         /* 0..11 */
+    char *filter;
+    char *mask;
+    char *clip_path;
     guint8 opacity;             /* 0..255 */
+    double baseline_shift;
+    gboolean has_baseline_shift;
 
     RsvgPaintServer *fill;
     gboolean has_fill_server;
@@ -161,9 +107,9 @@ struct _RsvgState {
     double miter_limit;
     gboolean has_miter_limit;
 
-    RsvgPathStrokeCapType cap;
+    cairo_line_cap_t cap;
     gboolean has_cap;
-    RsvgPathStrokeJoinType join;
+    cairo_line_join_t join;
     gboolean has_join;
 
     RsvgLength font_size;
@@ -184,6 +130,8 @@ struct _RsvgState {
     gboolean has_font_decor;
     PangoDirection text_dir;
     gboolean has_text_dir;
+    PangoGravity text_gravity;
+    gboolean has_text_gravity;
     UnicodeBidi unicode_bidi;
     gboolean has_unicode_bidi;
     TextAnchor text_anchor;
@@ -220,54 +168,79 @@ struct _RsvgState {
     guchar flood_opacity;
     gboolean has_flood_opacity;
 
-    RsvgNode *startMarker;
-    RsvgNode *middleMarker;
-    RsvgNode *endMarker;
+    char *startMarker;
+    char *middleMarker;
+    char *endMarker;
     gboolean has_startMarker;
     gboolean has_middleMarker;
     gboolean has_endMarker;
 
-    RsvgCompOpType comp_op;
+    cairo_operator_t comp_op;
     RsvgEnableBackgroundType enable_background;
 
-    ShapeRenderingProperty shape_rendering_type;
+    cairo_antialias_t shape_rendering_type;
     gboolean has_shape_rendering_type;
-    
-    TextRenderingProperty text_rendering_type;
+
+    cairo_antialias_t text_rendering_type;
     gboolean has_text_rendering_type;
-};
 
-RsvgState *rsvg_state_new ();
+    GHashTable *styles;
+};
 
-void rsvg_state_init	    (RsvgState * state);
-void rsvg_state_clone	    (RsvgState * dst, const RsvgState * src);
-void rsvg_state_inherit	    (RsvgState * dst, const RsvgState * src);
+G_GNUC_INTERNAL
+RsvgState *rsvg_state_new (void);
+
+G_GNUC_INTERNAL
+void rsvg_state_init        (RsvgState * state);
+G_GNUC_INTERNAL
+void rsvg_state_reinit      (RsvgState * state);
+G_GNUC_INTERNAL
+void rsvg_state_clone       (RsvgState * dst, const RsvgState * src);
+G_GNUC_INTERNAL
+void rsvg_state_inherit     (RsvgState * dst, const RsvgState * src);
+G_GNUC_INTERNAL
 void rsvg_state_reinherit   (RsvgState * dst, const RsvgState * src);
+G_GNUC_INTERNAL
 void rsvg_state_dominate    (RsvgState * dst, const RsvgState * src);
+G_GNUC_INTERNAL
 void rsvg_state_override    (RsvgState * dst, const RsvgState * src);
+G_GNUC_INTERNAL
 void rsvg_state_finalize    (RsvgState * state);
+G_GNUC_INTERNAL
+void rsvg_state_free_all    (RsvgState * state);
 
+G_GNUC_INTERNAL
 void rsvg_parse_style_pairs (RsvgHandle * ctx, RsvgState * state, RsvgPropertyBag * atts);
-void rsvg_parse_style_pair  (RsvgHandle * ctx, RsvgState * state, const char *key, const char *val);
+G_GNUC_INTERNAL
 void rsvg_parse_style	    (RsvgHandle * ctx, RsvgState * state, const char *str);
+G_GNUC_INTERNAL
 void rsvg_parse_cssbuffer   (RsvgHandle * ctx, const char *buff, size_t buflen);
-
+G_GNUC_INTERNAL
 void rsvg_parse_style_attrs (RsvgHandle * ctx, RsvgState * state, const char *tag,
                              const char *klazz, const char *id, RsvgPropertyBag * atts);
 
-gdouble rsvg_viewport_percentage    (gdouble width, gdouble height);
-gdouble rsvg_dpi_percentage	    (RsvgHandle * ctx);
+G_GNUC_INTERNAL
+gdouble rsvg_viewport_percentage (gdouble width, gdouble height);
+G_GNUC_INTERNAL
+gdouble rsvg_dpi_percentage      (RsvgHandle * ctx);
 
-gboolean rsvg_parse_transform	    (double dst[6], const char *src);
+G_GNUC_INTERNAL
+gboolean rsvg_parse_transform   (cairo_matrix_t *matrix, const char *src);
 
-RsvgState *rsvg_state_parent	(RsvgDrawingCtx * ctx);
-RsvgState *rsvg_state_current	(RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
+RsvgState *rsvg_state_parent    (RsvgState * state);
 
-void rsvg_state_pop	(RsvgDrawingCtx * ctx);
-void rsvg_state_push	(RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
+void       rsvg_state_pop       (RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
+void       rsvg_state_push      (RsvgDrawingCtx * ctx);
+G_GNUC_INTERNAL
+RsvgState *rsvg_current_state   (RsvgDrawingCtx * ctx);
 
+G_GNUC_INTERNAL
 void rsvg_state_reinherit_top	(RsvgDrawingCtx * ctx, RsvgState * state, int dominate);
 
+G_GNUC_INTERNAL
 void rsvg_state_reconstruct	(RsvgState * state, RsvgNode * current);
 
 G_END_DECLS
diff --git a/rsvg-text.c b/rsvg-text.c
index 05353dd..52e52c3 100644
--- a/rsvg-text.c
+++ b/rsvg-text.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-text.c: Text handling routines for RSVG
 
@@ -35,17 +36,6 @@
 /* what we use for text rendering depends on what cairo has to offer */
 #include <pango/pangocairo.h>
 
-#if defined CAIRO_HAS_FT_FONT
-#include <ft2build.h>
-#include FT_GLYPH_H
-#include FT_OUTLINE_H
-
-#include <pango/pangoft2.h>
-#elif defined (CAIRO_HAS_WIN32_FONT)
-/* nothing more needed? */
-#include <cairo-win32.h>
-#endif
-
 typedef struct _RsvgNodeText RsvgNodeText;
 
 struct _RsvgNodeText {
@@ -57,7 +47,7 @@ typedef struct _RsvgNodeTref RsvgNodeTref;
 
 struct _RsvgNodeTref {
     RsvgNode super;
-    RsvgNode *link;
+    char *link;
 };
 char *
 rsvg_make_valid_utf8 (const char *str, int len)
@@ -104,30 +94,30 @@ _rsvg_text_chomp (RsvgState *state, GString * in, gboolean * lastwasspace)
     guint i;
     out = g_string_new (in->str);
 
-	if (!state->space_preserve) {
-		for (i = 0; i < out->len;) {
-			if (out->str[i] == '\n')
-				g_string_erase (out, i, 1);
-			else
-				i++;
-		}
-		
-		for (i = 0; i < out->len; i++)
-			if (out->str[i] == '\t')
-				out->str[i] = ' ';
-		
-		for (i = 0; i < out->len;) {
-			if (out->str[i] == ' ' && *lastwasspace)
-				g_string_erase (out, i, 1);
-			else {
-				if (out->str[i] == ' ')
-					*lastwasspace = TRUE;
-				else
-					*lastwasspace = FALSE;
-				i++;
-			}
-		}
-	}
+    if (!state->space_preserve) {
+        for (i = 0; i < out->len;) {
+            if (out->str[i] == '\n')
+                g_string_erase (out, i, 1);
+            else
+                i++;
+        }
+
+        for (i = 0; i < out->len; i++)
+            if (out->str[i] == '\t')
+                out->str[i] = ' ';
+
+        for (i = 0; i < out->len;) {
+            if (out->str[i] == ' ' && *lastwasspace)
+                g_string_erase (out, i, 1);
+            else {
+                if (out->str[i] == ' ')
+                    *lastwasspace = TRUE;
+                else
+                    *lastwasspace = FALSE;
+                i++;
+            }
+        }
+    }
 
     return out;
 }
@@ -159,40 +149,55 @@ _rsvg_node_text_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
     }
 }
 
-void
+static void
  rsvg_text_render_text (RsvgDrawingCtx * ctx, const char *text, gdouble * x, gdouble * y);
 
 
 static void
  _rsvg_node_text_type_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx,
-                             gdouble * x, gdouble * y, gboolean * lastwasspace);
+                             gdouble * x, gdouble * y, gboolean * lastwasspace,
+                             gboolean usetextonly);
 
 static void
  _rsvg_node_text_type_tref (RsvgNodeTref * self, RsvgDrawingCtx * ctx,
-                            gdouble * x, gdouble * y, gboolean * lastwasspace);
+                            gdouble * x, gdouble * y, gboolean * lastwasspace,
+                            gboolean usetextonly);
 
+/* This function is responsible of selecting render for a text element including its children and giving it the drawing context */
 static void
 _rsvg_node_text_type_children (RsvgNode * self, RsvgDrawingCtx * ctx,
-                               gdouble * x, gdouble * y, gboolean * lastwasspace)
+                               gdouble * x, gdouble * y, gboolean * lastwasspace,
+                               gboolean usetextonly)
 {
     guint i;
 
     rsvg_push_discrete_layer (ctx);
     for (i = 0; i < self->children->len; i++) {
         RsvgNode *node = g_ptr_array_index (self->children, i);
-        if (!strcmp (node->type->str, "RSVG_NODE_CHARS")) {
+        RsvgNodeType type = RSVG_NODE_TYPE (node);
+
+        if (type == RSVG_NODE_TYPE_CHARS) {
             RsvgNodeChars *chars = (RsvgNodeChars *) node;
-            GString *str = _rsvg_text_chomp (rsvg_state_current (ctx), chars->contents, lastwasspace);
+            GString *str = _rsvg_text_chomp (rsvg_current_state (ctx), chars->contents, lastwasspace);
             rsvg_text_render_text (ctx, str->str, x, y);
             g_string_free (str, TRUE);
-        } else if (!strcmp (node->type->str, "tspan")) {
-            RsvgNodeText *tspan = (RsvgNodeText *) node;
-            rsvg_state_push (ctx);
-            _rsvg_node_text_type_tspan (tspan, ctx, x, y, lastwasspace);
-            rsvg_state_pop (ctx);
-        } else if (!strcmp (node->type->str, "tref")) {
-            RsvgNodeTref *tref = (RsvgNodeTref *) node;
-            _rsvg_node_text_type_tref (tref, ctx, x, y, lastwasspace);
+        } else {
+            if (usetextonly) {
+                _rsvg_node_text_type_children (node, ctx, x, y, lastwasspace,
+                                               usetextonly);
+            } else {
+                if (type == RSVG_NODE_TYPE_TSPAN) {
+                    RsvgNodeText *tspan = (RsvgNodeText *) node;
+                    rsvg_state_push (ctx);
+                    _rsvg_node_text_type_tspan (tspan, ctx, x, y, lastwasspace,
+                                                usetextonly);
+                    rsvg_state_pop (ctx);
+                } else if (type == RSVG_NODE_TYPE_TREF) {
+                    RsvgNodeTref *tref = (RsvgNodeTref *) node;
+                    _rsvg_node_text_type_tref (tref, ctx, x, y, lastwasspace,
+                                               usetextonly);
+                }
+            }
         }
     }
     rsvg_pop_discrete_layer (ctx);
@@ -200,34 +205,54 @@ _rsvg_node_text_type_children (RsvgNode * self, RsvgDrawingCtx * ctx,
 
 static int
  _rsvg_node_text_length_tref (RsvgNodeTref * self, RsvgDrawingCtx * ctx,
-                              gdouble * x, gboolean * lastwasspace);
+                              gdouble * x, gboolean * lastwasspace,
+                              gboolean usetextonly);
 
 static int
  _rsvg_node_text_length_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx,
-                               gdouble * x, gboolean * lastwasspace);
+                               gdouble * x, gboolean * lastwasspace,
+                               gboolean usetextonly);
 
 static gdouble rsvg_text_length_text_as_string (RsvgDrawingCtx * ctx, const char *text);
 
 static int
 _rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx,
-                                 gdouble * x, gboolean * lastwasspace)
+                                 gdouble * length, gboolean * lastwasspace,
+                                 gboolean usetextonly)
 {
     guint i;
     int out = FALSE;
     for (i = 0; i < self->children->len; i++) {
         RsvgNode *node = g_ptr_array_index (self->children, i);
-        if (!strcmp (node->type->str, "RSVG_NODE_CHARS")) {
+        RsvgNodeType type = RSVG_NODE_TYPE (node);
+
+        rsvg_state_push (ctx);
+        rsvg_state_reinherit_top (ctx, node->state, 0);
+        if (type == RSVG_NODE_TYPE_CHARS) {
             RsvgNodeChars *chars = (RsvgNodeChars *) node;
-            GString *str = _rsvg_text_chomp (rsvg_state_current (ctx), chars->contents, lastwasspace);
-            *x += rsvg_text_length_text_as_string (ctx, str->str);
+            GString *str = _rsvg_text_chomp (rsvg_current_state (ctx), chars->contents, lastwasspace);
+            *length += rsvg_text_length_text_as_string (ctx, str->str);
             g_string_free (str, TRUE);
-        } else if (!strcmp (node->type->str, "tspan")) {
-            RsvgNodeText *tspan = (RsvgNodeText *) node;
-            out = _rsvg_node_text_length_tspan (tspan, ctx, x, lastwasspace);
-        } else if (!strcmp (node->type->str, "tref")) {
-            RsvgNodeTref *tref = (RsvgNodeTref *) node;
-            out = _rsvg_node_text_length_tref (tref, ctx, x, lastwasspace);
+        } else {
+            if (usetextonly) {
+                out = _rsvg_node_text_length_children(node, ctx, length,
+                                                      lastwasspace,
+                                                      usetextonly);
+            } else {
+                if (type == RSVG_NODE_TYPE_TSPAN) {
+                    RsvgNodeText *tspan = (RsvgNodeText *) node;
+                    out = _rsvg_node_text_length_tspan (tspan, ctx, length,
+                                                        lastwasspace,
+                                                        usetextonly);
+                } else if (type == RSVG_NODE_TYPE_TREF) {
+                    RsvgNodeTref *tref = (RsvgNodeTref *) node;
+                    out = _rsvg_node_text_length_tref (tref, ctx, length,
+                                                       lastwasspace,
+                                                       usetextonly);
+                }
+            }
         }
+        rsvg_state_pop (ctx);
         if (out)
             break;
     }
@@ -238,27 +263,39 @@ _rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx,
 static void
 _rsvg_node_text_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
 {
-    double x, y;
+    double x, y, dx, dy, length = 0;
     gboolean lastwasspace = TRUE;
     RsvgNodeText *text = (RsvgNodeText *) self;
     rsvg_state_reinherit_top (ctx, self->state, dominate);
 
     x = _rsvg_css_normalize_length (&text->x, ctx, 'h');
     y = _rsvg_css_normalize_length (&text->y, ctx, 'v');
-    x += _rsvg_css_normalize_length (&text->dx, ctx, 'h');
-    y += _rsvg_css_normalize_length (&text->dy, ctx, 'v');
-
-    if (rsvg_state_current (ctx)->text_anchor != TEXT_ANCHOR_START) {
-        double length = 0;
-        _rsvg_node_text_length_children (self, ctx, &length, &lastwasspace);
-        if (rsvg_state_current (ctx)->text_anchor == TEXT_ANCHOR_END)
-            x -= length;
-        if (rsvg_state_current (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
-            x -= length / 2;
+    dx = _rsvg_css_normalize_length (&text->dx, ctx, 'h');
+    dy = _rsvg_css_normalize_length (&text->dy, ctx, 'v');
+
+    if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) {
+        _rsvg_node_text_length_children (self, ctx, &length, &lastwasspace, FALSE);
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
+            length /= 2;
     }
+    if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) {
+        y -= length;
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
+            dy /= 2;
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
+            dy = 0;
+    } else {
+        x -= length;
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
+            dx /= 2;
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
+            dx = 0;
+    }
+    x += dx;
+    y += dy;
 
     lastwasspace = TRUE;
-    _rsvg_node_text_type_children (self, ctx, &x, &y, &lastwasspace);
+    _rsvg_node_text_type_children (self, ctx, &x, &y, &lastwasspace, FALSE);
 }
 
 RsvgNode *
@@ -266,7 +303,7 @@ rsvg_new_text (void)
 {
     RsvgNodeText *text;
     text = g_new (RsvgNodeText, 1);
-    _rsvg_node_init (&text->super);
+    _rsvg_node_init (&text->super, RSVG_NODE_TYPE_TEXT);
     text->super.draw = _rsvg_node_text_draw;
     text->super.set_atts = _rsvg_node_text_set_atts;
     text->x = text->y = text->dx = text->dy = _rsvg_css_parse_length ("0");
@@ -275,36 +312,65 @@ rsvg_new_text (void)
 
 static void
 _rsvg_node_text_type_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx,
-                            gdouble * x, gdouble * y, gboolean * lastwasspace)
+                            gdouble * x, gdouble * y, gboolean * lastwasspace,
+                            gboolean usetextonly)
 {
+    double dx, dy, length = 0;
     rsvg_state_reinherit_top (ctx, self->super.state, 0);
 
+    dx = _rsvg_css_normalize_length (&self->dx, ctx, 'h');
+    dy = _rsvg_css_normalize_length (&self->dy, ctx, 'v');
+
+    if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) {
+        gboolean lws = *lastwasspace;
+        _rsvg_node_text_length_children (&self->super, ctx, &length, &lws,
+                                         usetextonly);
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
+            length /= 2;
+    }
+
     if (self->x.factor != 'n') {
         *x = _rsvg_css_normalize_length (&self->x, ctx, 'h');
-        if (rsvg_state_current (ctx)->text_anchor != TEXT_ANCHOR_START) {
-            double length = 0;
-            gboolean lws = *lastwasspace;
-            _rsvg_node_text_length_children (&self->super, ctx, &length, &lws);
-            if (rsvg_state_current (ctx)->text_anchor == TEXT_ANCHOR_END)
-                *x -= length;
-            if (rsvg_state_current (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
-                *x -= length / 2;
+        if (!PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) {
+            *x -= length;
+            if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
+                dx /= 2;
+            if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
+                dx = 0;
         }
     }
-    if (self->y.factor != 'n')
+    *x += dx;
+
+    if (self->y.factor != 'n') {
         *y = _rsvg_css_normalize_length (&self->y, ctx, 'v');
-    *x += _rsvg_css_normalize_length (&self->dx, ctx, 'h');
-    *y += _rsvg_css_normalize_length (&self->dy, ctx, 'v');
-    _rsvg_node_text_type_children (&self->super, ctx, x, y, lastwasspace);
+        if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) {
+            *y -= length;
+            if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
+                dy /= 2;
+            if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
+                dy = 0;
+        }
+    }
+    *y += dy;
+    _rsvg_node_text_type_children (&self->super, ctx, x, y, lastwasspace,
+                                   usetextonly);
 }
 
 static int
-_rsvg_node_text_length_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx, gdouble * x,
-                              gboolean * lastwasspace)
+_rsvg_node_text_length_tspan (RsvgNodeText * self,
+                              RsvgDrawingCtx * ctx, gdouble * length,
+                              gboolean * lastwasspace, gboolean usetextonly)
 {
     if (self->x.factor != 'n' || self->y.factor != 'n')
         return TRUE;
-    return _rsvg_node_text_length_children (&self->super, ctx, x, lastwasspace);
+
+    if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity))
+        *length += _rsvg_css_normalize_length (&self->dy, ctx, 'v');
+    else
+        *length += _rsvg_css_normalize_length (&self->dx, ctx, 'h');
+
+    return _rsvg_node_text_length_children (&self->super, ctx, length,
+                                             lastwasspace, usetextonly);
 }
 
 static void
@@ -338,7 +404,7 @@ rsvg_new_tspan (void)
 {
     RsvgNodeText *text;
     text = g_new (RsvgNodeText, 1);
-    _rsvg_node_init (&text->super);
+    _rsvg_node_init (&text->super, RSVG_NODE_TYPE_TSPAN);
     text->super.set_atts = _rsvg_node_tspan_set_atts;
     text->x.factor = text->y.factor = 'n';
     text->dx = text->dy = _rsvg_css_parse_length ("0");
@@ -347,19 +413,49 @@ rsvg_new_tspan (void)
 
 static void
 _rsvg_node_text_type_tref (RsvgNodeTref * self, RsvgDrawingCtx * ctx,
-                           gdouble * x, gdouble * y, gboolean * lastwasspace)
+                           gdouble * x, gdouble * y, gboolean * lastwasspace,
+                           gboolean usetextonly)
 {
-    if (self->link)
-        _rsvg_node_text_type_children (self->link, ctx, x, y, lastwasspace);
+    RsvgNode *link;
+
+    if (self->link == NULL)
+      return;
+    link = rsvg_acquire_node (ctx, self->link);
+    if (link == NULL)
+      return;
+
+    _rsvg_node_text_type_children (link, ctx, x, y, lastwasspace,
+                                                    TRUE);
+
+    rsvg_release_node (ctx, link);
 }
 
 static int
 _rsvg_node_text_length_tref (RsvgNodeTref * self, RsvgDrawingCtx * ctx, gdouble * x,
-                             gboolean * lastwasspace)
+                             gboolean * lastwasspace, gboolean usetextonly)
 {
-    if (self->link)
-        return _rsvg_node_text_length_children (self->link, ctx, x, lastwasspace);
-    return FALSE;
+    gboolean result;
+    RsvgNode *link;
+
+    if (self->link == NULL)
+      return FALSE;
+    link = rsvg_acquire_node (ctx, self->link);
+    if (link == NULL)
+      return FALSE;
+
+    result = _rsvg_node_text_length_children (link, ctx, x, lastwasspace, TRUE);
+
+    rsvg_release_node (ctx, link);
+
+    return result;
+}
+
+static void
+rsvg_node_tref_free (RsvgNode * node)
+{
+    RsvgNodeTref *self = (RsvgNodeTref *) node;
+    g_free (self->link);
+    _rsvg_node_free (node);
 }
 
 static void
@@ -369,8 +465,10 @@ _rsvg_node_tref_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * a
     RsvgNodeTref *text = (RsvgNodeTref *) self;
 
     if (rsvg_property_bag_size (atts)) {
-        if ((value = rsvg_property_bag_lookup (atts, "xlink:href")))
-            rsvg_defs_add_resolver (ctx->priv->defs, &text->link, value);
+        if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) {
+            g_free (text->link);
+            text->link = g_strdup (value);
+        }
         if ((value = rsvg_property_bag_lookup (atts, "id")))
             rsvg_defs_register_name (ctx->priv->defs, value, self);
     }
@@ -381,7 +479,8 @@ rsvg_new_tref (void)
 {
     RsvgNodeTref *text;
     text = g_new (RsvgNodeTref, 1);
-    _rsvg_node_init (&text->super);
+    _rsvg_node_init (&text->super, RSVG_NODE_TYPE_TREF);
+    text->super.free = rsvg_node_tref_free;
     text->super.set_atts = _rsvg_node_tref_set_atts;
     text->link = NULL;
     return &text->super;
@@ -394,121 +493,12 @@ struct _RsvgTextLayout {
     RsvgDrawingCtx *ctx;
     TextAnchor anchor;
     gdouble x, y;
-    gboolean orientation;
 };
 
-typedef struct _RenderCtx RenderCtx;
-
-struct _RenderCtx {
-    GString *path;
-    gboolean wrote;
-    gdouble offset_x;
-    gdouble offset_y;
-};
-
-#ifdef CAIRO_HAS_FT_FONT
-typedef void (*RsvgTextRenderFunc) (PangoFont * font,
-                                    PangoGlyph glyph,
-                                    FT_Int32 load_flags, gint x, gint y, gpointer render_data);
-
-#ifndef FT_GLYPH_FORMAT_OUTLINE
-#define FT_GLYPH_FORMAT_OUTLINE ft_glyph_format_outline
-#endif
-
-#ifndef FT_LOAD_TARGET_MONO
-#define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME
-#endif
-#endif /* CAIRO_HAS_FT_FONT */
-
-static RenderCtx *
-rsvg_render_ctx_new (void)
-{
-    RenderCtx *ctx;
-
-    ctx = g_new0 (RenderCtx, 1);
-    ctx->path = g_string_new (NULL);
-
-    return ctx;
-}
-
-static void
-rsvg_render_ctx_free (RenderCtx * ctx)
-{
-    g_string_free (ctx->path, TRUE);
-    g_free (ctx);
-}
-
-#ifdef CAIRO_HAS_FT_FONT
-static void
-rsvg_text_ft2_subst_func (FcPattern * pattern, gpointer data)
-{
-    RsvgHandle *ctx = (RsvgHandle *) data;
-
-    (void) ctx;
-
-    FcPatternAddBool (pattern, FC_HINTING, 0);
-    FcPatternAddBool (pattern, FC_ANTIALIAS, 0);
-    FcPatternAddBool (pattern, FC_AUTOHINT, 0);
-    FcPatternAddBool (pattern, FC_SCALABLE, 1);
-}
-
-static PangoContext *
-rsvg_text_get_pango_context (RsvgDrawingCtx * ctx)
-{
-    PangoContext *context;
-    PangoFT2FontMap *fontmap;
-
-    fontmap = PANGO_FT2_FONT_MAP (pango_ft2_font_map_new ());
-
-    pango_ft2_font_map_set_resolution (fontmap, ctx->dpi_x, ctx->dpi_y);
-
-    pango_ft2_font_map_set_default_substitute (fontmap,
-                                               rsvg_text_ft2_subst_func,
-                                               ctx, (GDestroyNotify) NULL);
-
-    context = pango_ft2_font_map_create_context (fontmap);
-    g_object_unref (fontmap);
-
-    /*  Workaround for bug #143542 (PangoFT2Fontmap leak),
-     *  see also bug #344235 (Text layer rendering leaks font file descriptor):
-     *
-     *  Calling pango_ft2_font_map_substitute_changed() causes the
-     *  font_map cache to be flushed, thereby removing the circular
-     *  reference that causes the leak.
-     */
-    g_object_weak_ref (G_OBJECT (context),
-                       (GWeakNotify) pango_ft2_font_map_substitute_changed, fontmap);
-
-
-    return context;
-}
-#else
-/* although the #if condtionalizes on FT2 here we try to use pure cairo */
-typedef void (*RsvgTextRenderFunc) (PangoFont * font,
-                                    PangoGlyph glyph,
-                                    gint x, gint y, gpointer render_data);
-
-static PangoContext *
-rsvg_text_get_pango_context (RsvgDrawingCtx * ctx)
-{
-    PangoContext *context;
-    PangoCairoFontMap *fontmap;
-    
-    fontmap = PANGO_CAIRO_FONT_MAP (pango_cairo_font_map_new ());
-    if (ctx->dpi_x != ctx->dpi_y)
-	g_warning ("asymmetric dpi not handled");
-    pango_cairo_font_map_set_resolution (fontmap, ctx->dpi_x);
-    context = pango_cairo_font_map_create_context (fontmap);
-    g_object_unref (fontmap);
-    
-    return context;
-}
-#endif /* #ifdef CAIRO_HAS_FT_FONT */
-
 static void
 rsvg_text_layout_free (RsvgTextLayout * layout)
 {
-    g_object_unref (G_OBJECT (layout->layout));
+    g_object_unref (layout->layout);
     g_free (layout);
 }
 
@@ -527,6 +517,9 @@ rsvg_text_create_layout (RsvgDrawingCtx * ctx,
     if (state->unicode_bidi == UNICODE_BIDI_OVERRIDE || state->unicode_bidi == UNICODE_BIDI_EMBED)
         pango_context_set_base_dir (context, state->text_dir);
 
+    if (PANGO_GRAVITY_IS_VERTICAL (state->text_gravity))
+        pango_context_set_base_gravity (context, state->text_gravity);
+
     font_desc = pango_font_description_copy (pango_context_get_font_description (context));
 
     if (state->font_family)
@@ -537,17 +530,16 @@ rsvg_text_create_layout (RsvgDrawingCtx * ctx,
     pango_font_description_set_weight (font_desc, state->font_weight);
     pango_font_description_set_stretch (font_desc, state->font_stretch);
     pango_font_description_set_size (font_desc,
-                                     _rsvg_css_normalize_length (&state->font_size, ctx,
-                                                                 'v') * PANGO_SCALE / ctx->dpi_y *
-                                     72);
+                                     _rsvg_css_normalize_font_size (state, ctx) *
+                                     PANGO_SCALE / ctx->dpi_y * 72);
 
     layout = pango_layout_new (context);
     pango_layout_set_font_description (layout, font_desc);
     pango_font_description_free (font_desc);
 
     attr_list = pango_attr_list_new ();
-    attribute = pango_attr_letter_spacing_new ( _rsvg_css_normalize_length (&state->letter_spacing, 
-									    ctx, 'h') * PANGO_SCALE);
+    attribute = pango_attr_letter_spacing_new (_rsvg_css_normalize_length (&state->letter_spacing,
+                                                                           ctx, 'h') * PANGO_SCALE);
     attribute->start_index = 0;
     attribute->end_index = G_MAXINT;
     pango_attr_list_insert (attr_list, attribute); 
@@ -575,8 +567,7 @@ rsvg_text_create_layout (RsvgDrawingCtx * ctx,
     else
         pango_layout_set_text (layout, NULL, 0);
 
-    pango_layout_set_alignment (layout, (state->text_dir == PANGO_DIRECTION_LTR ||
-                                         state->text_dir == PANGO_DIRECTION_TTB_LTR) ?
+    pango_layout_set_alignment (layout, (state->text_dir == PANGO_DIRECTION_LTR) ?
                                 PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT);
 
     return layout;
@@ -589,7 +580,7 @@ rsvg_text_layout_new (RsvgDrawingCtx * ctx, RsvgState * state, const char *text)
     RsvgTextLayout *layout;
 
     if (ctx->pango_context == NULL)
-        ctx->pango_context = rsvg_text_get_pango_context (ctx);
+        ctx->pango_context = ctx->render->create_pango_context (ctx);
 
     layout = g_new0 (RsvgTextLayout, 1);
 
@@ -601,350 +592,44 @@ rsvg_text_layout_new (RsvgDrawingCtx * ctx, RsvgState * state, const char *text)
     return layout;
 }
 
-#ifdef CAIRO_HAS_FT_FONT
-static FT_Int32
-rsvg_text_layout_render_flags (RsvgTextLayout * layout)
-{
-    gint flags = 0;
-
-    flags |= FT_LOAD_NO_BITMAP;
-    flags |= FT_LOAD_TARGET_MONO;
-    flags |= FT_LOAD_NO_HINTING;
-
-    return flags;
-}
-
-static void
-rsvg_text_vector_coords (RenderCtx * ctx, const FT_Vector * vector, gdouble * x, gdouble * y)
-{
-    *x = ctx->offset_x + (double) vector->x / 64;
-    *y = ctx->offset_y - (double) vector->y / 64;
-}
-
-static gint
-moveto (const FT_Vector * to, gpointer data)
-{
-    RenderCtx *ctx;
-    gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
-    gdouble x, y;
-
-    ctx = (RenderCtx *) data;
-
-    if (ctx->wrote)
-        g_string_append (ctx->path, "Z ");
-    else
-        ctx->wrote = TRUE;
-
-    g_string_append_c (ctx->path, 'M');
-
-    rsvg_text_vector_coords (ctx, to, &x, &y);
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (ctx->path, ',');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), y));
-    g_string_append_c (ctx->path, ' ');
-
-    return 0;
-}
-
-static gint
-lineto (const FT_Vector * to, gpointer data)
-{
-    RenderCtx *ctx;
-    gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
-    gdouble x, y;
-
-    ctx = (RenderCtx *) data;
-
-    if (!ctx->wrote)
-        return 0;
-
-    g_string_append_c (ctx->path, 'L');
-
-    rsvg_text_vector_coords (ctx, to, &x, &y);
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (ctx->path, ',');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), y));
-    g_string_append_c (ctx->path, ' ');
-
-    return 0;
-}
-
-static gint
-conicto (const FT_Vector * ftcontrol, const FT_Vector * to, gpointer data)
-{
-    RenderCtx *ctx;
-    gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
-    gdouble x, y;
-
-    ctx = (RenderCtx *) data;
-
-    if (!ctx->wrote)
-        return 0;
-
-    g_string_append_c (ctx->path, 'Q');
-
-    rsvg_text_vector_coords (ctx, ftcontrol, &x, &y);
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (ctx->path, ',');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), y));
-
-    rsvg_text_vector_coords (ctx, to, &x, &y);
-    g_string_append_c (ctx->path, ' ');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (ctx->path, ',');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), y));
-    g_string_append_c (ctx->path, ' ');
-
-    return 0;
-}
-
-static gint
-cubicto (const FT_Vector * ftcontrol1,
-         const FT_Vector * ftcontrol2, const FT_Vector * to, gpointer data)
-{
-    RenderCtx *ctx;
-    gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
-    gdouble x, y;
-
-    ctx = (RenderCtx *) data;
-
-    if (!ctx->wrote)
-        return 0;
-
-    g_string_append_c (ctx->path, 'C');
-
-    rsvg_text_vector_coords (ctx, ftcontrol1, &x, &y);
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (ctx->path, ',');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), y));
-
-    rsvg_text_vector_coords (ctx, ftcontrol2, &x, &y);
-    g_string_append_c (ctx->path, ' ');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (ctx->path, ',');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), y));
-
-    rsvg_text_vector_coords (ctx, to, &x, &y);
-    g_string_append_c (ctx->path, ' ');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), x));
-    g_string_append_c (ctx->path, ',');
-    g_string_append (ctx->path, g_ascii_dtostr (buf, sizeof (buf), y));
-    g_string_append_c (ctx->path, ' ');
-
-    return 0;
-}
-
-static gint
-rsvg_text_layout_render_glyphs (RsvgTextLayout * layout,
-                                PangoFont * font,
-                                PangoGlyphString * glyphs,
-                                RsvgTextRenderFunc render_func,
-                                gint x, gint y, gpointer render_data)
-{
-    PangoGlyphInfo *gi;
-    FT_Int32 flags;
-    FT_Vector pos;
-    gint i;
-    gint x_position = 0;
-
-    flags = rsvg_text_layout_render_flags (layout);
-
-    for (i = 0, gi = glyphs->glyphs; i < glyphs->num_glyphs; i++, gi++) {
-        if (gi->glyph) {
-            pos.x = x + x_position + gi->geometry.x_offset;
-            pos.y = y + gi->geometry.y_offset;
-
-            render_func (font, gi->glyph, flags, pos.x, pos.y, render_data);
-        }
-
-        x_position += glyphs->glyphs[i].geometry.width;
-    }
-    return x_position;
-}
-
-static void
-rsvg_text_render_vectors (PangoFont * font,
-                          PangoGlyph pango_glyph, FT_Int32 flags, gint x, gint y, gpointer ud)
-{
-    static const FT_Outline_Funcs outline_funcs = {
-        (FT_Outline_MoveToFunc) moveto,
-        (FT_Outline_LineToFunc) lineto,
-        (FT_Outline_ConicToFunc) conicto,
-        (FT_Outline_CubicToFunc) cubicto,
-        0,
-        0
-    };
-
-    FT_Face face;
-    FT_Glyph glyph;
-
-    RenderCtx *context = (RenderCtx *) ud;
-
-    face = pango_ft2_font_get_face (font);
-
-    if (0 != FT_Load_Glyph (face, (FT_UInt) pango_glyph, flags))
-		return;
-
-    if (0 != FT_Get_Glyph (face->glyph, &glyph))
-		return;
-
-    if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
-        FT_OutlineGlyph outline_glyph = (FT_OutlineGlyph) glyph;
-
-        context->offset_x = (gdouble) x / PANGO_SCALE;
-        context->offset_y = (gdouble) y / PANGO_SCALE - (int) face->size->metrics.ascender / 64;
-
-        FT_Outline_Decompose (&outline_glyph->outline, &outline_funcs, context);
-    }
-
-    FT_Done_Glyph (glyph);
-}
-#else 
-static gint
-rsvg_text_layout_render_glyphs (RsvgTextLayout * layout,
-                                PangoFont * font,
-                                PangoGlyphString * glyphs,
-                                RsvgTextRenderFunc render_func,
-                                gint x, gint y, gpointer render_data)
-{
-    PangoGlyphInfo *gi;
-    gint i;
-    gint x_position = 0;
-    gint pos_x, pos_y;
-    
-    for (i = 0, gi = glyphs->glyphs; i < glyphs->num_glyphs; i++, gi++) {
-        if (gi->glyph) {
-            pos_x = x + x_position + gi->geometry.x_offset;
-            pos_y = y + gi->geometry.y_offset;
-
-            render_func (font, gi->glyph, pos_x, pos_y, render_data);
-        }
-
-        x_position += glyphs->glyphs[i].geometry.width;
-    }
-
-    return x_position;
-}
-
-static void
-rsvg_text_render_vectors (PangoFont * font,
-                          PangoGlyph pango_glyph, gint x, gint y, gpointer ud)
-{
-}
-#endif /* CAIRO_HAS_FT_FONT */
-
-static void
-rsvg_text_layout_render_line (RsvgTextLayout * layout,
-                              PangoLayoutLine * line,
-                              RsvgTextRenderFunc render_func, gint x, gint y, gpointer render_data)
-{
-    GSList *list;
-    gint x_off = 0;
-
-    for (list = line->runs; list; list = list->next) {
-        PangoLayoutRun *run = list->data;
-
-        x_off += rsvg_text_layout_render_glyphs (layout,
-                                                 run->item->analysis.font, run->glyphs,
-                                                 render_func, x + x_off, y, render_data);
-
-    }
-}
-
-static void
-rsvg_text_layout_render (RsvgTextLayout * layout,
-                         RsvgTextRenderFunc render_func, gpointer render_data)
-{
-    PangoLayoutIter *iter;
-    gint x, y;
-
-    x = layout->x;
-    y = layout->y;
-
-    x *= PANGO_SCALE;
-    y *= PANGO_SCALE;
-
-    iter = pango_layout_get_iter (layout->layout);
-
-    if (iter) {
-        PangoRectangle logical;
-        PangoLayoutLine *line;
-        gint baseline;
-
-        line = pango_layout_iter_get_line (iter);
-
-        pango_layout_iter_get_line_extents (iter, NULL, &logical);
-        baseline = pango_layout_iter_get_baseline (iter);
-
-        rsvg_text_layout_render_line (layout, line,
-                                      render_func, x, y + baseline, render_data);
-
-        layout->x += logical.width / (double)PANGO_SCALE;
-    }
-
-    pango_layout_iter_free (iter);
-}
-
-static GString *
-rsvg_text_render_text_as_string (RsvgDrawingCtx * ctx, const char *text, gdouble * x, gdouble * y)
-{
-    RsvgTextLayout *layout;
-    RenderCtx *render;
-    RsvgState *state;
-    GString *output;
-    state = rsvg_state_current (ctx);
-
-    state->fill_rule = FILL_RULE_EVENODD;
-    state->has_fill_rule = TRUE;
-
-    layout = rsvg_text_layout_new (ctx, state, text);
-    layout->x = *x;
-    layout->y = *y;
-    layout->orientation = rsvg_state_current (ctx)->text_dir == PANGO_DIRECTION_TTB_LTR ||
-        rsvg_state_current (ctx)->text_dir == PANGO_DIRECTION_TTB_RTL;
-    render = rsvg_render_ctx_new ();
-
-    rsvg_text_layout_render (layout, rsvg_text_render_vectors, (gpointer) render);
-
-    if (render->wrote)
-        g_string_append_c (render->path, 'Z');
-
-    *x = layout->x;
-    *y = layout->y;
-
-    output = g_string_new (render->path->str);
-    rsvg_render_ctx_free (render);
-    rsvg_text_layout_free (layout);
-    return output;
-}
-
 void
 rsvg_text_render_text (RsvgDrawingCtx * ctx, const char *text, gdouble * x, gdouble * y)
 {
-    if (ctx->render->create_pango_context && ctx->render->render_pango_layout) {
-        PangoContext *context;
-        PangoLayout *layout;
-        PangoLayoutIter *iter;
-        RsvgState *state;
-        gint w, h, baseline;
-
-        state = rsvg_state_current (ctx);
-        context = ctx->render->create_pango_context (ctx);
-        layout = rsvg_text_create_layout (ctx, state, text, context);
-        pango_layout_get_size (layout, &w, &h);
-        iter = pango_layout_get_iter (layout);
-        baseline = pango_layout_iter_get_baseline (iter) / (double)PANGO_SCALE;
-        pango_layout_iter_free (iter);
-        ctx->render->render_pango_layout (ctx, layout, *x, *y - baseline);
-        *x += w / (double)PANGO_SCALE;
-        g_object_unref (layout);
-        g_object_unref (context);
+    PangoContext *context;
+    PangoLayout *layout;
+    PangoLayoutIter *iter;
+    RsvgState *state;
+    gint w, h;
+    double offset_x, offset_y, offset;
+
+    state = rsvg_current_state (ctx);
+
+    /* Do not render the text if the font size is zero. See bug #581491. */
+    if (state->font_size.length == 0)
+        return;
+
+    context = ctx->render->create_pango_context (ctx);
+    layout = rsvg_text_create_layout (ctx, state, text, context);
+    pango_layout_get_size (layout, &w, &h);
+    iter = pango_layout_get_iter (layout);
+    offset = pango_layout_iter_get_baseline (iter) / (double) PANGO_SCALE;
+    offset += _rsvg_css_accumulate_baseline_shift (state, ctx);
+    if (PANGO_GRAVITY_IS_VERTICAL (state->text_gravity)) {
+        offset_x = -offset;
+        offset_y = 0;
     } else {
-        GString *render;
-        render = rsvg_text_render_text_as_string (ctx, text, x, y);
-        rsvg_render_path (ctx, render->str);
-        g_string_free (render, TRUE);
+        offset_x = 0;
+        offset_y = offset;
     }
+    pango_layout_iter_free (iter);
+    ctx->render->render_pango_layout (ctx, layout, *x - offset_x, *y - offset_y);
+    if (PANGO_GRAVITY_IS_VERTICAL (state->text_gravity))
+        *y += w / (double)PANGO_SCALE;
+    else
+        *x += w / (double)PANGO_SCALE;
+
+    g_object_unref (layout);
+    g_object_unref (context);
 }
 
 static gdouble
@@ -963,10 +648,8 @@ rsvg_text_length_text_as_string (RsvgDrawingCtx * ctx, const char *text)
     RsvgTextLayout *layout;
     gdouble x;
 
-    layout = rsvg_text_layout_new (ctx, rsvg_state_current (ctx), text);
+    layout = rsvg_text_layout_new (ctx, rsvg_current_state (ctx), text);
     layout->x = layout->y = 0;
-    layout->orientation = rsvg_state_current (ctx)->text_dir == PANGO_DIRECTION_TTB_LTR ||
-        rsvg_state_current (ctx)->text_dir == PANGO_DIRECTION_TTB_RTL;
 
     x = rsvg_text_layout_width (layout);
 
diff --git a/rsvg-text.h b/rsvg-text.h
index 7cb0890..185f8ba 100644
--- a/rsvg-text.h
+++ b/rsvg-text.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg-text.h: Text handling routines for RSVG
 
@@ -31,9 +32,13 @@
 
 G_BEGIN_DECLS 
 
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_text	    (void);
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_tspan	    (void);
+G_GNUC_INTERNAL
 RsvgNode    *rsvg_new_tref	    (void);
+G_GNUC_INTERNAL
 char	    *rsvg_make_valid_utf8   (const char *str, int len);
 
 G_END_DECLS
diff --git a/rsvg-xml.c b/rsvg-xml.c
new file mode 100644
index 0000000..31f7ef0
--- /dev/null
+++ b/rsvg-xml.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright © 2010 Christian Persch
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+
+#include "rsvg-xml.h"
+
+typedef struct {
+    GInputStream *stream;
+    GCancellable *cancellable;
+    GError      **error;
+} RsvgXmlInputStreamContext;
+
+/* this should use gsize, but libxml2 is borked */
+static int
+context_read (RsvgXmlInputStreamContext *context,
+              char *buffer,
+              int   len)
+{
+    gssize n_read;
+
+    if (*(context->error))
+        return -1;
+
+    n_read = g_input_stream_read (context->stream, buffer, (gsize) len,
+                                  context->cancellable,
+                                  context->error);
+    if (n_read < 0)
+        return -1;
+
+    return (int) n_read;
+}
+
+static int
+context_close (RsvgXmlInputStreamContext *context)
+{
+    gboolean ret;
+
+    /* Don't overwrite a previous error */
+    ret = g_input_stream_close (context->stream, context->cancellable,
+                                *(context->error) == NULL ? context->error : NULL);
+
+    g_object_unref (context->stream);
+    if (context->cancellable)
+        g_object_unref (context->cancellable);
+    g_slice_free (RsvgXmlInputStreamContext, context);
+
+    return ret ? 0 : -1;
+}
+
+/**
+ * _rsvg_xml_input_buffer_new_from_stream:
+ * @context: a #xmlParserCtxtPtr
+ * @input_stream: a #GInputStream
+ *
+ * Returns: a new #xmlParserInputPtr wrapping @input_stream
+ */
+xmlParserInputBufferPtr
+_rsvg_xml_input_buffer_new_from_stream (GInputStream   *stream,
+                                        GCancellable   *cancellable,
+                                        xmlCharEncoding enc,
+                                        GError        **error)
+
+{
+    RsvgXmlInputStreamContext *context;
+
+    g_return_val_if_fail (G_IS_INPUT_STREAM (stream), NULL);
+    g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+    g_return_val_if_fail (error != NULL, NULL);
+
+    context = g_slice_new (RsvgXmlInputStreamContext);
+    context->stream = g_object_ref (stream);
+    context->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+    context->error = error;
+
+    return xmlParserInputBufferCreateIO ((xmlInputReadCallback) context_read,
+                                         (xmlInputCloseCallback) context_close,
+                                         context,
+                                         enc);
+}
diff --git a/rsvg-xml.h b/rsvg-xml.h
new file mode 100644
index 0000000..95158d8
--- /dev/null
+++ b/rsvg-xml.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2010 Christian Persch
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+  
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+*/
+
+#ifndef RSVG_XML_H
+#define RSVG_XML_H
+
+#include <libxml/xmlIO.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+xmlParserInputBufferPtr _rsvg_xml_input_buffer_new_from_stream (GInputStream   *stream,
+                                                                GCancellable   *cancellable,
+                                                                xmlCharEncoding enc,
+                                                                GError        **error);
+
+G_END_DECLS
+
+#endif /* !RSVG_XML_H */
diff --git a/rsvg.c b/rsvg.c
index 2f7c71c..31c4baf 100644
--- a/rsvg.c
+++ b/rsvg.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
    rsvg.c: SAX-based renderer for SVG files into a GdkPixbuf.
 
@@ -43,26 +44,20 @@
 #include "rsvg-cairo.h"
 #include "rsvg-cairo-draw.h"
 
-static void
-rsvg_pixmap_destroy (gchar * pixels, gpointer data)
-{
-    g_free (pixels);
-}
-
 /**
  * rsvg_handle_get_pixbuf_sub:
  * @handle: An #RsvgHandle
- * @id: The id of an element inside the SVG, or %NULL to render the whole SVG. For
- * example, if you have a layer called "layer1" that you wish to render, pass 
- * "##layer1" as the id.
+ * @id: (nullable): The id of an element inside the SVG, or %NULL to
+ * render the whole SVG. For example, if you have a layer called
+ * "layer1" that you wish to render, pass "##layer1" as the id.
  *
- * Returns the pixbuf loaded by #handle.  The pixbuf returned will be reffed, so
+ * Returns the pixbuf loaded by @handle.  The pixbuf returned will be reffed, so
  * the caller of this function must assume that ref.  If insufficient data has
  * been read to create the pixbuf, or an error occurred in loading, then %NULL
  * will be returned.  Note that the pixbuf may not be complete until
  * @rsvg_handle_close has been called.
  *
- * Returns: the pixbuf loaded by #handle, or %NULL.
+ * Returns: (transfer full) (nullable): the pixbuf loaded by @handle, or %NULL.
  *
  * Since: 2.14
  **/
@@ -71,10 +66,8 @@ rsvg_handle_get_pixbuf_sub (RsvgHandle * handle, const char *id)
 {
     RsvgDimensionData dimensions;
     GdkPixbuf *output = NULL;
-    guint8 *pixels;
     cairo_surface_t *surface;
     cairo_t *cr;
-    int rowstride;
 
     g_return_val_if_fail (handle != NULL, NULL);
 
@@ -85,36 +78,26 @@ rsvg_handle_get_pixbuf_sub (RsvgHandle * handle, const char *id)
     if (!(dimensions.width && dimensions.height))
         return NULL;
 
-    rowstride = dimensions.width * 4;
-
-    pixels = g_try_malloc0 (dimensions.width * dimensions.height * 4);
-    if (!pixels)
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                          dimensions.width, dimensions.height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
         return NULL;
+    }
 
-    surface = cairo_image_surface_create_for_data (pixels,
-                                                   CAIRO_FORMAT_ARGB32,
-                                                   dimensions.width, dimensions.height, rowstride);
     cr = cairo_create (surface);
-    cairo_surface_destroy (surface);
 
-    if (rsvg_handle_render_cairo_sub (handle, cr, id)) {
-		rsvg_cairo_to_pixbuf (pixels, rowstride, dimensions.height);
-
-		output = gdk_pixbuf_new_from_data (pixels,
-										   GDK_COLORSPACE_RGB,
-										   TRUE,
-										   8,
-										   dimensions.width,
-										   dimensions.height,
-										   rowstride,
-										   (GdkPixbufDestroyNotify) rsvg_pixmap_destroy, NULL);
-	} else {
-		g_free (pixels);
-		output = NULL;
-	}
+    if (!rsvg_handle_render_cairo_sub (handle, cr, id)) {
+        cairo_destroy (cr);
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
 
     cairo_destroy (cr);
 
+    output = rsvg_cairo_surface_to_pixbuf (surface);
+    cairo_surface_destroy (surface);
+
     return output;
 }
 
@@ -122,13 +105,13 @@ rsvg_handle_get_pixbuf_sub (RsvgHandle * handle, const char *id)
  * rsvg_handle_get_pixbuf:
  * @handle: An #RsvgHandle
  *
- * Returns the pixbuf loaded by #handle.  The pixbuf returned will be reffed, so
+ * Returns the pixbuf loaded by @handle.  The pixbuf returned will be reffed, so
  * the caller of this function must assume that ref.  If insufficient data has
  * been read to create the pixbuf, or an error occurred in loading, then %NULL
  * will be returned.  Note that the pixbuf may not be complete until
  * @rsvg_handle_close has been called.
  *
- * Returns: the pixbuf loaded by #handle, or %NULL.
+ * Returns: (transfer full) (nullable): the pixbuf loaded by @handle, or %NULL.
  **/
 GdkPixbuf *
 rsvg_handle_get_pixbuf (RsvgHandle * handle)
diff --git a/rsvg.h b/rsvg.h
index b1fa906..25b775a 100644
--- a/rsvg.h
+++ b/rsvg.h
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /* 
    rsvg.h: SAX-based renderer for SVG files into a GdkPixbuf.
  
@@ -25,10 +26,23 @@
 #ifndef RSVG_H
 #define RSVG_H
 
+#define __RSVG_RSVG_H_INSIDE__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
 G_BEGIN_DECLS
 
+#if defined(RSVG_DISABLE_DEPRECATION_WARNINGS) || !GLIB_CHECK_VERSION (2, 31, 0)
+#define RSVG_DEPRECATED
+#define RSVG_DEPRECATED_FOR(f)
+#else
+#define RSVG_DEPRECATED G_DEPRECATED
+#define RSVG_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f)
+#endif
+
 #define RSVG_TYPE_HANDLE                  (rsvg_handle_get_type ())
 #define RSVG_HANDLE(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSVG_TYPE_HANDLE, RsvgHandle))
 #define RSVG_HANDLE_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), RSVG_TYPE_HANDLE, RsvgHandleClass))
@@ -39,20 +53,22 @@ G_BEGIN_DECLS
 GType rsvg_handle_get_type (void);
 
 /**
- * An enumeration representing possible error domains
+ * RsvgError:
+ * @RSVG_ERROR_FAILED: the request failed
+ *
+ * An enumeration representing possible errors
  */
 typedef enum {
     RSVG_ERROR_FAILED
 } RsvgError;
 
-/**
- * 
- */
 #define RSVG_ERROR (rsvg_error_quark ())
 GQuark rsvg_error_quark (void) G_GNUC_CONST;
 
 /**
- * The RsvgHandle is an object representing the parsed form of a SVG
+ * RsvgHandle:
+ *
+ * The #RsvgHandle is an object representing the parsed form of a SVG
  */
 typedef struct _RsvgHandle RsvgHandle;
 typedef struct RsvgHandlePrivate RsvgHandlePrivate;
@@ -60,54 +76,56 @@ typedef struct _RsvgHandleClass RsvgHandleClass;
 typedef struct _RsvgDimensionData RsvgDimensionData;
 typedef struct _RsvgPositionData RsvgPositionData;
 
+/**
+ * RsvgHandleClass:
+ * @parent: parent class
+ *
+ * Class structure for #RsvgHandle
+ */
 struct _RsvgHandleClass {
     GObjectClass parent;
 
+    /*< private >*/
     gpointer _abi_padding[15];
 };
 
 struct _RsvgHandle {
     GObject parent;
 
+    /*< private >*/
+
     RsvgHandlePrivate *priv;
 
     gpointer _abi_padding[15];
 };
 
-/* RsvgDimensionData
+/**
+ * RsvgDimensionData:
+ * @width: SVG's width, in pixels
+ * @height: SVG's height, in pixels
+ * @em: em
+ * @ex: ex
  */
 struct _RsvgDimensionData {
-        /**
-	 * SVG's width, in pixels
-	 */
     int width;
-
-        /**
-	 * SVG's height, in pixels
-	 */
     int height;
-
-        /**
-	 * em
-	 */
     gdouble em;
-
-        /**
-	 * ex
-	 */
     gdouble ex;
 };
 
 /**
+ * RsvgPositionData:
+ * @x: position on the x axis
+ * @y: position on the y axis
+ *
  * Position of an SVG fragment.
- **/
+ */
 struct _RsvgPositionData {
     int x;
     int y;
 };
 
-void rsvg_init (void);
-void rsvg_term (void);
+void rsvg_cleanup (void);
 
 void rsvg_set_default_dpi	(double dpi);
 void rsvg_set_default_dpi_x_y	(double dpi_x, double dpi_y);
@@ -117,13 +135,13 @@ void rsvg_handle_set_dpi_x_y	(RsvgHandle * handle, double dpi_x, double dpi_y);
 
 RsvgHandle  *rsvg_handle_new		(void);
 gboolean     rsvg_handle_write		(RsvgHandle * handle, const guchar * buf, 
-					 gsize count, GError ** error);
+                                     gsize count, GError ** error);
 gboolean     rsvg_handle_close		(RsvgHandle * handle, GError ** error);
 GdkPixbuf   *rsvg_handle_get_pixbuf	(RsvgHandle * handle);
 GdkPixbuf   *rsvg_handle_get_pixbuf_sub (RsvgHandle * handle, const char *id);
 
-G_CONST_RETURN char	*rsvg_handle_get_base_uri (RsvgHandle * handle);
-void			 rsvg_handle_set_base_uri (RsvgHandle * handle, const char *base_uri);
+const char  *rsvg_handle_get_base_uri (RsvgHandle * handle);
+void         rsvg_handle_set_base_uri (RsvgHandle * handle, const char *base_uri);
 
 void rsvg_handle_get_dimensions (RsvgHandle * handle, RsvgDimensionData * dimension_data);
 
@@ -132,53 +150,116 @@ gboolean rsvg_handle_get_position_sub (RsvgHandle * handle, RsvgPositionData * p
 
 gboolean rsvg_handle_has_sub (RsvgHandle * handle, const char *id);
 
-/* Accessibility API */
+/* GIO APIs */
 
-G_CONST_RETURN char *rsvg_handle_get_title	(RsvgHandle * handle);
-G_CONST_RETURN char *rsvg_handle_get_desc	(RsvgHandle * handle);
-G_CONST_RETURN char *rsvg_handle_get_metadata	(RsvgHandle * handle);
+/**
+ * RsvgHandleFlags:
+ * @RSVG_HANDLE_FLAGS_NONE: none
+ * @RSVG_HANDLE_FLAG_UNLIMITED: Allow any SVG XML without size limitations.
+ *   For security reasons, this should only be used for trusted input!
+ *   Since: 2.40.3
+ * @RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA: Keeps the image data when loading images,
+ *  for use by cairo when painting to e.g. a PDF surface. This will make the
+ *  resulting PDF file smaller and faster.
+ *  Since: 2.40.3
+ */
+typedef enum /*< flags >*/ 
+{
+    RSVG_HANDLE_FLAGS_NONE           = 0,
+    RSVG_HANDLE_FLAG_UNLIMITED       = 1 << 0,
+    RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA = 1 << 1
+} RsvgHandleFlags;
+
+RsvgHandle *rsvg_handle_new_with_flags (RsvgHandleFlags flags);
+
+void        rsvg_handle_set_base_gfile (RsvgHandle *handle,
+                                        GFile      *base_file);
+
+gboolean    rsvg_handle_read_stream_sync (RsvgHandle   *handle,
+                                          GInputStream *stream,
+                                          GCancellable *cancellable,
+                                          GError      **error);
+
+RsvgHandle *rsvg_handle_new_from_gfile_sync (GFile          *file,
+                                             RsvgHandleFlags flags,
+                                             GCancellable   *cancellable,
+                                             GError        **error);
+
+RsvgHandle *rsvg_handle_new_from_stream_sync (GInputStream   *input_stream,
+                                              GFile          *base_file,
+                                              RsvgHandleFlags flags,
+                                              GCancellable   *cancellable,
+                                              GError        **error);
 
 RsvgHandle *rsvg_handle_new_from_data (const guint8 * data, gsize data_len, GError ** error);
 RsvgHandle *rsvg_handle_new_from_file (const gchar * file_name, GError ** error);
 
-#ifndef RSVG_DISABLE_DEPRECATED
+/* BEGIN deprecated APIs. Do not use! */
+
+#ifndef __GI_SCANNER__
+
+RSVG_DEPRECATED_FOR(g_type_init)
+void rsvg_init (void);
+RSVG_DEPRECATED
+void rsvg_term (void);
 
+RSVG_DEPRECATED_FOR(g_object_unref)
 void rsvg_handle_free (RsvgHandle * handle);
 
 /**
- * RsvgSizeFunc ():
- * @width: Pointer to where to set/store the width
- * @height: Pointer to where to set/store the height
- * @user_data: User data pointer
+ * RsvgSizeFunc:
+ * @width: (out): the width of the SVG
+ * @height: (out): the height of the SVG
+ * @user_data: user data
  *
  * Function to let a user of the library specify the SVG's dimensions
- * @width: the ouput width the SVG should be
- * @height: the output height the SVG should be
- * @user_data: user data
  *
  * Deprecated: Set up a cairo matrix and use rsvg_handle_render_cairo() instead.
+ * See the documentation for rsvg_handle_set_size_callback() for an example.
  */
-typedef void (*RsvgSizeFunc) (gint * width, gint * height, gpointer user_data);
+typedef /* RSVG_DEPRECATED */ void (*RsvgSizeFunc) (gint * width, gint * height, gpointer user_data);
+
+RSVG_DEPRECATED
 void rsvg_handle_set_size_callback (RsvgHandle * handle,
-				    RsvgSizeFunc size_func,
-				    gpointer user_data, GDestroyNotify user_data_destroy);
+                                    RsvgSizeFunc size_func,
+                                    gpointer user_data, GDestroyNotify user_data_destroy);
 
 /* GdkPixbuf convenience API */
 
-GdkPixbuf *rsvg_pixbuf_from_file		(const gchar * file_name, GError ** error);
-GdkPixbuf *rsvg_pixbuf_from_file_at_zoom	(const gchar * file_name,
-						 double x_zoom, double y_zoom, GError ** error);
-GdkPixbuf *rsvg_pixbuf_from_file_at_size	(const gchar * file_name, gint width, gint height, 
-						 GError ** error);
-GdkPixbuf *rsvg_pixbuf_from_file_at_max_size	(const gchar * file_name,
-						 gint max_width, gint max_height, GError ** error);
+RSVG_DEPRECATED
+GdkPixbuf *rsvg_pixbuf_from_file            (const gchar * file_name, GError ** error);
+RSVG_DEPRECATED
+GdkPixbuf *rsvg_pixbuf_from_file_at_zoom    (const gchar * file_name,
+                                             double x_zoom, double y_zoom, GError ** error);
+RSVG_DEPRECATED
+GdkPixbuf *rsvg_pixbuf_from_file_at_size    (const gchar * file_name, gint width, gint height,
+                                             GError ** error);
+RSVG_DEPRECATED
+GdkPixbuf *rsvg_pixbuf_from_file_at_max_size    (const gchar * file_name,
+                                                 gint max_width, gint max_height, GError ** error);
+RSVG_DEPRECATED
 GdkPixbuf *rsvg_pixbuf_from_file_at_zoom_with_max (const gchar * file_name,
-						   double x_zoom,
-						   double y_zoom,
-						   gint max_width, gint max_height, GError ** error);
+                                                   double x_zoom,
+                                                   double y_zoom,
+                                                   gint max_width, gint max_height, GError ** error);
 
-#endif                          /* RSVG_DISABLE_DEPRECATED */
+RSVG_DEPRECATED
+const char *rsvg_handle_get_title       (RsvgHandle * handle);
+RSVG_DEPRECATED
+const char *rsvg_handle_get_desc        (RsvgHandle * handle);
+RSVG_DEPRECATED
+const char *rsvg_handle_get_metadata    (RsvgHandle * handle);
+
+#endif /* !__GI_SCANNER__ */
+
+/* END deprecated APIs. */
 
 G_END_DECLS
 
+#include "librsvg-enum-types.h"
+#include "librsvg-features.h"
+#include "rsvg-cairo.h"
+
+#undef __RSVG_RSVG_H_INSIDE__
+
 #endif                          /* RSVG_H */
diff --git a/test-display.c b/test-display.c
index 3391188..3c6df24 100644
--- a/test-display.c
+++ b/test-display.c
@@ -1,4 +1,5 @@
-/* vim: set sw=4 sts=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 sts=4 ts=4 expandtab: */
 /*
  * test-display: 
  *
@@ -17,172 +18,499 @@
  */
 
 #include "config.h"
-#include "rsvg.h"
-#include "rsvg-cairo.h"
 #include "rsvg-private.h"
+#include "rsvg-size-callback.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <locale.h>
+#include <errno.h>
+#include <math.h>
 
 #include <gtk/gtk.h>
 #include <gdk/gdk.h>
 #include <gdk/gdkkeysyms.h>
 
-#ifdef HAVE_BASENAME
-#include <libgen.h>
+#include "rsvg-compat.h"
+
+#if 0 // defined (G_OS_UNIX)
+#include <gio/gunixinputstream.h>
 #endif
 
-#ifdef ENABLE_XEMBED
-#include <gdk/gdkx.h>
-#endif                          /* ENABLE_XEMBED */
+/* RGBA color parsing
+ *
+ * For pragmatic reasons (i.e. I'm lazy), the following code is copied over from
+ * GDK in GTK+ 3.x.  In GDK2 we don't have support for RGBA colors, so we do
+ * rendering of those by hand here.
+ */
 
-#define DEFAULT_WIDTH  640
-#define DEFAULT_HEIGHT 480
+typedef struct
+{
+  gdouble red;
+  gdouble green;
+  gdouble blue;
+  gdouble alpha;
+} MyGdkRGBA;
 
-static char *
-_rsvg_basename (const char *file)
+#define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
+
+/* Parses a single color component from a rgb() or rgba() specification
+ * according to CSS3 rules. Compared to exact CSS3 parsing we are liberal
+ * in what we accept as follows:
+ *
+ *  - For non-percentage values, we accept floats in the range 0-255
+ *    not just [0-9]+ integers
+ *  - For percentage values we accept any float, not just
+ *     [ 0-9]+ | [0-9]* “.” [0-9]+
+ *  - We accept mixed percentages and non-percentages in a single
+ *    rgb() or rgba() specification.
+ */
+static gboolean
+parse_rgb_value (const gchar  *str,
+                 gchar       **endp,
+                 gdouble      *number)
 {
-#ifdef HAVE_BASENAME
-    if (file && *file) {
-        char *file_dup = g_strdup (file);
-        return basename (file_dup);
+  const char *p;
+
+  *number = g_ascii_strtod (str, endp);
+  if (errno == ERANGE || *endp == str ||
+      isinf (*number) || isnan (*number))
+    return FALSE;
+
+  p = *endp;
+
+  SKIP_WHITESPACES (p);
+
+  if (*p == '%')
+    {
+      *endp = (char *)(p + 1);
+      *number = CLAMP(*number / 100., 0., 1.);
+    }
+  else
+    {
+      *number = CLAMP(*number / 255., 0., 1.);
     }
-#endif
 
-    return NULL;
+  return TRUE;
 }
 
-static GdkPixbuf *
-pixbuf_from_data_with_size_data (const guchar * buff,
-                                 size_t len,
-                                 struct RsvgSizeCallbackData *data,
-                                 const char *base_uri,
-                                 const char *id,
-                                 GError ** error)
+/**
+ * my_gdk_rgba_parse:
+ * @rgba: the #GdkRGBA to fill in
+ * @spec: the string specifying the color
+ *
+ * Parses a textual representation of a color, filling in
+ * the @red, @green, @blue and @alpha fields of the @rgba #GdkRGBA.
+ *
+ * The string can be either one of:
+ * - A standard name (Taken from the X11 rgb.txt file).
+ * - A hexadecimal value in the form “\#rgb”, “\#rrggbb”,
+ *   “\#rrrgggbbb” or ”\#rrrrggggbbbb”
+ * - A RGB color in the form “rgb(r,g,b)” (In this case the color will
+ *   have full opacity)
+ * - A RGBA color in the form “rgba(r,g,b,a)”
+ *
+ * Where “r”, “g”, “b” and “a” are respectively the red, green, blue and
+ * alpha color values. In the last two cases, r g and b are either integers
+ * in the range 0 to 255 or precentage values in the range 0% to 100%, and
+ * a is a floating point value in the range 0 to 1.
+ *
+ * Returns: %TRUE if the parsing succeeded
+ *
+ * Since: 3.0
+ */
+static gboolean
+my_gdk_rgba_parse (MyGdkRGBA   *rgba,
+				   const gchar *spec)
 {
-    RsvgHandle *handle;
-    GdkPixbuf *retval;
+  gboolean has_alpha;
+  gdouble r, g, b, a;
+  gchar *str = (gchar *) spec;
+  gchar *p;
 
-    handle = rsvg_handle_new ();
+  g_return_val_if_fail (spec != NULL, FALSE);
 
-    if (!handle) {
-        g_set_error (error, rsvg_error_quark (), 0, _("Error creating SVG reader"));
-        return NULL;
+
+  if (strncmp (str, "rgba", 4) == 0)
+    {
+      has_alpha = TRUE;
+      str += 4;
+    }
+  else if (strncmp (str, "rgb", 3) == 0)
+    {
+      has_alpha = FALSE;
+      a = 1;
+      str += 3;
+    }
+  else
+    {
+      PangoColor pango_color;
+
+      /* Resort on PangoColor for rgb.txt color
+       * map and '#' prefixed colors
+       */
+      if (pango_color_parse (&pango_color, str))
+        {
+          if (rgba)
+            {
+              rgba->red = pango_color.red / 65535.;
+              rgba->green = pango_color.green / 65535.;
+              rgba->blue = pango_color.blue / 65535.;
+              rgba->alpha = 1;
+            }
+
+          return TRUE;
+        }
+      else
+        return FALSE;
     }
 
-    rsvg_handle_set_size_callback (handle, _rsvg_size_callback, data, NULL);
-    rsvg_handle_set_base_uri (handle, base_uri);
+  SKIP_WHITESPACES (str);
 
-    if (!rsvg_handle_write (handle, buff, len, error)) {
-        g_object_unref (G_OBJECT (handle));
-        return NULL;
+  if (*str != '(')
+    return FALSE;
+
+  str++;
+
+  /* Parse red */
+  SKIP_WHITESPACES (str);
+  if (!parse_rgb_value (str, &str, &r))
+    return FALSE;
+  SKIP_WHITESPACES (str);
+
+  if (*str != ',')
+    return FALSE;
+
+  str++;
+
+  /* Parse green */
+  SKIP_WHITESPACES (str);
+  if (!parse_rgb_value (str, &str, &g))
+    return FALSE;
+  SKIP_WHITESPACES (str);
+
+  if (*str != ',')
+    return FALSE;
+
+  str++;
+
+  /* Parse blue */
+  SKIP_WHITESPACES (str);
+  if (!parse_rgb_value (str, &str, &b))
+    return FALSE;
+  SKIP_WHITESPACES (str);
+
+  if (has_alpha)
+    {
+      if (*str != ',')
+        return FALSE;
+
+      str++;
+
+      SKIP_WHITESPACES (str);
+      a = g_ascii_strtod (str, &p);
+      if (errno == ERANGE || p == str ||
+          isinf (a) || isnan (a))
+        return FALSE;
+      str = p;
+      SKIP_WHITESPACES (str);
     }
 
-    if (!rsvg_handle_close (handle, error)) {
-        g_object_unref (G_OBJECT (handle));
-        return NULL;
+  if (*str != ')')
+    return FALSE;
+
+  str++;
+
+  SKIP_WHITESPACES (str);
+
+  if (*str != '\0')
+    return FALSE;
+
+  if (rgba)
+    {
+      rgba->red = CLAMP (r, 0, 1);
+      rgba->green = CLAMP (g, 0, 1);
+      rgba->blue = CLAMP (b, 0, 1);
+      rgba->alpha = CLAMP (a, 0, 1);
     }
 
-    retval = rsvg_handle_get_pixbuf_sub (handle, id);
-    g_object_unref (G_OBJECT (handle));
+  return TRUE;
+}
+
+#undef SKIP_WHITESPACES
 
-    return retval;
+/* RsvgImage */
+
+#define RSVG_TYPE_IMAGE (rsvg_image_get_type ())
+#define RSVG_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSVG_TYPE_IMAGE, RsvgImage))
+
+typedef struct _RsvgImage       RsvgImage;
+typedef struct _RsvgImageClass  RsvgImageClass;
+
+struct _RsvgImage {
+    GtkWidget parent_instance;
+
+    cairo_surface_t *surface; /* a cairo image surface */
+	MyGdkRGBA bg_color;
+};
+
+struct _RsvgImageClass {
+    GtkWidgetClass parent_class;
+};
+
+static GType rsvg_image_get_type (void);
+
+static void
+rsvg_image_take_surface (RsvgImage *image,
+                         cairo_surface_t *surface)
+{
+    if (image->surface == surface)
+      return;
+    if (image->surface)
+      cairo_surface_destroy (image->surface);
+    image->surface = surface; /* adopted */
+
+    gtk_widget_queue_resize (GTK_WIDGET (image));
+}
+
+static void
+rsvg_image_set_bg_color (RsvgImage *image,
+						 MyGdkRGBA  color)
+{
+	image->bg_color = color;
+
+	gtk_widget_queue_draw (GTK_WIDGET (image));
+}
+
+G_DEFINE_TYPE (RsvgImage, rsvg_image, GTK_TYPE_WIDGET);
+
+static void
+rsvg_image_init (RsvgImage *image)
+{
+  gtk_widget_set_has_window (GTK_WIDGET (image), FALSE);
+  image->bg_color.red	= 1.0;
+  image->bg_color.green = 1.0;
+  image->bg_color.blue	= 1.0;
+  image->bg_color.alpha = 1.0;
+}
+
+static void
+rsvg_image_finalize (GObject *object)
+{
+  RsvgImage *image = RSVG_IMAGE (object);
+
+  rsvg_image_take_surface (image, NULL);
+}
+
+static void
+rsvg_image_size_request (GtkWidget      *widget,
+						 GtkRequisition *requisition)
+{
+	RsvgImage *image = RSVG_IMAGE (widget);
+
+	if (image->surface) {
+		requisition->width  = cairo_image_surface_get_width (image->surface);
+		requisition->height = cairo_image_surface_get_height (image->surface);
+	} else {
+		requisition->width  = 1;
+		requisition->height = 1;
+	}
+}
+
+static gboolean
+rsvg_image_expose_event (GtkWidget *widget,
+						 GdkEventExpose *event)
+{
+	RsvgImage *image = RSVG_IMAGE (widget);
+	cairo_surface_t *surf;
+	cairo_t *surf_cr;
+	cairo_t *gdk_cr;
+	int width, height;
+
+	if (image->surface == NULL)
+		return FALSE;
+
+	width  = cairo_image_surface_get_width (image->surface);
+	height = cairo_image_surface_get_height (image->surface);
+
+	surf = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
+	if (!surf)
+		return FALSE;
+
+	surf_cr = cairo_create (surf);
+	cairo_set_source_rgb (surf_cr,
+						  image->bg_color.red,
+						  image->bg_color.green,
+						  image->bg_color.blue); /* we drop alpha! */
+	cairo_paint (surf_cr);
+
+	cairo_set_source_surface (surf_cr, image->surface, 0.0, 0.0);
+	cairo_paint (surf_cr);
+	cairo_destroy (surf_cr);
+
+	gdk_cr = gdk_cairo_create (widget->window);
+	cairo_set_source_surface (gdk_cr, surf, 0, 0);
+	cairo_paint (gdk_cr);
+	cairo_destroy (gdk_cr);
+
+	cairo_surface_destroy (surf);
+
+	return TRUE;
+}
+
+static void
+rsvg_image_class_init (RsvgImageClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+	gobject_class->finalize = rsvg_image_finalize;
+	widget_class->size_request = rsvg_image_size_request;
+	widget_class->expose_event = rsvg_image_expose_event;
+}
+
+static RsvgImage *
+rsvg_image_new_take_surface (cairo_surface_t *surface)
+{
+  RsvgImage *image;
+
+  image = g_object_new (RSVG_TYPE_IMAGE, NULL);
+  rsvg_image_take_surface (image, surface);
+
+  return image;
+}
+
+static cairo_surface_t *
+rsvg_image_get_surface (RsvgImage *image)
+{
+  return image->surface;
+}
+
+/* Main */
+
+static char *
+_rsvg_basename (const char *file)
+{
+    if (file && *file)
+        return g_path_get_basename (file);
+
+    return NULL;
 }
 
 typedef struct _ViewerCbInfo ViewerCbInfo;
 struct _ViewerCbInfo {
     GtkWidget *window;
     GtkWidget *popup_menu;
-    GtkWidget *image;           /* the image widget */
-
-    GdkPixbuf *pixbuf;
-    GByteArray *svg_bytes;
+    RsvgImage *image;
+    RsvgHandle *handle;
     GtkAccelGroup *accel_group;
     char *base_uri;
     char *id;
+    RsvgDimensionData dimensions;
+    gdouble x_zoom;
+    gdouble y_zoom;
 };
 
-static void
-zoom_image (ViewerCbInfo * info, gint width, gint height)
+static cairo_surface_t *
+render_to_surface (ViewerCbInfo *info)
 {
-    struct RsvgSizeCallbackData size_data;
-    GdkPixbuf *save_pixbuf = info->pixbuf;
+    int width, height;
+    cairo_matrix_t matrix;
+    cairo_surface_t *surface;
+    cairo_t *cr;
 
-    size_data.type = RSVG_SIZE_WH;
-    size_data.width = width;
-    size_data.height = height;
-    size_data.keep_aspect_ratio = FALSE;
+    width = ceil ((double) info->dimensions.width * info->x_zoom);
+    height = ceil ((double) info->dimensions.height * info->y_zoom);
 
-    info->pixbuf =
-        pixbuf_from_data_with_size_data (info->svg_bytes->data, info->svg_bytes->len,
-                                         &size_data, info->base_uri, info->id, NULL);
-    gtk_image_set_from_pixbuf (GTK_IMAGE (info->image), info->pixbuf);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
 
-    if (save_pixbuf)
-        g_object_unref (G_OBJECT (save_pixbuf));
-}
+    cr = cairo_create (surface);
 
-static void
-zoom_in (GObject * ignored, ViewerCbInfo * info)
-{
-    if (!info->pixbuf)
-        return;
-    zoom_image (info, gdk_pixbuf_get_width (info->pixbuf) * 1.25,
-                gdk_pixbuf_get_height (info->pixbuf) * 1.25);
+    cairo_matrix_init_scale (&matrix, info->x_zoom, info->y_zoom);
+    cairo_transform (cr, &matrix);
+
+    if (!rsvg_handle_render_cairo_sub (info->handle, cr, info->id)) {
+        cairo_destroy (cr);
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
+
+    cairo_destroy (cr);
+
+    if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) {
+        g_printerr ("Error while rendering image: %d\n", cairo_surface_status (surface));
+        cairo_surface_destroy (surface);
+        return NULL;
+    }
+
+    return surface;
 }
 
 static void
-zoom_out (GObject * ignored, ViewerCbInfo * info)
+set_window_title (ViewerCbInfo * info)
 {
-    if (!info->pixbuf)
-        return;
-    zoom_image (info, gdk_pixbuf_get_width (info->pixbuf) / 1.25,
-                gdk_pixbuf_get_height (info->pixbuf) / 1.25);
+    char *title;
+    gchar *zoom_string;
+
+    if (info->x_zoom != info->y_zoom) {
+        zoom_string = g_strdup_printf ("%4d%% : %4d%%",
+                                       (gint) (info->x_zoom * 100),
+                                       (gint) (info->y_zoom * 100));
+    } else {
+        zoom_string = g_strdup_printf ("%4d%%",
+                                       (gint) (info->x_zoom * 100));
+    }
+
+    if (info->id) {
+        title = g_strdup_printf ("%s#%s (%s) — %s",
+                                 info->base_uri, info->id,
+                                 zoom_string,
+                                 _("SVG Viewer"));
+    } else {
+        title = g_strdup_printf ("%s (%s) — %s",
+                                 info->base_uri,
+                                 zoom_string,
+                                 _("SVG Viewer"));
+    }
+    gtk_window_set_title (GTK_WINDOW (info->window), title);
+    g_free (title);
+    g_free (zoom_string);
 }
 
 static void
-rsvg_window_set_default_icon (GtkWindow * window, GdkPixbuf * src)
+zoom_image (ViewerCbInfo * info, gdouble factor)
 {
-    GList *list;
-    GdkPixbuf *icon;
-    gint width, height;
-
-    width = gdk_pixbuf_get_width (src);
-    height = gdk_pixbuf_get_height (src);
-
-    if (width > 128 || height > 128) {
-        /* sending images greater than 128x128 has this nasty tendency to 
-           cause broken pipe errors X11 Servers */
-        if (width > height) {
-            width = 0.5 + width * 128. / height;
-            height = 128;
-        } else {
-            height = 0.5 + height * 128. / width;
-            width = 128;
-        }
+    info->x_zoom *= factor;
+    info->y_zoom *= factor;
 
-        icon = gdk_pixbuf_scale_simple (src, width, height, GDK_INTERP_BILINEAR);
-    } else {
-        icon = g_object_ref (G_OBJECT (src));
-    }
+    rsvg_image_take_surface (info->image, render_to_surface (info));
 
-    list = g_list_prepend (NULL, icon);
-    gtk_window_set_icon_list (window, list);
-    g_list_free (list);
+    set_window_title (info);
+}
 
-    g_object_unref (G_OBJECT (icon));
+static void
+zoom_in (GObject * ignored, ViewerCbInfo * info)
+{
+    zoom_image (info, sqrt (G_SQRT2));
 }
 
-#if GTK_CHECK_VERSION(2,10,0)
+static void
+zoom_out (GObject * ignored, ViewerCbInfo * info)
+{
+    zoom_image (info, 1. / sqrt (G_SQRT2));
+}
 
 static void
 begin_print (GtkPrintOperation *operation,
 			 GtkPrintContext   *context,
 			 gpointer           user_data)
 {
-	gtk_print_operation_set_n_pages(operation, 1);
+	gtk_print_operation_set_n_pages (operation, 1);
 }
 
 static void
@@ -192,55 +520,46 @@ draw_page (GtkPrintOperation *operation,
 		   gpointer           user_data)
 {
     ViewerCbInfo *info = (ViewerCbInfo *) user_data;
+    cairo_t *cr;
+    gdouble page_width, page_height, page_aspect;
+    gdouble width, height, aspect;
+    cairo_matrix_t matrix;
+
+    cr = gtk_print_context_get_cairo_context (context);
+    page_width = gtk_print_context_get_width (context);
+    page_height = gtk_print_context_get_height (context);
+    page_aspect = page_width / page_height;
+
+    // FIXMEchpe
+    rsvg_handle_set_dpi_x_y (info->handle, 
+                             gtk_print_context_get_dpi_x(context), 
+                             gtk_print_context_get_dpi_y(context));
+
+    width = info->dimensions.width;
+    height = info->dimensions.height;
+    aspect = width / height;
+
+    if (aspect <= page_aspect) {
+        width = page_height * aspect;
+        height = page_height;
+    } else {
+        width = page_width;
+        height = page_width / aspect;
+    }
 
-	cairo_t *cr;
-	gdouble page_width, page_height;
-  
-	cr = gtk_print_context_get_cairo_context (context);
-	page_width = gtk_print_context_get_width (context);
-	page_height = gtk_print_context_get_height (context);
-
-	{
-		RsvgHandle *handle;
-		RsvgDimensionData svg_dimensions;
-		struct RsvgSizeCallbackData size_data;
-
-		/* should not fail */
-		handle = rsvg_handle_new_from_data(info->svg_bytes->data, info->svg_bytes->len, NULL);
-		rsvg_handle_set_base_uri (handle, info->base_uri);
-		rsvg_handle_set_dpi_x_y (handle, gtk_print_context_get_dpi_x(context), 
-								 gtk_print_context_get_dpi_y(context));
-		rsvg_handle_get_dimensions(handle, &svg_dimensions);
-
-        if (svg_dimensions.width > page_width || svg_dimensions.height > page_height) {
-            /* scale down the image to the page's size, while preserving the aspect ratio */
-
-            if ((double) svg_dimensions.height * (double) page_width > (double) svg_dimensions.width * (double) page_height) {
-                svg_dimensions.width = 0.5 + (double) svg_dimensions.width *(double) page_height / (double) svg_dimensions.height;
-                svg_dimensions.height = page_height;
-            } else {
-                svg_dimensions.height = 0.5 + (double) svg_dimensions.height *(double) page_width / (double) svg_dimensions.width;
-                svg_dimensions.width = page_width;
-            }
-        }
-
-		size_data.type = RSVG_SIZE_WH;
-		size_data.width = svg_dimensions.width;
-		size_data.height = svg_dimensions.height;
-		size_data.keep_aspect_ratio = FALSE;
-		rsvg_handle_set_size_callback (handle, _rsvg_size_callback, &size_data, NULL);
-
-		rsvg_handle_render_cairo(handle, cr);
-
-		g_object_unref (handle);
-	}
+    cairo_save (cr);
+    cairo_matrix_init_scale (&matrix, 
+                             width / info->dimensions.width,
+                             height / info->dimensions.height);
+    cairo_transform (cr, &matrix);
+    rsvg_handle_render_cairo (info->handle, cr);
+    cairo_restore (cr);
 }
 
 static void
 print_pixbuf (GObject * ignored, gpointer user_data)
 {
   GtkPrintOperation *print;
-  GtkPrintOperationResult res;
   ViewerCbInfo *info = (ViewerCbInfo *) user_data;
 
   print = gtk_print_operation_new ();
@@ -248,14 +567,12 @@ print_pixbuf (GObject * ignored, gpointer user_data)
   g_signal_connect (print, "begin_print", G_CALLBACK (begin_print), info);
   g_signal_connect (print, "draw_page", G_CALLBACK (draw_page), info);
 
-  res = gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
+  (void)gtk_print_operation_run (print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
                                  GTK_WINDOW (info->window), NULL);
 
   g_object_unref (print);
 }
 
-#endif                          /* HAVE_GNOME_PRINT */
-
 static char *
 save_file (const char *title, const char *suggested_filename, GtkWidget * parent, int *success)
 {
@@ -266,8 +583,8 @@ save_file (const char *title, const char *suggested_filename, GtkWidget * parent
     dialog = gtk_file_chooser_dialog_new (title,
                                           GTK_WINDOW (parent),
                                           GTK_FILE_CHOOSER_ACTION_SAVE,
-                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                          GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
+                                          _("_Cancel"), GTK_RESPONSE_CANCEL,
+                                          _("_Save"), GTK_RESPONSE_ACCEPT, NULL);
 
     if (suggested_filename && *suggested_filename) {
         gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested_filename);
@@ -289,6 +606,7 @@ save_pixbuf (GObject * ignored, gpointer user_data)
     ViewerCbInfo *info = (ViewerCbInfo *) user_data;
     char *filename, *base_name, *filename_suggestion;
     int success = 0;
+    cairo_surface_t *surface;
 
     base_name = _rsvg_basename (info->base_uri);
     if (base_name)
@@ -301,92 +619,18 @@ save_pixbuf (GObject * ignored, gpointer user_data)
     g_free (filename_suggestion);
 
     if (filename) {
-        GError *err = NULL;
-
-        if (!gdk_pixbuf_save (info->pixbuf, filename, "png", &err, NULL)) {
-            if (err) {
+        surface = rsvg_image_get_surface (info->image);
+        if (cairo_surface_write_to_png (surface, filename) != CAIRO_STATUS_SUCCESS) {
                 GtkWidget *errmsg;
 
                 errmsg = gtk_message_dialog_new (GTK_WINDOW (info->window),
                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                                                  GTK_MESSAGE_WARNING,
-                                                 GTK_BUTTONS_CLOSE, "%s", err->message);
+                                                 GTK_BUTTONS_CLOSE, "Failed to save");
 
                 gtk_dialog_run (GTK_DIALOG (errmsg));
 
-                g_error_free (err);
                 gtk_widget_destroy (errmsg);
-            }
-        }
-
-        g_free (filename);
-    } else if (success) {
-        GtkWidget *errmsg;
-
-        errmsg = gtk_message_dialog_new (GTK_WINDOW (info->window),
-                                         GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-                                         GTK_MESSAGE_WARNING,
-                                         GTK_BUTTONS_CLOSE, _("No filename given"));
-        gtk_window_set_transient_for (GTK_WINDOW (errmsg), GTK_WINDOW (info->window));
-
-        gtk_dialog_run (GTK_DIALOG (errmsg));
-        gtk_widget_destroy (errmsg);
-    }
-}
-
-static void
-save_svg (GObject * ignored, gpointer user_data)
-{
-    ViewerCbInfo *info = (ViewerCbInfo *) user_data;
-    char *filename, *base_name;
-    int success = 0;
-
-    base_name = _rsvg_basename (info->base_uri);
-    filename = save_file (_("Save SVG"), base_name, info->window, &success);
-    g_free (base_name);
-
-    if (filename) {
-        FILE *fp;
-
-        /* todo: make this support gnome vfs */
-        fp = fopen (filename, "wb");
-        if (!fp) {
-            GtkWidget *errmsg;
-
-            errmsg = gtk_message_dialog_new (GTK_WINDOW (info->window),
-                                             GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
-                                             GTK_MESSAGE_WARNING,
-                                             GTK_BUTTONS_CLOSE, _("Couldn't save %s"), filename);
-            gtk_window_set_transient_for (GTK_WINDOW (errmsg), GTK_WINDOW (info->window));
-
-            gtk_dialog_run (GTK_DIALOG (errmsg));
-            gtk_widget_destroy (errmsg);
-        } else {
-            size_t written = 0, remaining = info->svg_bytes->len;
-            const unsigned char *buffer = info->svg_bytes->data;
-
-            while (remaining > 0) {
-                written = fwrite (buffer + (info->svg_bytes->len - remaining), 1, remaining, fp);
-                if ((written < remaining) && ferror (fp) != 0) {
-                    GtkWidget *errmsg;
-
-                    errmsg = gtk_message_dialog_new (GTK_WINDOW (info->window),
-                                                     GTK_DIALOG_MODAL |
-                                                     GTK_DIALOG_DESTROY_WITH_PARENT,
-                                                     GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
-                                                     _("Couldn't save %s"), filename);
-                    gtk_window_set_transient_for (GTK_WINDOW (errmsg), GTK_WINDOW (info->window));
-
-                    gtk_dialog_run (GTK_DIALOG (errmsg));
-                    gtk_widget_destroy (errmsg);
-
-                    break;
-                }
-
-                remaining -= written;
-            }
-
-            fclose (fp);
         }
 
         g_free (filename);
@@ -421,16 +665,12 @@ create_popup_menu (ViewerCbInfo * info)
 {
     GtkWidget *popup_menu;
     GtkWidget *menu_item;
-    GtkWidget *stock;
 
     popup_menu = gtk_menu_new ();
     gtk_menu_set_accel_group (GTK_MENU (popup_menu), info->accel_group);
 
     if (info->base_uri) {
-        menu_item = gtk_image_menu_item_new_with_label (_("Copy SVG location"));
-        stock = gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
-        gtk_widget_show (stock);
-        gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), stock);
+        menu_item = gtk_menu_item_new_with_label (_("Copy SVG location"));
         g_signal_connect (menu_item, "activate", G_CALLBACK (copy_svg_location), info);
         gtk_widget_show (menu_item);
         gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
@@ -438,40 +678,28 @@ create_popup_menu (ViewerCbInfo * info)
                                     GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
     }
 
-    menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE, NULL);
-    g_signal_connect (menu_item, "activate", G_CALLBACK (save_svg), info);
-    gtk_widget_show (menu_item);
-    gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
-    gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_S, GDK_CONTROL_MASK,
-                                GTK_ACCEL_VISIBLE);
-
-    menu_item = gtk_image_menu_item_new_with_label (_("Save as PNG"));
-    stock = gtk_image_new_from_stock (GTK_STOCK_SAVE_AS, GTK_ICON_SIZE_MENU);
-    gtk_widget_show (stock);
-    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), stock);
+    menu_item = gtk_menu_item_new_with_label (_("Save as PNG"));
     g_signal_connect (menu_item, "activate", G_CALLBACK (save_pixbuf), info);
     gtk_widget_show (menu_item);
     gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
     gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_S,
                                 GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_ACCEL_VISIBLE);
 
-#if GTK_CHECK_VERSION(2,10,0)
-    menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_PRINT, NULL);
+    menu_item = gtk_menu_item_new_with_label (_("Print"));
     g_signal_connect (menu_item, "activate", G_CALLBACK (print_pixbuf), info);
     gtk_widget_show (menu_item);
     gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
     gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_P, GDK_CONTROL_MASK,
                                 GTK_ACCEL_VISIBLE);
-#endif
 
-    menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_IN, NULL);
+    menu_item = gtk_menu_item_new_with_label (_("Zoom In"));
     g_signal_connect (menu_item, "activate", G_CALLBACK (zoom_in), info);
     gtk_widget_show (menu_item);
     gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
     gtk_widget_add_accelerator (menu_item, "activate", info->accel_group, GDK_plus,
                                 GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
-
-    menu_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_ZOOM_OUT, NULL);
+                                              
+    menu_item = gtk_menu_item_new_with_label (_("Zoom Out"));
     g_signal_connect (menu_item, "activate", G_CALLBACK (zoom_out), info);
     gtk_widget_show (menu_item);
     gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), menu_item);
@@ -502,122 +730,74 @@ quit_cb (GtkWidget * win, gpointer unused)
     gtk_main_quit ();
 }
 
+static GtkToolItem *
+tool_button_new (const char *icon_name)
+{
+    GtkToolItem *toolitem;
+
+    toolitem = gtk_tool_button_new (NULL, NULL);
+    gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (toolitem), icon_name);
+    return toolitem;
+}
+
 static void
-populate_window (GtkWidget * win, ViewerCbInfo * info, int xid, gint win_width, gint win_height)
+populate_window (GtkWidget * win, 
+                 ViewerCbInfo * info, 
+                 cairo_surface_t *surface /* adopted */)
 {
     GtkWidget *vbox;
     GtkWidget *scroll;
-    gint img_width, img_height;
+    GtkWidget *toolbar;
+    GtkToolItem *toolitem;
 
     vbox = gtk_vbox_new (FALSE, 0);
     gtk_container_add (GTK_CONTAINER (win), vbox);
 
     /* create a new image */
-    info->image = gtk_image_new_from_pixbuf (info->pixbuf);
-
-    /* pack the window with the image */
-    img_width = gdk_pixbuf_get_width (info->pixbuf);
-    img_height = gdk_pixbuf_get_height (info->pixbuf);
-
-    if (xid <= 0) {
-		GtkWidget *toolbar;
-		GtkToolItem *toolitem;
-		GtkRequisition requisition;
-
-        toolbar = gtk_toolbar_new ();
-        gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
+    info->image = rsvg_image_new_take_surface (surface);
 
-        toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_ZOOM_IN);
-        gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, 0);
-        g_signal_connect (G_OBJECT (toolitem), "clicked", G_CALLBACK (zoom_in), info);
+    toolbar = gtk_toolbar_new ();
+    gtk_box_pack_start (GTK_BOX (vbox), toolbar, FALSE, FALSE, 0);
 
-        toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_ZOOM_OUT);
-        gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, 1);
-        g_signal_connect (G_OBJECT (toolitem), "clicked", G_CALLBACK (zoom_out), info);
+    toolitem = tool_button_new ("zoom-in");
+    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, 0);
+    g_signal_connect (toolitem, "clicked", G_CALLBACK (zoom_in), info);
 
-		gtk_widget_size_request(toolbar, &requisition);
+    toolitem = tool_button_new ("zoom-out");
+    gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, 1);
+    g_signal_connect (toolitem, "clicked", G_CALLBACK (zoom_out), info);
 
-		/* HACK: adjust for frame width & height + packing borders */
-		img_height += requisition.height + 30;
-		win_height += requisition.height + 30;
-		img_width  += 20;
-		win_width  += 20;
-    }
-
-    if ((xid > 0 && (img_width > win_width || img_height > win_height))
-        || (xid <= 0)) {
-        gtk_window_set_default_size (GTK_WINDOW (win), MIN (img_width, win_width),
-                                     MIN (img_height, win_height));
-
-        scroll = gtk_scrolled_window_new (NULL, NULL);
-        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
-                                        GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
-        gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll), info->image);
-        gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
-    } else {
-        gtk_box_pack_start (GTK_BOX (vbox), info->image, TRUE, TRUE, 0);
-        gtk_window_set_default_size (GTK_WINDOW (win), img_width, img_height);
-    }
+    scroll = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+    gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll), GTK_WIDGET (info->image));
+    gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
 }
 
 static void
-view_pixbuf (ViewerCbInfo * info, int xid, const char *color)
+view_surface (ViewerCbInfo * info, 
+              cairo_surface_t *surface /* adopted */,
+              const char *bg_color)
 {
     GtkWidget *win;
-    GdkColor bg_color;
-    gint win_width, win_height;
 
     /* create toplevel window and set its title */
 
-#ifdef ENABLE_XEMBED
-    if (xid > 0) {
-        GdkWindow *gdk_parent;
-
-        win = gtk_plug_new (0);
-
-        gdk_parent = gdk_window_foreign_new (xid);
-        gdk_window_get_geometry (gdk_parent, NULL, NULL, &win_width, &win_height, NULL);
-
-        /* so that button and key presses get registered */
-        gtk_widget_add_events (win, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
-    } else
-#endif
-    {
-        char *title;
-
-        win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
-        win_width = DEFAULT_WIDTH;
-        win_height = DEFAULT_HEIGHT;
+    win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 
-        if (info->id) {
-            title = g_strdup_printf ("%s#%s — %s", info->base_uri, info->id, _("SVG Viewer"));
-        } else {
-            title = g_strdup_printf ("%s — %s", info->base_uri,  _("SVG Viewer"));
-        }
-        gtk_window_set_title (GTK_WINDOW (win), title);
-        g_free (title);
-    }
+    populate_window (win, info, surface);
 
-    populate_window (win, info, xid, win_width, win_height);
+    /* exit when 'X' is clicked */
+    g_signal_connect (win, "destroy", G_CALLBACK (quit_cb), NULL);
+    g_signal_connect (win, "delete_event", G_CALLBACK (quit_cb), NULL);
 
-    rsvg_window_set_default_icon (GTK_WINDOW (win), info->pixbuf);
+    if (bg_color && strcmp (bg_color, "none") != 0) {
+        MyGdkRGBA rgba;
 
-    /* exit when 'X' is clicked */
-    g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (quit_cb), NULL);
-    g_signal_connect (G_OBJECT (win), "delete_event", G_CALLBACK (quit_cb), NULL);
-
-    if (color && strcmp (color, "none") != 0) {
-        if (gdk_color_parse (color, &bg_color)) {
-            GtkWidget *parent_widget = gtk_widget_get_parent (info->image);
-
-            if (gdk_colormap_alloc_color
-                (gtk_widget_get_colormap (parent_widget), &bg_color, FALSE, TRUE))
-                gtk_widget_modify_bg (parent_widget, GTK_STATE_NORMAL, &bg_color);
-            else
-                g_warning (_("Couldn't allocate color '%s'"), color);
-        } else
-            g_warning (_("Couldn't parse color '%s'"), color);
+        if (my_gdk_rgba_parse (&rgba, bg_color))
+			rsvg_image_set_bg_color (info->image, rgba);
+        else
+            g_warning (_("Couldn't parse color '%s'"), bg_color);
     }
 
     create_popup_menu (info);
@@ -625,22 +805,17 @@ view_pixbuf (ViewerCbInfo * info, int xid, const char *color)
     info->window = win;
     gtk_window_add_accel_group (GTK_WINDOW (win), info->accel_group);
 
-    g_signal_connect (G_OBJECT (win), "button-press-event", G_CALLBACK (button_press_event), info);
+    g_signal_connect (win, "button-press-event", G_CALLBACK (button_press_event), info);
 
     gtk_widget_show_all (win);
 
-#ifdef ENABLE_XEMBED
-    if (xid > 0) {
-        XReparentWindow (GDK_WINDOW_XDISPLAY (win->window),
-                         GDK_WINDOW_XID (win->window), xid, 0, 0);
-        XMapWindow (GDK_WINDOW_XDISPLAY (win->window), GDK_WINDOW_XID (win->window));
-    }
-#endif
+    set_window_title (info);
 }
 
 int
 main (int argc, char **argv)
 {
+    int retval = 1;
     GError *err = NULL;
     GOptionContext *g_option_context;
     double x_zoom = 1.0;
@@ -652,22 +827,22 @@ main (int argc, char **argv)
     int bVersion = 0;
     char *bg_color = NULL;
     char *base_uri = NULL;
-    int bKeepAspect = 0;
+    gboolean keep_aspect_ratio = FALSE;
+    gboolean unlimited = FALSE;
     char *id = NULL;
+    GInputStream *input;
+    GFile *file, *base_file;
+    cairo_surface_t *surface;
 
-    int xid = -1;
     int from_stdin = 0;
     ViewerCbInfo info;
 
-    struct RsvgSizeCallbackData size_data;
+    RsvgHandleFlags flags = RSVG_HANDLE_FLAGS_NONE;
 
     char **args = NULL;
     gint n_args = 0;
 
     GOptionEntry options_table[] = {
-#ifdef ENABLE_XEMBED
-        {"xid", 'i', 0, G_OPTION_ARG_INT, &xid, N_("XWindow ID [for X11 embedding]"), N_("<int>")},
-#endif
         {"stdin", 's', 0, G_OPTION_ARG_NONE, &from_stdin, N_("Read from stdin instead of a file"),
          NULL},
         {"dpi-x", 'd', 0, G_OPTION_ARG_DOUBLE, &dpi_x, N_("Set the # of Pixels Per Inch"),
@@ -686,20 +861,20 @@ main (int argc, char **argv)
          N_("<string>")},
         {"id", 0, 0, G_OPTION_ARG_STRING, &id, N_("Only show one node (default: all)"),
          N_("<string>")},
-        {"keep-aspect", 'k', 0, G_OPTION_ARG_NONE, &bKeepAspect,
+        {"keep-aspect", 'k', 0, G_OPTION_ARG_NONE, &keep_aspect_ratio,
          N_("Preserve the image's aspect ratio"), NULL},
+        {"unlimited", 'u', 0, G_OPTION_ARG_NONE, &unlimited,
+         N_("Allow huge SVG files"), NULL},
         {"version", 'v', 0, G_OPTION_ARG_NONE, &bVersion, N_("Show version information"), NULL},
         {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("[FILE...]")},
         {NULL}
     };
 
 	/* Set the locale so that UTF-8 filenames work */
-	setlocale(LC_ALL, "");
+    setlocale(LC_ALL, "");
 
-    g_thread_init (NULL);
+    RSVG_G_TYPE_INIT;
 
-    info.pixbuf = NULL;
-    info.svg_bytes = NULL;
     info.window = NULL;
     info.popup_menu = NULL;
 
@@ -718,109 +893,104 @@ main (int argc, char **argv)
         return 0;
     }
 
-    if (args) {
-        while (args[n_args] != NULL)
-            n_args++;
-    }
+    if (args)
+        n_args = g_strv_length (args);
+    else
+        n_args = 0;
 
     if ((!from_stdin) && (n_args != 1)) {
-        g_print (_("No files specified, and not using --stdin\n"));
+        g_printerr (_("No files specified, and not using --stdin\n"));
         return 1;
     }
 
-    /* initialize gtk+ and rsvg */
-    rsvg_init ();
-
     rsvg_set_default_dpi_x_y (dpi_x, dpi_y);
 
-    /* if both are unspecified, assume user wants to zoom the pixbuf in at least 1 dimension */
-    if (width == -1 && height == -1) {
-        size_data.type = RSVG_SIZE_ZOOM;
-        size_data.x_zoom = x_zoom;
-        size_data.y_zoom = y_zoom;
-    }
-    /* if both are unspecified, assume user wants to resize pixbuf in at least 1 dimension */
-    else if (x_zoom == 1.0 && y_zoom == 1.0) {
-        size_data.type = RSVG_SIZE_WH;
-        size_data.width = width;
-        size_data.height = height;
-    }
-    /* assume the user wants to zoom the pixbuf, but cap the maximum size */
-    else {
-        size_data.type = RSVG_SIZE_ZOOM_MAX;
-        size_data.x_zoom = x_zoom;
-        size_data.y_zoom = y_zoom;
-        size_data.width = width;
-        size_data.height = height;
-    }
-
-    size_data.keep_aspect_ratio = bKeepAspect;
+    if (unlimited)
+        flags |= RSVG_HANDLE_FLAG_UNLIMITED;
 
-    if (!from_stdin) {
-        if (base_uri == NULL)
-            base_uri = (char *) args[0];
-
-        info.svg_bytes = _rsvg_acquire_xlink_href_resource (args[0], base_uri, NULL);
+    if (from_stdin) {
+#if 0 // defined (G_OS_UNIX)
+        input = g_unix_input_stream_new (STDIN_FILENO, FALSE);
+#else
+        input = NULL;
+        g_set_error_literal (&err, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                             "Reading from stdin not supported");
+#endif
+        base_file = NULL;
     } else {
-        info.svg_bytes = g_byte_array_new ();
+        file = g_file_new_for_commandline_arg (args[0]);
+        input = (GInputStream *) g_file_read (file, NULL, &err);
 
-        for (;;) {
-            unsigned char buf[1024 * 8];
-            size_t nread = fread (buf, 1, sizeof (buf), stdin);
-
-            if (nread > 0)
-                g_byte_array_append (info.svg_bytes, buf, nread);
-
-            if (nread < sizeof (buf)) {
-                if (ferror (stdin)) {
-                    g_print (_("Error reading\n"));
-                    g_byte_array_free (info.svg_bytes, TRUE);
-                    fclose (stdin);
-
-                    return 1;
-                } else if (feof (stdin))
-                    break;
-            }
-        }
+        if (base_uri)
+            base_file = g_file_new_for_uri (base_uri);
+        else
+            base_file = g_object_ref (file);
 
-        fclose (stdin);
+        g_object_unref (file);
     }
 
-    if (!info.svg_bytes || !info.svg_bytes->len) {
-        g_print (_("Couldn't open %s\n"), args[0]);
+    g_strfreev (args);
+
+    if (input == NULL) {
+        g_printerr ("Failed to read input: %s\n", err->message);
+        g_error_free (err);
         return 1;
     }
 
-    info.base_uri = base_uri;
+    info.base_uri = base_file ? g_file_get_uri (base_file) : g_strdup ("");
     info.id = id;
+    info.x_zoom = x_zoom;
+    info.y_zoom = y_zoom;
+
+    info.handle = rsvg_handle_new_from_stream_sync (input, 
+                                                    base_file, 
+                                                    flags,
+                                                    NULL /* cancellable */,
+                                                    &err);
+    if (base_file != NULL)
+        g_object_unref (base_file);
+    g_object_unref (input);
+
+    if (info.handle == NULL) {
+        g_printerr ("Failed to load SVG: %s\n", err->message);
+        g_error_free (err);
+        return 1;
+    }
 
-    info.pixbuf = 
-        pixbuf_from_data_with_size_data (info.svg_bytes->data, info.svg_bytes->len, &size_data,
-                                         base_uri, id, &err);
-
-    if (!info.pixbuf) {
-        g_print (_("Error displaying image"));
-
-        if (err) {
-            g_print (": %s", err->message);
-            g_error_free (err);
-        }
+    rsvg_handle_get_dimensions (info.handle, &info.dimensions);
 
-        g_print ("\n");
+    if (width != -1) {
+        info.x_zoom = (double) width / info.dimensions.width;
+    } else {
+        info.x_zoom = x_zoom;
+    }
+    if (height != -1) {
+        info.y_zoom = (double) height / info.dimensions.height;
+    } else {
+        info.y_zoom = y_zoom;
+    }
 
-        return 1;
+    surface = render_to_surface (&info);
+    if (surface == NULL) {
+        g_printerr ("Unknown error while rendering image\n");
+        goto done;
     }
 
+    retval = 0;
+
     info.accel_group = gtk_accel_group_new ();
 
-    view_pixbuf (&info, xid, bg_color);
+    view_surface (&info, surface, bg_color);
 
     /* run the gtk+ main loop */
     gtk_main ();
 
-    g_object_unref (G_OBJECT (info.pixbuf));
-    g_byte_array_free (info.svg_bytes, TRUE);
-    rsvg_term ();
+  done:
+
+    g_free (info.base_uri);
+    g_object_unref (info.handle);
 
-    return 0;
+    rsvg_cleanup ();
+
+    return retval;
 }
diff --git a/test-performance.c b/test-performance.c
deleted file mode 100644
index 79987b9..0000000
--- a/test-performance.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
-
-   test-performance.c: performance tests.
- 
-   Copyright (C) 2002 Ximian, Inc.
-   Copyright (C) 2004 Dom Lachowicz
-  
-   This program is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
-  
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
-  
-   You should have received a copy of the GNU Library General Public
-   License along with this program; if not, write to the
-   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.
-  
-   Author: Michael Meeks <michael@ximian.com>
-   Author: Dom Lachowicz <cinamod@hotmail.com>
-*/
-
-#include <config.h>
-#include <glib.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "rsvg.h"
-#include "rsvg-private.h"
-
-int
-main (int argc, char **argv)
-{
-    int i, count = 10;
-    GTimer *timer;
-
-    GOptionContext *g_option_context;
-    double x_zoom = 1.0;
-    double y_zoom = 1.0;
-    double dpi = -1.0;
-    int width = -1;
-    int height = -1;
-    int bVersion = 0;
-
-    char **args;
-    gint n_args = 0;
-    GdkPixbuf *pixbuf;
-
-    GOptionEntry options_table[] = {
-        {"dpi", 'd', 0, G_OPTION_ARG_DOUBLE, &dpi, "pixels per inch", "<float>"},
-        {"x-zoom", 'x', 0, G_OPTION_ARG_DOUBLE, &x_zoom, "x zoom factor", "<float>"},
-        {"y-zoom", 'y', 0, G_OPTION_ARG_DOUBLE, &y_zoom, "y zoom factor", "<float>"},
-        {"width", 'w', 0, G_OPTION_ARG_INT, &width, "width", "<int>"},
-        {"height", 'h', 0, G_OPTION_ARG_INT, &height, "height", "<int>"},
-        {"count", 'c', 0, G_OPTION_ARG_INT, &count, "number of times to render the SVG", "<int>"},
-        {"version", 'v', 0, G_OPTION_ARG_NONE, &bVersion, "show version information", NULL},
-        {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args, NULL, N_("[FILE...]")},
-        {NULL}
-    };
-
-    g_option_context = g_option_context_new (_("- SVG Performance Test"));
-    g_option_context_add_main_entries (g_option_context, options_table, NULL);
-    g_option_context_set_help_enabled (g_option_context, TRUE);
-    if (!g_option_context_parse (g_option_context, &argc, &argv, NULL)) {
-        exit (1);
-    }
-
-    g_option_context_free (g_option_context);
-
-    if (bVersion != 0) {
-        g_print ("test-performance version %s\n", VERSION);
-        return 0;
-    }
-
-    if (args)
-        while (args[n_args] != NULL)
-            n_args++;
-
-    if (n_args != 1) {
-        g_print (_("Must specify a SVG file\n"));
-        return 1;
-    }
-
-    rsvg_init ();
-
-    fprintf (stdout, "File '%s'\n", args[0]);
-
-    timer = g_timer_new ();
-    g_timer_start (timer);
-
-    for (i = 0; i < count; i++) {
-        /* if both are unspecified, assume user wants to zoom the pixbuf in at least 1 dimension */
-        if (width == -1 && height == -1)
-            pixbuf = rsvg_pixbuf_from_file_at_zoom (args[0], x_zoom, y_zoom, NULL);
-        /* if both are unspecified, assume user wants to resize pixbuf in at least 1 dimension */
-        else if (x_zoom == 1.0 && y_zoom == 1.0)
-            pixbuf = rsvg_pixbuf_from_file_at_size (args[0], width, height, NULL);
-        else
-            /* assume the user wants to zoom the pixbuf, but cap the maximum size */
-            pixbuf = rsvg_pixbuf_from_file_at_zoom_with_max (args[0], x_zoom, y_zoom,
-                                                             width, height, NULL);
-
-        g_object_unref (pixbuf);
-    }
-
-    fprintf (stdout, "Rendering took %g(s)\n", g_timer_elapsed (timer, NULL) / count);
-    g_timer_destroy (timer);
-
-    rsvg_term ();
-
-    return 0;
-}
