[gcompris/GCOMPRIS_8_3] Integrated gnomecanvas and libart_lgpl to make it easier to compile GCompris



commit dc4f522a1b1faacd0a825409054647feaaef0ae4
Author: Bruno Coudoin <bruno coudoin free fr>
Date:   Sat Jul 4 20:15:47 2009 +0200

    Integrated gnomecanvas and libart_lgpl to make it easier to compile GCompris
    on different platforms. This is similar to the way GCompris 8.4.X is provided.

 configure.in                                    |   49 +-
 src/Makefile.am                                 |    2 +-
 src/boards/Makefile.am                          |    9 -
 src/gcompris/Makefile.am                        |   18 +-
 src/gcompris/gcompris.c                         |    7 +
 src/libart_lgpl/.gitignore                      |    2 +
 src/libart_lgpl/AUTHORS                         |    1 +
 src/libart_lgpl/COPYING                         |  674 ++++
 src/libart_lgpl/ChangeLog                       | 1281 +++++++
 src/libart_lgpl/Makefile.am                     |  109 +
 src/libart_lgpl/NEWS                            |   23 +
 src/libart_lgpl/README                          |   19 +
 src/libart_lgpl/README.CVS                      |   14 +
 src/libart_lgpl/art_affine.c                    |  457 +++
 src/libart_lgpl/art_affine.h                    |   92 +
 src/libart_lgpl/art_alphagamma.c                |   84 +
 src/libart_lgpl/art_alphagamma.h                |   54 +
 src/libart_lgpl/art_bpath.c                     |   91 +
 src/libart_lgpl/art_bpath.h                     |   58 +
 src/libart_lgpl/art_filterlevel.h               |   67 +
 src/libart_lgpl/art_gray_svp.c                  |  122 +
 src/libart_lgpl/art_gray_svp.h                  |   45 +
 src/libart_lgpl/art_misc.c                      |   92 +
 src/libart_lgpl/art_misc.h                      |  101 +
 src/libart_lgpl/art_pathcode.h                  |   38 +
 src/libart_lgpl/art_pixbuf.c                    |  284 ++
 src/libart_lgpl/art_pixbuf.h                    |  103 +
 src/libart_lgpl/art_point.h                     |   37 +
 src/libart_lgpl/art_rect.c                      |  214 ++
 src/libart_lgpl/art_rect.h                      |   77 +
 src/libart_lgpl/art_rect_svp.c                  |   81 +
 src/libart_lgpl/art_rect_svp.h                  |   46 +
 src/libart_lgpl/art_rect_uta.c                  |  133 +
 src/libart_lgpl/art_rect_uta.h                  |   42 +
 src/libart_lgpl/art_render.c                    | 1382 ++++++++
 src/libart_lgpl/art_render.h                    |  182 +
 src/libart_lgpl/art_render_gradient.c           |  715 ++++
 src/libart_lgpl/art_render_gradient.h           |   85 +
 src/libart_lgpl/art_render_mask.c               |  167 +
 src/libart_lgpl/art_render_mask.h               |   46 +
 src/libart_lgpl/art_render_svp.c                |  420 +++
 src/libart_lgpl/art_render_svp.h                |   46 +
 src/libart_lgpl/art_rgb.c                       |  174 +
 src/libart_lgpl/art_rgb.h                       |   43 +
 src/libart_lgpl/art_rgb_a_affine.c              |  148 +
 src/libart_lgpl/art_rgb_a_affine.h              |   51 +
 src/libart_lgpl/art_rgb_affine.c                |  105 +
 src/libart_lgpl/art_rgb_affine.h                |   49 +
 src/libart_lgpl/art_rgb_affine_private.c        |  126 +
 src/libart_lgpl/art_rgb_affine_private.h        |   38 +
 src/libart_lgpl/art_rgb_bitmap_affine.c         |  197 ++
 src/libart_lgpl/art_rgb_bitmap_affine.h         |   51 +
 src/libart_lgpl/art_rgb_pixbuf_affine.c         |  103 +
 src/libart_lgpl/art_rgb_pixbuf_affine.h         |   51 +
 src/libart_lgpl/art_rgb_rgba_affine.c           |  141 +
 src/libart_lgpl/art_rgb_rgba_affine.h           |   50 +
 src/libart_lgpl/art_rgb_svp.c                   |  456 +++
 src/libart_lgpl/art_rgb_svp.h                   |   54 +
 src/libart_lgpl/art_rgba.c                      |  257 ++
 src/libart_lgpl/art_rgba.h                      |   48 +
 src/libart_lgpl/art_svp.c                       |  151 +
 src/libart_lgpl/art_svp.h                       |   67 +
 src/libart_lgpl/art_svp_intersect.c             | 1802 ++++++++++
 src/libart_lgpl/art_svp_intersect.h             |   69 +
 src/libart_lgpl/art_svp_ops.c                   |  400 +++
 src/libart_lgpl/art_svp_ops.h                   |   43 +
 src/libart_lgpl/art_svp_point.c                 |  143 +
 src/libart_lgpl/art_svp_point.h                 |   48 +
 src/libart_lgpl/art_svp_render_aa.c             |  462 +++
 src/libart_lgpl/art_svp_render_aa.h             |   66 +
 src/libart_lgpl/art_svp_vpath.c                 |  214 ++
 src/libart_lgpl/art_svp_vpath.h                 |   43 +
 src/libart_lgpl/art_svp_vpath_stroke.c          |  738 ++++
 src/libart_lgpl/art_svp_vpath_stroke.h          |   69 +
 src/libart_lgpl/art_svp_wind.c                  | 1544 +++++++++
 src/libart_lgpl/art_svp_wind.h                  |   56 +
 src/libart_lgpl/art_uta.c                       |   87 +
 src/libart_lgpl/art_uta.h                       |   71 +
 src/libart_lgpl/art_uta_ops.c                   |  111 +
 src/libart_lgpl/art_uta_ops.h                   |   41 +
 src/libart_lgpl/art_uta_rect.c                  |  110 +
 src/libart_lgpl/art_uta_rect.h                  |   41 +
 src/libart_lgpl/art_uta_svp.c                   |   53 +
 src/libart_lgpl/art_uta_svp.h                   |   44 +
 src/libart_lgpl/art_uta_vpath.c                 |  381 +++
 src/libart_lgpl/art_uta_vpath.h                 |   49 +
 src/libart_lgpl/art_vpath.c                     |  240 ++
 src/libart_lgpl/art_vpath.h                     |   70 +
 src/libart_lgpl/art_vpath_bpath.c               |  327 ++
 src/libart_lgpl/art_vpath_bpath.h               |   47 +
 src/libart_lgpl/art_vpath_dash.c                |  199 ++
 src/libart_lgpl/art_vpath_dash.h                |   49 +
 src/libart_lgpl/art_vpath_svp.c                 |  195 ++
 src/libart_lgpl/art_vpath_svp.h                 |   42 +
 src/libart_lgpl/gen_art_config.c                |   52 +
 src/libart_lgpl/libart-config.in                |   56 +
 src/libart_lgpl/libart-features.c               |   18 +
 src/libart_lgpl/libart-features.h               |   24 +
 src/libart_lgpl/libart-features.h.in            |   24 +
 src/libart_lgpl/libart.h                        |   39 +
 src/libart_lgpl/libartConf.sh.in                |    6 +
 src/libart_lgpl/makefile.msc                    |  104 +
 src/libart_lgpl/test_gradient.c                 |  376 +++
 src/libart_lgpl/testart.c                       |  635 ++++
 src/libart_lgpl/testuta.c                       |  232 ++
 src/libgnomecanvas/.gitignore                   |    3 +
 src/libgnomecanvas/Makefile.am                  |   75 +
 src/libgnomecanvas/gnome-canvas-bpath.c         |  175 +
 src/libgnomecanvas/gnome-canvas-bpath.h         |   61 +
 src/libgnomecanvas/gnome-canvas-clipgroup.c     |  451 +++
 src/libgnomecanvas/gnome-canvas-clipgroup.h     |   58 +
 src/libgnomecanvas/gnome-canvas-i18n.h          |   68 +
 src/libgnomecanvas/gnome-canvas-line.c          | 1422 ++++++++
 src/libgnomecanvas/gnome-canvas-line.h          |  154 +
 src/libgnomecanvas/gnome-canvas-marshal.list    |    2 +
 src/libgnomecanvas/gnome-canvas-path-def.c      | 1287 +++++++
 src/libgnomecanvas/gnome-canvas-path-def.h      |   96 +
 src/libgnomecanvas/gnome-canvas-pixbuf.c        | 1069 ++++++
 src/libgnomecanvas/gnome-canvas-pixbuf.h        |   60 +
 src/libgnomecanvas/gnome-canvas-polygon.c       |  249 ++
 src/libgnomecanvas/gnome-canvas-polygon.h       |   88 +
 src/libgnomecanvas/gnome-canvas-rect-ellipse.c  |  431 +++
 src/libgnomecanvas/gnome-canvas-rect-ellipse.h  |  159 +
 src/libgnomecanvas/gnome-canvas-rich-text.c     | 2201 ++++++++++++
 src/libgnomecanvas/gnome-canvas-rich-text.h     |   77 +
 src/libgnomecanvas/gnome-canvas-shape-private.h |  103 +
 src/libgnomecanvas/gnome-canvas-shape.c         | 1559 +++++++++
 src/libgnomecanvas/gnome-canvas-shape.h         |   81 +
 src/libgnomecanvas/gnome-canvas-text.c          | 1740 ++++++++++
 src/libgnomecanvas/gnome-canvas-text.h          |  170 +
 src/libgnomecanvas/gnome-canvas-util.c          |  700 ++++
 src/libgnomecanvas/gnome-canvas-util.h          |  155 +
 src/libgnomecanvas/gnome-canvas-widget.c        |  599 ++++
 src/libgnomecanvas/gnome-canvas-widget.h        |  103 +
 src/libgnomecanvas/gnome-canvas.c               | 4081 +++++++++++++++++++++++
 src/libgnomecanvas/gnome-canvas.h               |  635 ++++
 src/libgnomecanvas/libgnomecanvas.h             |   48 +
 src/libgnomecanvas/libgnomecanvastypes.c        |   43 +
 138 files changed, 37484 insertions(+), 68 deletions(-)
---
diff --git a/configure.in b/configure.in
index 5394bdb..8a07368 100644
--- a/configure.in
+++ b/configure.in
@@ -12,9 +12,8 @@ AC_ISC_POSIX
 
 GTK_REQUIRED=2.4.0
 GDK_PIXBUF_REQUIRED=2.4.0
-LIBGNOMECANVAS_REQUIRED=2.3.6
 
-PKG_CHECK_MODULES(GCOMPRIS, gtk+-2.0 >= $GTK_REQUIRED gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED libgnomecanvas-2.0 >= $LIBGNOMECANVAS_REQUIRED gthread-2.0)
+PKG_CHECK_MODULES(GCOMPRIS, gtk+-2.0 >= $GTK_REQUIRED gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED gthread-2.0)
 
 AC_SUBST(GCOMPRIS_CFLAGS)
 AC_SUBST(GCOMPRIS_LIBS)
@@ -181,6 +180,10 @@ PKG_CHECK_MODULES(XML, libxml-2.0)
 AC_SUBST(XML_CFLAGS)
 AC_SUBST(XML_LIBS)
 
+dnl Freetype2 flags (needed for libgnomecanvas)
+PKG_CHECK_MODULES(FT2, freetype2)
+AC_SUBST(FT2_CFLAGS)
+
 dnl glib-genmarshal
 AC_PATH_PROG(GLIB_GENMARSHAL, glib-genmarshal)
 
@@ -489,19 +492,10 @@ else
 
   if test x$py_build_only = xno; then
     AM_CHECK_PYMOD(gtk,,,AC_MSG_ERROR([*** pygtk installed but not visible from python ]))
-    dnl AM_CHECK_PYMOD(xml.dom.DOMImplementation,,,AC_MSG_WARN([*** pyxml missing ]))
 
     dnl Set this variable for the .spec.in file
     REQUIRE_PYTHON="python pygtk2.0"
 
-    dnl with gnome-python >= 2.10 gnome.canvas is obsolete. use gnomecanvas
-    dnl gnomecanvas not supported yet in gcompris
-    dnl
-    dnl   AM_CHECK_PYMOD(gnome.canvas,,test_gnomecanvas="no",test_gnomevanvas="yes")
-    dnl  if test x$test_gnomecanvas = xyes ; then
-    dnl     AM_CHECK_PYMOD(gnomecanvas,,,AC_MSG_ERROR([*** gnome.canvas or gnomecanvas installed but not visible from python ]))
-    dnl   fi
-
     if test x$with_sqlite = xyes; then
 	AM_CHECK_PYMOD(pysqlite2,,,AC_MSG_ERROR([*** pysqlite2 missing ]))
 	REQUIRE_PYTHON="$REQUIRE_PYTHON python-sqlite2"
@@ -525,37 +519,6 @@ AC_SUBST(SQLITE3_LIBS)
 
 AC_SUBST(REQUIRE_PYTHON)
 
-dnl PYCAIRO SUPPORT
-dnl Check for pycairo
-AC_ARG_ENABLE(cairo,
-          AC_HELP_STRING(
-          [--enable-cairo],
-          [Enable cairo support (experimental)]),
-          with_cairo="$enableval", with_cairo="no")
-
-use_cairo=no
-if test x$with_cairo = xyes; then
-   PKG_CHECK_MODULES(CAIRO, cairo >= 0.5.0,, AC_MSG_ERROR([*** cairo not found! get it at http://cairographics.org/ if you want --enable-cairo ]))
-   if test x$py_build_only = xno; then
-      if test x$build_python_plugin = xyes; then
-         PKG_CHECK_MODULES(PYCAIRO, pycairo >= 0.5.0,, AC_MSG_ERROR([*** pycairo not found! get it at http://cairographics.org/ if you want --enable-cairo ]))
-         AM_CHECK_PYMOD(cairo,,,AC_MSG_ERROR([*** pycairo installed but not visible from python ]))
-         use_cairo=yes
-         AC_DEFINE([USE_CAIRO],[],[use Cairo graphic library])
-      else
-         AC_MSG_WARN([ cairo enabled without python ?])
-      fi
-   else
-    AC_MSG_WARN([py-build-only: skipping test. Do not forget to install pycairo !])
-    use_cairo=yes
-    AC_DEFINE([USE_CAIRO],[],[use Cairo graphic library])
-   fi
-fi
-
-AM_CONDITIONAL(USE_CAIRO, test x$use_cairo = xyes)
-AC_SUBST(CAIRO_CFLAGS)
-AC_SUBST(CAIRO_LIBS)
-
 dnl mingw32 specific stuff.
 dnl use libgw32c
 dnl define __GW32__ and WIN32
@@ -622,6 +585,8 @@ src/boards/python/gcompris/timer/Makefile
 src/boards/python/gcompris/utils/Makefile
 src/boards/python/connect4p/Makefile
 src/boards/python/admin/Makefile
+src/libart_lgpl/Makefile
+src/libgnomecanvas/Makefile
 po/Makefile.in
 docs/Makefile
 docs/C/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index cbc22fb..1cf7fa9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,3 +1,3 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = gcompris boards
+SUBDIRS = libart_lgpl libgnomecanvas gcompris boards
diff --git a/src/boards/Makefile.am b/src/boards/Makefile.am
index 9dabb27..e62b52e 100644
--- a/src/boards/Makefile.am
+++ b/src/boards/Makefile.am
@@ -16,14 +16,6 @@ sqlite_ldflags =
 sqlite_cflags =
 endif
 
-if USE_CAIRO
-cairo_ldflags = $(CAIRO_LIBS)
-cairo_cflags = $(CAIRO_CFLAGS)
-else
-cairo_ldflags =
-cairo_cflags =
-endif
-
 if PLATFORM_WIN32
 shared = -shared
 no_undefined = -no-undefined
@@ -91,7 +83,6 @@ INCLUDES = \
 	$(PYTHON_CFLAGS) \
 	$(PYGTK_CFLAGS) \
 	$(sqlite_cflags) \
-	$(cairo_cflags) \
 	$(dll_export)
 
 libawele_la_LDFLAGS = $(shared) $(no_undefined) -module -avoid-version $(gc_libs)
diff --git a/src/gcompris/Makefile.am b/src/gcompris/Makefile.am
index d6ebad3..971601b 100644
--- a/src/gcompris/Makefile.am
+++ b/src/gcompris/Makefile.am
@@ -15,18 +15,6 @@ sqlite_ldadd =
 sqlite_cflags =
 endif
 
-#if USE_CAIRO
-#cairo_ldflags = $(CAIRO_LIBS)
-#cairo_cflags = $(CAIRO_CFLAGS)
-#cairo_h = gcompris-cairo.h
-#cairo_c = gcompris-cairo.c
-#else
-#cairo_ldflags =
-#cairo_cflags =
-#cairo_h =
-#cairo_c =
-#endif
-
 if PLATFORM_WIN32
 no_undefined = -no-undefined
 shared = -shared
@@ -55,12 +43,9 @@ INCLUDES = \
 	-DGNOME_DISABLE_DEPRECATED \
         $(sqlite_cflags) \
 	$(GNET_CFLAGS) \
-	$(cairo_cflags) \
 	$(NSBUNDLE_CPPFLAGS)
 
 gcompris_SOURCES = \
-	$(cairo_c) \
-	$(cairo_h) \
 	$(gcompris_nsbundle_h) \
 	$(gcompris_nsbundle_m) \
 	about.c \
@@ -122,7 +107,8 @@ gcompris_SOURCES = \
 	wordlist.h
 
 gcompris_LDFLAGS = \
-	-export-dynamic $(cairo_ldflags)
+	-export-dynamic $(top_builddir)/src/libgnomecanvas/libgnomecanvas-2.a \
+	$(top_builddir)/src/libart_lgpl/libart_lgpl_2.a
 
 
 gcompris_LDADD = \
diff --git a/src/gcompris/gcompris.c b/src/gcompris/gcompris.c
index 2a9e65c..5929276 100644
--- a/src/gcompris/gcompris.c
+++ b/src/gcompris/gcompris.c
@@ -1909,5 +1909,12 @@ main (int argc, char *argv[])
 
   gtk_main ();
 
+  /* FIXME: HACK Needed or we have unresolved symbols at python plugin dlopen
+   *        Is there a better way to fix these?
+   */
+  printf("%s\n", g_type_name(GNOME_TYPE_CANVAS_POLYGON));
+  printf("%s\n", g_type_name(GNOME_TYPE_CANVAS_CLIPGROUP));
+  printf("%s\n", g_type_name(GNOME_TYPE_CANVAS_BPATH));
+
   return(0);
 }
diff --git a/src/libart_lgpl/.gitignore b/src/libart_lgpl/.gitignore
new file mode 100644
index 0000000..a5b85e4
--- /dev/null
+++ b/src/libart_lgpl/.gitignore
@@ -0,0 +1,2 @@
+art_config.h
+gen_art_config
diff --git a/src/libart_lgpl/AUTHORS b/src/libart_lgpl/AUTHORS
new file mode 100644
index 0000000..fbb51b3
--- /dev/null
+++ b/src/libart_lgpl/AUTHORS
@@ -0,0 +1 @@
+Raph Levien <raph acm org>
diff --git a/src/libart_lgpl/COPYING b/src/libart_lgpl/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/src/libart_lgpl/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 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 General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/src/libart_lgpl/ChangeLog b/src/libart_lgpl/ChangeLog
new file mode 100644
index 0000000..1894dbd
--- /dev/null
+++ b/src/libart_lgpl/ChangeLog
@@ -0,0 +1,1281 @@
+2007-03-01  Frederic Crozat  <fcrozat mandriva com>
+
+	reviewed by: Dom Lachowicz <cinamod hotmail com>
+
+	* art_misc.h: Fix header when included in C++.
+	Patch from Goetz Waschk and Laurent Montel.
+
+2007-02-28  Kjartan Maraas  <kmaraas gnome org>
+
+	configure.in: Release 2.3.19
+
+2007-02-28  Kjartan Maraas  <kmaraas gnome org>
+
+	* configure.in: Fix a typo introduced with the bug fix
+	for bug #131478. Noticed by Yanko Kaneti.
+
+2007-02-26  Kjartan Maraas  <kmaraas gnome org>
+
+	* configure.in: 2.3.18.
+
+2006-12-06  Kjartan Maraas  <kmaraas gnome org>
+
+	* .cvsignore:
+	* Makefile.am:
+	* autogen.sh:
+	* configure.in:
+	Port to automake 1.9. Patch from Christian Persch.
+	Closes bug #131478
+
+2005-10-26  Alexander Larsson  <alexl redhat com>
+
+	* Makefile.am:
+	* configure.in:
+	Cygwin build fixes from Cygwin Ports maintainer (#317562)
+
+2005-10-05  Dom Lachowicz <cinamod hotmail com>
+
+	* art-misc.[ch]: Turn memory-managing macros into proper functions
+	instead of preprocessor wrappers around malloc. Win32 ships multiple 
+	runtimes, and an art_alloc'd block from one runtime can't be 
+	art_free'd by a different runtime.
+
+	* libart.def: Export the 3 new functions
+	
+2005-05-27  Alexander Larsson  <alexl redhat com>
+
+	* art_vpath_bpath.c: (art_vpath_render_bez):
+	Handle the case where start and endpoint are very near
+	in a better way. (#301750)
+
+	Patch from Bernhard Herzog  
+
+2005-03-24  Tor Lillqvist  <tml novell com>
+
+	* art_misc.h (ART_GNUC_PRINTF, ART_GNUC_NORETURN): Use __format__,
+	__printf__ and __noreturn__ to avoid warnings in case "format",
+	"printf" or "noreturn" are #defined to something else. For example
+	GNU libintl.h (at least some versions of it) #defines printf as
+	libintl_printf.
+
+2005-01-24  Kjartan Maraas  <kmaraas gnome org>
+
+	* art_render_gradient.c: (art_render_gradient_radial_render):
+	* testart.c:
+	* testuta.c: (main): Fix some compiler warnings etc.
+
+2005-01-21  Kjartan Maraas  <kmaraas gnome org>
+
+	* Release 2.3.17
+
+2004-03-22  Michael Meeks  <michael ximian com>
+
+	* Makefile.am (EXTRA_DIST): add libart.def.
+
+2004-03-04  Glynn Foster  <glynn foster sun com>
+
+	* Makefile.am, configure.in, libart-2.0-uninstalled.pc.in:
+	Add uninstalled pkg-config file.
+
+Tue Sep 09 16:30:31 2003  George Lebl <jirka 5z com>
+
+	* Release 2.3.16
+
+	* art_render_gradient.c: revert the double comparison test in the
+	  asserts and comment out the two asserts causing crashes
+	  everywhere, see bug #121850
+
+2003-09-02  Alexander Larsson  <alexl redhat com>
+
+	* configure.in: 2.3.15
+
+2003-08-26  Morten Welinder  <terra gnome org>
+
+	* art_render_gradient.c (art_render_gradient_linear_render_8): Fix
+	vertical gradients.  Fix yet more floating point comparisons using
+	!=.  For the record, Gnumeric is a heavy user.
+
+2003-08-14  Tor Lillqvist  <tml iki fi>
+
+	Some nitpicking changes for Win32:
+
+	* libart-features.h.in: Declare the variables from the library as
+	dlimport/dllexport on Win32.
+
+	* Makefile.am: Don't use -lm on Win32. Use the libart.def file.
+
+	* makefile.msc: Update to generate same name DLL and import
+	library as auto* and libtool. Add missing objects. (But dunno if
+	it is useful to maintain this file. It isn't distributed in the
+	source tarballs (should it be?). And, if it were distributed, and
+	the intention was to support random people really building libart
+	with MSVC, one should distribute also prebuilt config.h(.win32)
+	and libart-features.h(.win32) files.)
+
+	* libart-zip.in (DEVZIP): Fix typo.
+	(DLLDIR): libtool 1.5 installs DLLs in $(prefix)/bin, so look
+	there, too.
+
+	* libart.def: Add missing entry points.
+
+	* testuta.c: To check that the export/import of the version
+	variables work, print out them in a comment. Undefine
+	LIBART_COMPILATION before including libart-features.h.
+
+2003-08-08  Alexander Larsson  <alexl redhat com>
+
+	* configure.in: 2.3.14
+
+Fri Jul 25 12:29:35 2003  George Lebl <jirka 5z com>
+
+	* art_render_gradient.c (art_render_gradient_linear_render_8)
+	  (art_render_gradient_linear_render) (art_render_gradient_linear)
+	  (art_render_gradient_radial_render) (art_render_gradient_radial):
+	  Redo the checks where float was compared by == or != to using
+	  the EPSILON define.  Also copy the ArtGradientLinear and
+	  ArtGradientRadial into the source structure, pretending that
+	  these are constants that will never change or be freed by
+	  the caller is utterly evil and in fact for librsvg it is
+	  not constant.  This fixes some more very random crashes
+	  when using librsvg with libart (which seems to be the
+	  only usage of the gradient stuff)
+
+Fri Jul 18 12:57:36 2003  George Lebl <jirka 5z com>
+
+	* art_render_gradient.c: Fix more comparison-of-doubles by == bugs,
+	  this code is uber ugly.  Should fix the fairly random crashes
+	  on asserts I've been having.
+
+2003-07-11  Michael Meeks  <michael ximian com>
+
+	* Version 2.3.13
+
+2003-07-11  Federico Mena Quintero  <federico ximian com>
+
+	* art_svp_ops.c (art_svp_minus): impl.
+
+Tue Jul 08 01:15:02 2003  George Lebl <jirka 5z com>
+
+	* art_render_gradient.c: fix comment as pointed out by alex
+
+Tue Jul 08 01:13:48 2003  George Lebl <jirka 5z com>
+
+	* art_render_gradient.c (art_render_gradient_linear_render_8):
+	  when we wish to find the current segment and we go beyond the
+	  last stop due to float fun, use the last segment as that's
+	  really what we want.  Avoids a very abrupt assert death.
+
+2003-05-05  Alexander Larsson  <alexl redhat com>
+
+	* configure.in:
+	Bump to 2.3.12
+
+2003-04-24  Alexander Larsson  <alexl redhat com>
+
+	* art_uta_vpath.c (art_uta_from_vpath):
+	Don't silently stomp on memory on bad vpaths.
+
+2003-04-11  Alexander Larsson  <alexl redhat com>
+
+	* art_svp_vpath_stroke.c (render_seg):
+	Handle cases when dmr2 is very small better.
+
+2003-04-10  Alexander Larsson  <alexl redhat com>
+
+	* art_svp_wind.c (x_order_2):
+	Handle horizontally aligned segments.
+
+2002-11-25  Alexander Larsson  <alexl redhat com>
+
+	* configure.in: Bump to 2.3.11
+
+2002-10-16  John Harper  <jsh unfactored org>
+
+	* art_render_svp.c (art_render_svp_callback,
+	art_render_svp_callback_span, art_render_svp_callback_opacity,
+	art_render_svp_callback_opacity_span): if no runs would
+	normally be emitted, but start is greater than zero, emit a
+	single run covering the entire width of the rendered region
+
+2002-08-18  Havoc Pennington  <hp pobox com>
+
+	* autogen.sh: hardcode aclocal-1.4/automake-1.4 so that users with
+	both automake 1.6 and 1.4 installed get the right automake. Means
+	compilation from CVS will now require the latest automake 1.4
+	release, or manually creating symlinks called "automake-1.4" and
+	"aclocal-1.4"
+
+2002-07-01  Alexander Larsson  <alexl redhat com>
+
+	* Release 2.3.10
+
+2002-07-01  Alexander Larsson  <alexl redhat com>
+
+	* configure.in (LIBART_VERSION_INFO):
+	Bump to 2.3.10
+
+	* art_svp_intersect.c (art_svp_intersect_add_seg):
+	Initialize seg->wind_left to zero, this avoids
+	uninitialized memory read later in art_svp_intersect_horiz_commit.
+
+2002-06-24  Alexander Larsson  <alexl redhat com>
+
+	Release 2.3.9.
+	
+2002-06-24  Alexander Larsson  <alexl redhat com>
+
+	* configure.in: Bump to 2.3.9
+
+2002-06-04  Balamurali Viswanathan <balamurali viswanathan wipro com>
+
+	* Makefile.am: Added -lm to libart_lgpl_2_la_LIBADD
+	fixes bug 75711 (Jacob's suggestion)
+
+2002-05-08  Raph Levien  <raph pixel artofcode com>
+
+	* art_uta_vpath.c (art_uta_add_line): Fixes very subtle
+	edge case found by Federico Mena Quintero: (96, 96) -
+	(96.220200017562348, 93.034868598919431). Previously, through
+	numerical error, xn was a hair to the left, throwing off
+	the Bresenham iteration.
+
+2002-03-08  Tor Lillqvist  <tml iki fi>
+
+	* configure.in: Minor changes for build on Win32. Call
+	AC_LIBTOOL_WIN32_DLL. Set automake conditionals OS_WIN32 and
+	MS_LIB_AVAILABLE.
+
+	* art_affine.c: Include art_misc.h for M_PI, which not necessarily
+	is in math.h.
+
+	* Makefile.am: On Win32, build and install import libraries, both
+	for gcc and for MSVC (if available).
+
+	* libart-zip.in: New file. Used to build runtime and developer
+	package for Win32.
+
+2002-02-06  Laszlo Peter  <laca ireland sun com>
+
+	* configure.in: add AC_FUNC_ALLOCA
+
+	* art_render_gradient.c: copy alloca hacks from glib/galloca.h
+
+2002-02-02  Alexander Larsson  <alla lysator liu se>
+
+	* art_render.c:
+	Add special case for art_render_composite_8 for two cases
+	that are very common when rendering SVGs with librsvg.
+	I'm sure these can be optimized further, but i'm feeling
+	very slow today.
+
+2002-02-01  Alex Larsson  <alexl redhat com>
+
+	* art_render_gradient.c:
+	Add optimized case for depth==8,  n_channels == 3
+
+	* test_gradient.c:
+	Test case for gradients. Uses gtk+, so not built by
+	default.
+
+2002-01-10  Anders Carlsson  <andersca gnu org>
+
+	* Release 2.3.8
+
+2002-01-04  Anders Carlsson  <andersca gnu org>
+
+	* configure.in: Bump version to 2.3.8.
+
+2002-01-03  Darin Adler  <darin bentspoon com>
+
+	* Makefile.am: Add art_render_mask.[ch].
+
+	* art_bpath.h:
+	* art_rect_svp.h:
+	* art_rect_uta.h:
+	* art_render.h:
+	* art_render_gradient.h:
+	* art_render_mask.h:
+	* art_render_svp.h:
+	* art_rgb_svp.h:
+	* art_svp_intersect.h:
+	* art_svp_ops.h:
+	* art_svp_point.h:
+	* art_svp_render_aa.h:
+	* art_svp_vpath.h:
+	* art_svp_vpath_stroke.h:
+	* art_svp_wind.h:
+	* art_uta.h:
+	* art_uta_rect.h:
+	* art_uta_ops.h:
+	* art_uta_svp.h:
+	* art_uta_vpath.h:
+	* art_vpath_bpath.h:
+	* art_vpath_dash.h:
+	* art_vpath_svp.h:
+	Fix includes so that each header includes what it needs.
+
+	* art_affine.c:
+	* art_alphagamma.c:
+	* art_bpath.c:
+	* art_gray_svp.c:
+	* art_misc.c:
+	* art_pixbuf.c:
+	* art_rect.c:
+	* art_rect_svp.c:
+	* art_rect_uta.c:
+	* art_render.c:
+	* art_render_gradient.c:
+	* art_render_mask.c:
+	* art_render_svp.c:
+	* art_rgb.c:
+	* art_rgb_a_affine.c:
+	* art_rgb_affine.c:
+	* art_rgb_affine_private.c:
+	* art_rgb_bitmap_affine.c:
+	* art_rgb_pixbuf_affine.c:
+	* art_rgb_rgba_affine.c:
+	* art_rgb_svp.c:
+	* art_rgba.c:
+	* art_svp.c:
+	* art_svp_intersect.c:
+	* art_svp_ops.c:
+	* art_svp_point.c:
+	* art_svp_render_aa.c:
+	* art_svp_vpath.c:
+	* art_svp_vpath_stroke.c:
+	* art_svp_wind.c:
+	* art_uta.c:
+	* art_uta_ops.c:
+	* art_uta_rect.c:
+	* art_uta_svp.c:
+	* art_uta_vpath.c:
+	* art_vpath.c:
+	* art_vpath_bpath.c:
+	* art_vpath_dash.c:
+	* art_vpath_svp.c:
+	Fix order of includes so that the corresponding header is
+	included first, to test that each header includes what it
+	needs.
+
+2002-01-02  Darin Adler  <darin bentspoon com>
+
+	* art_alphagamma.h:
+	* art_gray_svp.h:
+	* art_pixbuf.h:
+	* art_render.h:
+	* art_rgb.h:
+	* art_rgba.h:
+	Add some missing includes.
+
+2001-12-26  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c: More fixes to intersector. When tops
+	coincide, full ordering test is performed (with breaking),
+	rather than just comparing b from line eq. Also, had a test
+	for x >= backwards in art_svp_intersect_add_point. Thanks
+	to Bruce Q. Hammond for test cases.
+
+2001-11-21  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_intersect_break): Changed break
+	logic systematically so that new breaks are always in order,
+	rather than allowing them to become out of order, then try to fix
+	them up later. It's no doubt possible to come up with cases in
+	which this reduces precision, but it simplifies life nicely, so I
+	did it.
+	
+	* (art_svp_intersect_horiz): Initialize (a, b, c) values of hs to
+	avoid UMR's when they are tested later.
+
+	* art_svp_ops.c (print_ps_vpath): Changed the coordinate
+	transform so that it's done in the PostScript rather than
+	the C code that outputs the coordinates. This makes it easier
+	to reconstruct the vector path from the debug output.
+
+2001-11-16  Alex Larsson  <alexl redhat com>
+
+	* art_rect.c (art_drect_svp_union, art_drect_svp):
+	Don't call art_drect_union() in these functions, since
+	it considers zero-width or zero-height svg segments to
+	be empty. This causes it to think i.e. rectangular svps
+	are empty.
+
+2001-11-07  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_intersect_horiz): Fixed important
+	logic bug, failing to test crossings of neighbors when inserting
+	horizontal lines. Also changed printf to art_dprint.
+
+	* art_misc.c (art_dprint): Added function for debug printing,
+	so verbose intersector output doesn't have to go through printf.
+
+	* art_misc.h (ART_USE_NEW_INTERSECTOR): I've turned this on
+	now, as the new intersector certainly seems to be performing
+	better than the old one now.
+
+2001-10-31  Anders Carlsson  <andersca gnu org>
+
+	* Release 2.3.7
+
+2001-10-15  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_intersect_horiz): Minor
+	logic fix so that horiz segments successfully cross
+	zero length segments in the active list.
+
+	(art_svp_intersect_test_cross): Flags indicating whether to
+	do add_point (potentially breaking neighbors) to left and
+	to right.
+
+	(art_svp_intersect_insert_cross): Provide ART_BREAK_LEFT and
+	ART_BREAK_RIGHT flags to art_svp_intersect_test_cross,
+	depending on direction of search.
+
+	(art_svp_intersect_advance_cursor): Provide flags (allow
+	both left and right breaking) to test_cross.
+	
+2001-10-15  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (CHEAP_SANITYCHECK): Added an inexpensive
+	sanitycheck to detect multiple insertions of a segment into the
+	horiz list.
+
+	(art_svp_writer_rewind_add_point): Avoid breaking lines below
+	their bottom point.
+
+	(art_svp_intersect_test_cross): Handle cases correctly where
+	intersection point matches y0 of left or right segment. These _do_
+	happen in real world examples. Also, do add_point on newly
+	inserted intersection point.
+
+2001-10-14  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_intersect_add_point): Fixed
+	rather subtle logic bug that misplaced insertion point
+	when seg argument was NULL.
+
+2001-10-11  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_render_aa.c (art_svp_render_aa_iter_step): Got rid
+	of qsort of steps, and now keep the step list in sorted order.
+	Further, we combine duplicate steps with the same x value,
+	which bounds the size of the step list to x1 - x0, so we
+	don't need to dynamically resize it. Thanks greatly to
+	Bruce Q. Hammond for the original version of this patch.
+
+2001-10-09  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_intersect_test_cross): Breaks
+	bottom part of line segments in "too close" cases.
+
+2001-10-09  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_writer_rewind_add_point): Fixed
+	bbox computation.
+	(art_svp_intersector): Handle degenerate case where input
+	SVP has 0 segments.
+
+	* art_svp_intersect.h: Moved definition of art_svp_intersector
+	inside #ifdef __cplusplus, so it links properly against C++
+
+2001-10-09  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_intersect_break): Handle
+	case when break y equals sweep line correctly. Also adds
+	first try at winding number sanitychecker, but that makes
+	too many false positives.
+
+2001-10-07  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp.c (EPSILON): Set to zero if new intersector is
+	in use - we want svp's to be in strict sorted order.
+
+	* art_svp_intersect.c (art_svp_intersect_test_cross): Explicitly
+	check that top points are equal, and swap immediately if b is out
+	of order.
+	(art_svp_intersect_horiz): Break segments that intersect
+	horizontal lines. Now passes "two squares with offset" test.
+
+2001-10-05  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c: Major changes to accommodate
+	horizontal lines. Intersections of horizontal lines
+	aren't fully processed, but should work a lot better
+	than before.
+
+	* testart.c: Minor tweaks. testpat now frees memory
+	so it can be run under memprof to detect leaks.
+
+2001-10-03  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_intersect.c (art_svp_intersect_advance_cursor):
+	Made test_cross for inserted segments common between
+	intersection processing and cursor advance, and also took
+	care of a case that hadn't been handled before. Also added
+	invariant sanitychecker for debugging purposes.
+
+2001-10-02  Raph Levien  <raph pixel artofcode com>
+
+	* art_svp_ops.c: ART_USE_NEW_INTERSECTOR variants of svp
+	ops changed to do shallow free of merged svp.
+
+2001-10-01  Raph Levien  <raph acm org>
+
+	* art_svp_intersect.c:
+	* art_svp_intersect.h:
+	* Makefile.am:
+	* art_misc.h:
+	* art_svp_wind.h: First commit of new intersector code. It is
+	  turned off by default, but can be enabled by #defining
+	  ART_USE_NEW_INTERSECTOR in art_misc.h.
+
+	* art_svp_ops.c: Make svp ops use new intersector if enabled.
+
+	* art_svp_vpath_stroke.c: Make vpath stroking use new intersector
+	if enabled.
+	
+	* testart.c: New test case for intersector.
+
+Wed Sep 26 03:48:13 2001  George Lebl <jirka 5z com>
+
+	* Release 2.3.6
+
+Wed Sep 26 03:11:40 2001  George Lebl <jirka 5z com>
+
+	* gen_art_config.c: Fix 64bit issues, cast sizeof return when
+	  using %d to print it.
+
+2001-09-13  Havoc Pennington  <hp redhat com>
+
+	* Makefile.am: rename library to libart_lgpl_2
+
+	* libart-2.0.pc.in (Cflags): move includes
+
+	* libart-config.in: move includes
+
+	* Makefile.am: delete libartConf.sh, rename libart-config
+	(EXTRA_DIST): don't install m4 files
+	(libart_lgplincdir): move headers
+
+2001-08-03  Michael Meeks  <michael ximian com>
+
+	* Version 2.3.5 for the API freeze.
+
+2001-07-12  Darin Adler  <darin bentspoon com>
+
+	* art_affine.c: (art_affine_expansion): Fix handling of
+	negative numbers. We ran into this bug a while back when
+	figuring out why librsvg couldn't handle certain svg files.
+
+2001-07-12  Darin Adler  <darin bentspoon com>
+
+	* art_misc.h: Change art_expand macro so it's a single
+	statement, using the do while (0) trick, which gets rid
+	of some warnings.
+	* art_pixbuf.c: Add a missing include.
+	* art_vpath_svp.c: (art_vpath_from_svp): Initialize a
+	variable to avoid a compiler warning.
+	* gen_art_config.c: Add a missing include.
+
+2001-03-24  Martin Baulig  <baulig suse de>
+
+	Applied the patch from Alexander Larsson which was sitting
+	in gnome-libs/patches/libart.diff since February.
+	[FIXME: Alex, can you please provide a ChangeLog?])
+
+	* art_rgb_a_affine.[ch]: New files.
+
+2001-01-07  Hans Breuer <Hans Breuer Org>
+	* art_misc.c : embryonic change to use libart_lgpl on win32
+	* libart.def : new file, exported functions for win32 dll
+	* makefile.msc : handwritten for MSVC compiler
+
+2000-09-30  Martin Baulig  <baulig suse de>
+
+	* libart-2.0.pc.in: Provide pkg-config script.
+
+	* configure.in: Create libart-2.0.pc from libart-2.0.pc.in.
+	* Makefile.am (pkgconfig_DATA): Install the libart-2.0.pc
+	script in `$(libdir)/pkgconfig'.
+
+2000-08-15  Raph Levien  <raph acm org>
+
+	* art_render.c (art_render_image_solid_negotiate): Only
+	sets ART_IMAGE_SOURCE_CAN_COMPOSITE when a compositing
+	callback is selected. Previously was causing segfaults on
+	non-alpha images. Thanks to Leonard Rosenthol for spotting
+	the bug.
+
+Fri Jun 30 22:56:58 2000  Raph Levien  <raph acm org>
+
+	* art_render.c (art_render_composite): Fixed a bug that caused
+	it to ignore the alpha setting. Also art_render_composite_8().
+
+2000-06-01  John Sullivan  <sullivan eazel com>
+
+	* art_svp_render_aa.c: (art_svp_render_aa_iter_step):
+	Made it build by correcting struct member name from
+	Raph's previous checkin.
+
+Wed May 31 11:10:58 2000  Raph Levien  <raph acm org>
+
+	* art_svp_render_aa.c (art_svp_render_aa_iter_step): Updated
+	n_steps_max in iter structure after steps reallocation.
+
+Tue May 30 10:33:13 2000  Raph Levien  <raph acm org>
+
+	* art_svp_render_aa.c (art_svp_render_aa_iter_step): Fixed not
+	updating iter->steps when steps gets reallocated.
+
+2000-05-30  Pavel Cisler <pavel eazel com>
+
+	* art_rgba.c:
+	Make it build -- fix a broken include.
+
+Tue May 30 00:09:21 2000  Raph Levien  <raph acm org>
+
+	* art_render_gradient.c (art_render_gradient_setpix): Fixed
+	an off-by-one loop error.
+
+Mon May 29 15:00:39 2000  Raph Levien  <raph acm org>
+
+	* Makefile.am: Moved relevant .h files into HEADERS stanza.
+
+Mon May 29 13:48:49 2000  Raph Levien  <raph acm org>
+
+	This is a fairly major commit, as it adds both the new, modular
+	rendering architecture and gradients. Quite a bit of the code
+	feels like "reference code" now, in that it is (hopefully)
+	correct, but not necessarily very fast. In addition, there remain
+	a few things not done, including the use of SVP's as non-driver
+	mask sources. AlphaGamma and filter level also remain
+	unimplemented. No compositing modes other than ART_NORMAL are
+	implemented. All in good time!
+	
+	* configure.in: added -Wmissing-prototypes flag. Bumped version
+	number to 2.3.0.
+
+	* art_render.h:
+	* art_render.c: Added new rendering architecture.
+
+	* art_render_svp.h:
+	* art_render_svp.c: Added renderers to use SVP's as mask
+	sources in new rendering architecture.
+
+	* art_render_gradient.h:
+	* art_render_gradient.c: Added linear and radial gradients
+	as image sources in new rendering architecture.
+
+	* art_rgba.h:
+	* art_rgba.c: Added functions for manipulating and compositing
+	RGBA pixels.
+
+	* art_svp_wind.c: Added static to trap_epsilon(), #ifdef'd out
+	traverse().
+
+	* art_uta_ops.c: Added #include "art_uta_ops.h".
+
+	* art_uta_rect.c: Added #include "art_uta_rect.h".
+
+	* art_uta_svp.h: fixed __ART_UTA_SVP_H__ name.
+
+	* art_misc.h: Added ART_GNUC_NORETURN attribute, added that
+	to the prototype for art_die(). Added "static" to function
+	declarations to avoid warnings when compiled with
+
+	* testart.c: Added gradient test.
+
+Thu May 25 23:30:39 2000  Raph Levien  <raph acm org>
+
+	* art_svp_render_aa.h:
+	* art_svp_render_aa.c: Added art_svp_render_aa_iter functions,
+	suitable for iterative rendering of an svp, one scan line at a
+	time.
+
+	* configure.in: Bumped version to 2.2.0.
+
+Tue May 16 15:03:35 2000  Raph Levien  <raph acm org>
+
+	* art_rgb_pixbuf_affine.c: Included corresponding .h file.
+
+	* art_rgb_pixbuf_affine.h: Put recursive #includes inside
+	LIBART_COMPILATION test.
+
+	* art_gray_svp.c:
+	* art_rgb_svp.c: Explicit casts for callback data. Also removed
+	"render the steps into tmpbuf" comment.
+
+	* gen_art_config.c:
+	* Makefile.am:
+	* configure.in: Added code to automatically generate an
+	art_config.h file, to be installed in libart's include dir. This
+	file defines ART_SIZEOF_{CHAR,SHORT,INT,LONG} and art_u{8,16,32}.
+
+	* art_misc.h: Moved definition of art_u8 and art_u32 into
+	art_config.h. Added GCC printf format attributes.
+
+	* art_svp_wind.c (traverse): Fixed UMR bug here. The function
+	isn't actually used, so it's just for cleanliness.
+
+2000-04-18  Lauris Kaplinski  <lauris ariman ee>
+
+	* art_affine.c (art_affine_to_string): Replaced snprinf with
+	art_ftoa to avoid localisation of generated numbers
+
+2000-04-18  ERDI Gergo  <cactus cactus rulez org>
+
+	* art_rgb_pixbuf_affine.h: Included the ArtPixBuf declaration
+
+Fri Apr 14 16:33:55 2000  Raph Levien  <raph acm org>
+
+	* art_svp_wind.c (art_svp_uncross, art_svp_rewind_uncrossed):
+	Fixed uninitialized memory reads when inserting new segment into
+	active_segs.
+
+	* art_bpath.c (art_bpath_affine_transform): Made it avoid
+	potential uninitialized memory reads when not all the coordinates
+	are needed. Thanks to Morten Welinder for spotting both of these
+	problems.
+
+2000-04-05  Raph Levien  <raph gimp org>
+
+	* art_svp_wind.c: Make "colinear" warnings go to stderr instead
+	of stdout. Of course, when I finish the new intersector, these
+	will go away entirely.
+
+2000-04-04  Raph Levien  <raph gimp org>
+
+	* art_uta_vpath.c (art_uta_add_line): Fixed bug that was causing
+	segfaults on alphas. Thanks to msw for localizing it.
+
+2000-01-17  Raph Levien  <raph gimp org>
+
+	* art_svp_vpath_stroke.c (art_svp_vpath_stroke): Typo in api
+	header (thanks rak).
+
+2000-01-16  Timur Bakeyev  <timur gnu org>
+
+	* autoconf.sh: Instead of jumping between srdir and builddir just process
+	all the auto* staff in srcdir. In fact, just saying the same things in 
+	other words.
+
+2000-01-10  Elliot Lee  <sopwith redhat com>
+
+	* Makefile.am, *.h: Add rather bad hacks to the header files to allow compilation
+
+	* Makefile.am: Distribute libart-config.in
+
+2000-01-09  Raph Levien  <raph gimp org>
+
+	art_rgb_pixbuf_affine.c, art_rgb_rgba_affine.c, art_rgb_svp.c,
+	art_svp.c, art_svp_ops.c, art_svp_point.c, art_svp_render_aa.c,
+	art_svp_vpath.c, art_svp_vpath_stroke.c, art_svp_wind.c,
+	art_uta.c, art_uta_ops.c, art_uta_rect.c, art_uta_svp.c,
+	art_uta_vpath.c, art_vpath.c, art_vpath_bpath.c, art_vpath_dash.c,
+	art_vpath_svp.c: Added API documentation.
+
+Fri Sep 24 17:53:21 1999  Raph Levien  <raph acm org>
+
+	* art_svp_render_aa.c (art_svp_render_insert_active): Avoid
+	reading undefined memory (thanks to Morten Welinder).
+
+1999-09-19  Raph Levien  <raph gimp org>
+
+	* art_pixbuf.c (art_pixbuf_duplicate): Added a duplicate function
+	at the request of Michael Meeks.
+
+1999-09-11  Raph Levien  <raph gimp org>
+
+	* art_affine.c (art_affine_to_string): Tightened the predicate for
+	matching rotate-only affines, which was too weak. Thanks to lewing
+	for spotting it!
+
+1999-09-01  Raph Levien  <raph gimp org>
+
+	* art_affine.c, art_alphagamma.c, art_bpath.c, art_gray_svp.c,
+	art_misc.c, art_pixbuf.c, art_rect.c, art_rect_svp.c,
+	art_rect_uta.c, art_rgb.c, art_rgb_affine.c,
+	art_rgb_bitmap_affine.c: Updates to api doc headers.
+
+1999-08-24  Raph Levien  <raph gimp org>
+
+	* art_affine.c, art_alphagamma.c, art_alphagamma.h, art_bpath.c,
+	art_bpath.h, art_gray_svp.c, art_misc.c, art_pixbuf.c,
+	art_pixbuf.h, art_point.h, art_rect.c, art_rect.h: Added api
+	documentation headers.
+
+	* testart.c: Added "dash" test, for testing the vpath_dash
+	functions.
+
+	* art_rgb_pixbuf_affine.h: Fixed the #ifdef for conditional
+	inclusion. Thanks to Kristian Hogsberg Kristensen for spotting
+	the bug.
+
+1999-08-24  Raph Levien  <raph gimp org>
+
+	* art_svp_render_aa.c (art_svp_render_aa): Added some tests to
+	avoid NaN for infinite slopes, which were causing problems on
+	Alphas. Closes bug #1966.
+
+1999-08-20  Federico Mena Quintero  <federico redhat com>
+
+	* configure.in: Fixed library's libtool version number.
+
+1999-08-03  Larry Ewing  <lewing gimp org>
+
+	* art_vpath_dash.c (art_vpath_dash): fix a bug/typo that was causing
+	certain paths to loop infinitely.
+
+1999-07-28  Raph Levien  <raph gimp org>
+
+	* art_vpath_dash.[ch]: Added a function to add a dash style
+	to vpaths. It is tested, but has a couple of rough edges (see
+	code for details).
+
+	* testart.c: added tests for the new vpath_dash functionality.
+
+	* Makefile.am: added art_vpath_dash.[ch] files.
+
+1999-07-26  Raph Levien  <raph gimp org>
+
+	* art_rgb.c (art_rgb_fill_run): fixed incorrect test for
+	big-endianness. Thanks to Michael Zucchi for spotting it.
+
+Fri Jul 16 23:42:59 1999  Tim Janik  <timj gtk org>
+
+	* art_affine.c (art_affine_flip): flip translation matrixes as well, by
+	inverting matrix[4] if (horz) and inverting matrix[5] if (vert).
+
+Fri Jul 16 23:03:26 1999  Tim Janik  <timj gtk org>
+
+	* art_pixbuf.[hc]: deprecated art_pixbuf_free_shallow(), people should
+	always free pixbufs with art_pixbuf_free() and use the _dnotify variants
+	for specific destruction behaviour.
+	added art_pixbuf_new_rgb_dnotify() and art_pixbuf_new_rgba_dnotify()
+	which allow for a destruction notification function. (this involved
+	adding two extra pointers to the ArtPixBuf structure, and removal of
+	the recently introduced int flags field).
+
+Mon Jul 12 01:13:23 1999  Tim Janik  <timj gtk org>
+
+	* art_affine.[hc]: added art_affine_equal(), which checks two
+	matrixes for equality within grid alignment.
+
+Fri Jul  9 17:50:19 1999  Tim Janik  <timj gtk org>
+
+	* art_affine.[hc]: added art_affine_flip() to flip a matrix horizontally
+	and/or vertically, or just copy it.
+	added art_affine_shear() to setup a shearing matrix.
+
+Tue Jul  6 19:03:39 1999  Tim Janik  <timj gtk org>
+
+	* art_pixbuf.h: added an int flags; member to the end of the
+	structure, it currently only holds information about whether the
+	pixels member should be freed. (raph: i think flags is more generic
+	than free_pixels, so we can reuse that field if further demands popup
+	in the future).
+
+	* art_pixbuf.c:
+	(art_pixbuf_new_const_rgba):
+	(art_pixbuf_new_const_rgb): new functions that prevent the pixels
+	member from being freed upon art_pixbuf_free ().
+	(art_pixbuf_free): only free the pixels member if it is non-NULL and
+	the PIXBUF_FLAG_DESTROY_PIXELS is set.
+
+1999-07-02  Raph Levien  <raph gimp org>
+
+	* art_vpath_bpath.c (art_vpath_render_bez): Bad bad uninitialized
+	variables.
+
+	* configure.in: added compile warnings. Guess why :)
+
+1999-06-28  Raph Levien  <raph gimp org>
+
+	* art_svp_point.h:
+	* art_svp_point.c: Added methods for insideness and distance
+	testing, very useful for ::point methods in canvas items.
+
+	* testart.c: test code to exercise the art_svp_point functions.
+
+	* Makefile.am: Additional entries for art_svp_point.
+
+1999-06-28  Raph Levien  <raph gimp org>
+
+	* art_svp_render_aa.c (art_svp_render_aa): Subtle boundary
+	case in realloc code -- was causing nasty segfaults.
+
+Wed Jun 23 15:05:43 1999  Raph Levien  <raph gimp org>
+
+	* art_rgb_svp.c (art_rgb_svp_alpha_opaque_callback): Missed a
+	case in the anti-segfault crusade. Thanks lewing!
+
+Wed Jun 23 11:16:42 1999  Raph Levien  <raph gimp org>
+
+	* art_rgb_svp.c: Made these routines so they won't segfault even
+	if alpha is out of range. Of course, that begs the question of
+	fixing the render routines so they won't _make_ alpha go out of
+	range, but all in good time.
+
+Fri Jun 18 17:32:34 1999  Raph Levien  <raph acm org>
+
+	* art_vpath_bpath.c (art_bez_path_to_vec): Switched to a new
+	adaptive subdivision algorithm, which (finally!) takes flatness
+	into account. This should result in both smoother curves and
+	faster operation.
+
+Sun Jun 13 21:07:20 1999  Raph Levien  <raph gimp org>
+
+	* art_svp_wind.c (art_svp_rewind_uncrossed): Made the winding
+	rule logic even more correct :). I somehow missed the fact that
+	a clockwise path should be given a winding number of zero;
+	last night's commit tried to make it -1 (which worked for the
+	test cases I was using).
+
+Sun Jun 13 01:23:14 1999  Raph Levien  <raph gimp org>
+
+	* art_svp_wind.c (art_svp_rewind_uncrossed): Change to winding
+	rule logic so that it correctly handles the case where the
+	leftmost segment is negative.
+
+	* Makefile.am (libart_lgplinc_HEADERS): made art_svp_wind.h
+	a public headerfile. This is needed for the bpath canvas item.
+	I'm not sure this is the correct way to do it, but it will do
+	for now.
+
+	* art_vpath_bpath.h: 
+	* art_vpath_bpath.c (art_bez_path_to_vec): Added const to arg.
+
+	* art_vpath_bpath.h: Embarrassing typo.
+
+	* art_bpath.h: Minor tweaks to the #include paths. It is now
+	consistent with the other header files.
+
+Wed Jun  9 20:24:45 1999  Raph Levien  <raph gimp org>
+
+	* art_svp_vpath_stroke.c: Added all remaining line join and cap
+	types, including round, which takes flatness into account. Several
+	new internal functions (art_svp_vpath_stroke_arc) and added
+	flatness argument to a few internal functions. I might want to
+	change the BEVEL join type to MITER for very small turn angles
+	(i.e. within a flatness threshold) for efficiency.
+
+	* art_misc.h: Added M_SQRT2 constant.
+
+Wed Jun  2 21:56:30 1999  Raph Levien  <raph gimp org>
+
+	* art_svp_vpath_stroke.c (art_svp_vpath_stroke_raw): Made the
+	closed path detection capable of PostScript semantics (i.e. it
+	now senses the difference between ART_MOVETO and ART_MOVETO_OPEN).
+
+	* art_svp_vpath_stroke.c (art_svp_vpath_stroke_raw): it now
+	filters out successive points that are (nearly) coincident. This
+	fixes some of the crashes and hangs, including Tim Janik's
+	singularity (trying to stroke MOVETO 50, 50; LINETO 50, 50; END).
+
+	* art_svp_wind.c (art_svp_rewind_uncrossed): added a test to
+	correctly handle empty input svp's.
+
+	* art_svp_wind.c (art_svp_uncross): added a test to correctly
+	handle empty input svp's.
+
+Sun Jan 17 20:53:40 1999  Jeff Garzik  <jgarzik pobox com>
+
+	* art_affine.c:
+	Include string.h for memcpy.
+
+	* art_svp_vpath.c:
+	Remove conflicting static func definition.
+
+	* art_uta_svp.c:
+	Include art_vpath_svp.h for func definition.
+
+Mon Jan  4 12:47:47 1999  Raph Levien  <raph acm org>
+
+	* art_bpath.c (art_bpath_affine_transform): Stupid misnaming
+	of this function (forgot the "art_").
+
+Thu Dec 31 09:04:23 1998  Raph Levien  <raph gimp org>
+
+	* art_affine.c (art_affine_rectilinear): Added this function.
+
+	* art_rect.c (art_drect_affine_transform): Corrected the name (it
+	was right in the .h). Also made it work with non-rectilinear
+	transforms, while I was at it.
+
+Thu Dec 17 11:58:24 1998  Raph Levien  <raph acm org>
+
+	* art_alphagamma.h:
+	* art_alphagamma.c: The real code for alphagamma.
+
+Wed Dec 16 14:18:46 1998  Raph Levien  <raph gimp org>
+
+	* art_alphagamma.h:
+	* art_alphagamma.c: Added. At present, it only contains a dummy
+	stub. When the real code is added, it supports doing alpha
+	compositing in a gamma-corrected color space (suppressing
+	jaggies).
+
+	* art_pixbuf.h:
+	* art_pixbuf.c: Added. This is a virtualization layer over
+	a few different kinds of image formats.
+
+	* art_rgb_pixbuf_affine.h:
+	* art_rgb_pixbuf_affine.c: Added. Supports compositing of
+	generic images over an rgb buffer.
+
+	* art_affine.h: 
+	* art_affine.c (art_affine_expansion): Added this function,
+	which reports the exact scale factor in the case of rotation,
+	scaling, and transformation (an approximate number in the
+	case of shearing or anamorphic distortion).
+
+	* art_misc.h:
+	* art_misc.c (art_warn): Added.
+
+	* art_rgb_affine.h:
+	* art_rgb_affine.c: Added alphagamma argument (not yet implemented).
+
+	* art_rgb_affine_private.c: Fixed typo bug that was causing
+	repaint problems for nonsquare images.
+
+	* art_rgb_bitmap_affine.h:
+	* art_rgb_bitmap_affine.c: Major speed improvement, probably fixed
+	correctness while I was at it. Added alphagamma argument (not yet
+	implemented).
+
+	* art_rgb_svp.h:
+	* art_rgb_svp.c: Added alphagamma argument (only implemented
+	in aa case, not yet alpha case).
+
+	* art_vpath.c: increased perturbation to 2e-3, because the old
+	value (1e-6) was too small.
+
+	* testart.c: added alphagamma.
+	
+	* Makefile.am: added new files
+
+Sun Dec 27 21:45:03 1998  Raph Levien  <raph gimp org>
+
+	* art_rect.h: 
+	* art_rect.c: Added DRect versions of the basic ops (double
+	rather than int).
+
+	* art_rect_svp.h:
+	* art_rect_svp.c: Added. This computes the bounding box of
+	an svp.
+
+Wed Dec 16 14:18:46 1998  Raph Levien  <raph gimp org>
+
+	* art_alphagamma.h:
+	* art_alphagamma.c: Added. At present, it only contains a dummy
+	stub. When the real code is added, it supports doing alpha
+	compositing in a gamma-corrected color space (suppressing
+	jaggies).
+
+	* art_pixbuf.h:
+	* art_pixbuf.c: Added. This is a virtualization layer over
+	a few different kinds of image formats.
+
+	* art_rgb_pixbuf_affine.h:
+	* art_rgb_pixbuf_affine.c: Added. Supports compositing of
+	generic images over an rgb buffer.
+
+	* art_affine.h: 
+	* art_affine.c (art_affine_expansion): Added this function,
+	which reports the exact scale factor in the case of rotation,
+	scaling, and transformation (an approximate number in the
+	case of shearing or anamorphic distortion).
+
+	* art_misc.h:
+	* art_misc.c (art_warn): Added.
+
+	* art_rgb_affine.h:
+	* art_rgb_affine.c: Added alphagamma argument (not yet implemented).
+
+	* art_rgb_affine_private.c: Fixed typo bug that was causing
+	repaint problems for nonsquare images.
+
+	* art_rgb_bitmap_affine.h:
+	* art_rgb_bitmap_affine.c: Major speed improvement, probably fixed
+	correctness while I was at it. Added alphagamma argument (not yet
+	implemented).
+
+	* art_rgb_svp.h:
+	* art_rgb_svp.c: Added alphagamma argument (only implemented
+	in aa case, not yet alpha case).
+
+	* art_vpath.c: increased perturbation to 2e-3, because the old
+	value (1e-6) was too small.
+
+	* testart.c: added alphagamma.
+	
+	* Makefile.am: added new files
+
+Mon Dec 14 00:16:53 1998  Raph Levien  <raph gimp org>
+
+	* art_affine.c (art_affine_to_string): re-added the "scale" method
+	that was accidentally deleted before check-in.
+
+	* Makefile.am: added new files
+
+Sun Dec 13 00:52:39 1998  Raph Levien  <raph gimp org>
+
+	* art_affine.h:
+	* art_affine.c: Added. Everything you ever wanted to do with an
+	affine transform. Especially check the functions that generate
+	concise PostScript strings for affine transforms.	
+
+	* art_filterlevel.h: A simple enum for selecting filtering
+	style.
+
+	* art_rgb_affine.h:
+	* art_rgb_affine.c (art_rgb_affine): Added. This function
+	composites an (opaque) rgb image over an rgb pixel buffer. At
+	present, it's slow and only nearest neighbor filtering is enabled.
+
+	* art_rgb_rgba_affine.h:
+	* art_rgb_rgba_affine.c: Analogous, but for compositing rgba
+	images.
+
+	* art_rgb_bitmap_affine.h:
+	* art_rgb_bitmap_affine.c: Analogous, but for compositing bitmap
+	images.
+
+	* art_rgb_affine_private.c (art_rgb_affine_run): Added. This is
+	a common function used by all the rgb_affine modules to move
+	testing for source image bbox out of the inner loop.
+
+	* Makefile.am: added the new files
+
+	* testart.c: exercise the image compositors
+
+Wed Dec  9 23:36:35 1998  Raph Levien  <raph gimp org>
+
+	* art_vpath.c (art_vpath_perturb): Made it deal correctly
+	with closed paths (the MOVETO and closing LINETO have to
+	agree).
+
+	* art_svp_wind.c: Made the bbox calculations for the resulting
+	svp's correct.
+
+	* art_svp.h:
+	* art_svp.c: The art_svp_seg_compare function moved here, because
+	it's required in art_svp_ops.
+
+	* art_svp.c (art_svp_add_segment): It now does bbox calculations.
+
+	* art_svp_ops.h: 
+	* art_svp_ops.c: Added. Populated with basic union, intersection,
+	and diff functions.
+
+	* art_vpath_svp.h: 
+	* art_vpath_svp.c: Added. Populated with a function to convert
+	from sorted to unsorted vector paths
+
+	* Makefile.am: added the new files
+
+	* testart.c: exercise the stroke outline and vector path
+	operations.
+
+1998-12-08  Herbert Valerio Riedel  <hvr hvrlab ml org>
+
+	* art_svp_wind.c: added #include <string.h> for memcpy()
+
+Sun Dec  6 22:15:12 1998  Raph Levien  <raph gimp org>
+
+	* art_svp_wind.[ch], art_svp_vpath_stroke.[ch]: Added, but it
+	doesn't work yet. These will do stroke outline and basic
+	vector ops like union, intersect, etc.
+
+	* art_svp_render_aa.c: Added a simple speedup based on bbox
+	culling. I will want to do more speedups, but none of this is
+	necessary for the freeze.
+
+	* art_svp_vpath.c: Fixed some bugs in the art_svp_from_vpath in
+	cases where there is more than one subpath.
+
+	* art_vpath.h:
+	* art_vpath.c (art_vpath_perturb): Added this function.  This will
+	help cheat as long as the basic vector ops have numerical
+	stability problems.
+
+Fri Dec  4 18:00:38 1998  Raph Levien  <raph gimp org>
+
+	* art_svp_render_aa.c (art_svp_render_aa): Changed the api
+	slightly, to guarantee that the steps are all within the range
+	from x0 (inclusive) to x1 (exclusive).
+
+	* art_gray_svp.c, art_gray_svp.h: Added. Populated with functions
+	to render into a simple graymap.
+
+	* art_rgb.c, art_rgb.c: Added. Populated with fill_run and
+	run_alpha methods.
+
+	* art_rgb_svp.c, art_rgb_svp.h: Added. Populated with functions to
+	render into an RGB buffer, and to composite over an RGB buffer.
+
+	* Makefile.am: added art_gray_svp, art_rgb, and art_rgb_svp.
+
+	* testart.c: test the color and layered rendering.
+
+Mon Nov 30 01:30:25 1998  Raph Levien  <raph gimp org>
+
+	* testart.c: added vector path rendering stuff. Some of it needs
+	to go out of the test framework and into the module, but the
+	api hasn't settled down entirely yet (in the real code, all
+	x's in the step field are within bounds).
+
+	* art_svp_render_aa.c, art_svp_render_aa.c.h: added.
+
+	* art_svp_vpath.c, art_svp_vpath.h: added.
+
+	* art_pathcode.h: added ART_MOVETO_OPEN (libart uses an
+	ART_MOVETO_OPEN code at the beginning to indicate an open path,
+	while PostScript uses the lack of a closepath at the end).
+
+	* art_vpath_bpath.c, art_vpath_bpath.h: fixed it up, added
+	flatness arg to api.
+
+	* Makefile.am: added new source files.
+
+Wed Nov 25 17:19:44 1998  Raph Levien  <raph gimp org>
+
+	* art_svp.h, art_svp.c: added, basic constructors for sorted
+	vector paths.
+
+Sun Nov 22 23:21:09 1998  Raph Levien  <raph gimp org>
+
+	* Makefile.am (libart_lgplincdir): Fixed stupid bug in naming of
+	the variable.
+
+Sun Nov 22 21:41:13 1998  Raph Levien  <raph gimp org>
+
+	* art_uta_vpath.c: moved art_uta_union into art_uta_ops.
+
+	* art_uta_ops.[ch]: added, populated with art_uta_union.
+
+Thu Nov 19 00:19:40 1998  Raph Levien  <raph gimp org>
+
+	* libartConf.sh.in: added
+
+	* Makefile.am: added creation of libartConf.sh, added -version-info
+	* configure.in: added LIBART_VERSION_INFO, support for libartConf.sh
+	
+	* libart.m4: updated version history :)
+
+Wed Nov 18 18:15:20 1998  Raph Levien  <raph gimp org>
+
+	* configure.in (LIBART_VERSION): set this, so that libart-config
+	--version now works.
+
+Wed Nov 18 16:50:58 1998  Raph Levien  <raph gimp org>
+
+	* libart.m4: added (just copied from esound)
+	* configure.in, Makefile.am: added support for libart-config
+	* libart-config.in: added (mostly copied from esound)
+
+Tue Nov 10 12:43:30 1998  Raph Levien  <raph acm org>
+
+	* Getting the library in shape for initial checkin to CVS.
+
+
diff --git a/src/libart_lgpl/Makefile.am b/src/libart_lgpl/Makefile.am
new file mode 100644
index 0000000..0cc4d28
--- /dev/null
+++ b/src/libart_lgpl/Makefile.am
@@ -0,0 +1,109 @@
+noinst_PROGRAMS = gen_art_config
+
+BUILT_SOURCES = art_config.h
+
+art_config.h:	gen_art_config$(EXEEXT)
+	./gen_art_config > art_config.h
+
+noinst_LIBRARIES = libart_lgpl_2.a
+
+libm = -lm
+
+libart_lgpl_2_a_headers = \
+	art_affine.h \
+	art_alphagamma.h \
+	art_bpath.h \
+	art_config.h \
+	art_filterlevel.h \
+	art_gray_svp.h \
+	art_misc.h \
+	art_pathcode.h \
+	art_pixbuf.h \
+	art_point.h \
+	art_rect.h \
+	art_rect_svp.h \
+	art_rect_uta.h \
+	art_render.h \
+	art_render_gradient.h \
+	art_render_mask.h \
+	art_render_svp.h \
+	art_rgb.h \
+	art_rgb_affine.h \
+	art_rgb_bitmap_affine.h \
+	art_rgb_pixbuf_affine.h \
+	art_rgb_rgba_affine.h \
+	art_rgb_a_affine.h \
+	art_rgb_svp.h \
+	art_rgba.h \
+	art_svp.h \
+	art_svp_intersect.h \
+	art_svp_ops.h \
+	art_svp_point.h \
+	art_svp_render_aa.h \
+	art_svp_vpath.h \
+	art_svp_vpath_stroke.h \
+	art_svp_wind.h \
+	art_uta.h \
+	art_uta_ops.h \
+	art_uta_rect.h \
+	art_uta_vpath.h \
+	art_uta_svp.h \
+	art_vpath.h \
+	art_vpath_bpath.h \
+	art_vpath_dash.h \
+	art_vpath_svp.h	\
+	libart.h	\
+	libart-features.h
+
+libart_lgpl_2_a_SOURCES = \
+	$(libart_lgpl_2_a_headers) \
+	art_affine.c \
+	art_alphagamma.c \
+	art_bpath.c \
+	art_gray_svp.c \
+	art_misc.c \
+	art_pixbuf.c \
+	art_rect.c \
+	art_rect_svp.c \
+	art_rect_uta.c \
+	art_render.c \
+	art_render_gradient.c \
+	art_render_mask.c \
+	art_render_svp.c \
+	art_rgb.c \
+	art_rgb_affine.c \
+	art_rgb_affine_private.c \
+	art_rgb_affine_private.h \
+	art_rgb_bitmap_affine.c \
+	art_rgb_pixbuf_affine.c \
+	art_rgb_rgba_affine.c \
+	art_rgb_a_affine.c \
+	art_rgba.c \
+	art_rgb_svp.c \
+	art_svp.c \
+	art_svp_intersect.c \
+	art_svp_ops.c \
+	art_svp_point.c \
+	art_svp_render_aa.c \
+	art_svp_vpath.c \
+	art_svp_vpath_stroke.c \
+	art_svp_wind.c \
+	art_uta.c \
+	art_uta_ops.c \
+	art_uta_rect.c \
+	art_uta_vpath.c \
+	art_uta_svp.c \
+	art_vpath.c \
+	art_vpath_bpath.c \
+	art_vpath_dash.c \
+	art_vpath_svp.c	\
+	libart-features.c
+
+libart_lgplincdir = $(top_srcdir)/src
+INCLUDES = -I$(top_srcdir) -I$(top_builddir) -DLIBART_COMPILATION
+
+DEPS = $(top_builddir)/libart_lgpl_2.a
+LDADDS = $(top_builddir)/libart_lgpl_2.a
+
+CLEANFILES = $(BUILT_SOURCES)
+DISTCLEANFILES = $(BUILT_SOURCES)
diff --git a/src/libart_lgpl/NEWS b/src/libart_lgpl/NEWS
new file mode 100644
index 0000000..aa0007e
--- /dev/null
+++ b/src/libart_lgpl/NEWS
@@ -0,0 +1,23 @@
+==============
+Version 2.3.19
+==============
+
+- Fix typo that broke versioning. (Yanko Kaneti)
+
+==============
+Version 2.3.18
+==============
+
+- Port to automake 1.9 (Christian Persch)
+- Cygwin build fixes (Cygwin ports maintainer)
+- Fixes for memory management on win32 (Dom Lachowicz)
+- Compiler warning fixes (Kjartan, Tor Lillqvist)
+
+==============
+Version 2.3.17
+==============
+
+- Updated pkg-config files (Glynn Foster)
+- Distribute libart.def (Michael Meeks)
+- Add link to Mathieu's tutorial on libart (Kjartan)
+
diff --git a/src/libart_lgpl/README b/src/libart_lgpl/README
new file mode 100644
index 0000000..882527e
--- /dev/null
+++ b/src/libart_lgpl/README
@@ -0,0 +1,19 @@
+This is the LGPL'd component of libart. All functions needed for
+running the Gnome canvas, and for printing support, will be going in
+here. The GPL'd component will be getting various enhanced functions
+for specific applications.
+
+Libart is free software. It is also for sale. For information about
+licensing libart, please contact Raph Levien
+<raph acm org>. Contributions to the codebase are also very welcome,
+but the copyright must be assigned in writing to preserve the
+licensing flexibility.
+
+
+For more information about libart, see the web page:
+
+   http://www.levien.com/libart/
+
+There's also a libart tutorial available at
+http://www.gnome.org/~mathieu/libart/libart.html
+
diff --git a/src/libart_lgpl/README.CVS b/src/libart_lgpl/README.CVS
new file mode 100644
index 0000000..7ba42f7
--- /dev/null
+++ b/src/libart_lgpl/README.CVS
@@ -0,0 +1,14 @@
+Welcome to the libart source tree!
+
+This code is being developed in a "free software for sale"
+model. Thus, it's available under a free software license (LGPL for
+this module), but I'm also requesting that for all changes to the code
+the copyright gets assigned back to me.
+
+So if you want to contribute, please do, but contact me about getting
+the copyright assigned. Otherwise, I will have to back your changes
+out.
+
+Thanks!
+
+Raph Levien <raph acm org>
diff --git a/src/libart_lgpl/art_affine.c b/src/libart_lgpl/art_affine.c
new file mode 100644
index 0000000..90c67bc
--- /dev/null
+++ b/src/libart_lgpl/art_affine.c
@@ -0,0 +1,457 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Simple manipulations with affine transformations */
+
+#include "config.h"
+#include "art_affine.h"
+#include "art_misc.h" /* for M_PI */
+
+#include <math.h>
+#include <stdio.h> /* for sprintf */
+#include <string.h> /* for strcpy */
+
+
+/* According to a strict interpretation of the libart structure, this
+   routine should go into its own module, art_point_affine.  However,
+   it's only two lines of code, and it can be argued that it is one of
+   the natural basic functions of an affine transformation.
+*/
+
+/**
+ * art_affine_point: Do an affine transformation of a point.
+ * @dst: Where the result point is stored.
+ * @src: The original point.
+ @ @affine: The affine transformation.
+ **/
+void
+art_affine_point (ArtPoint *dst, const ArtPoint *src,
+		  const double affine[6])
+{
+  double x, y;
+
+  x = src->x;
+  y = src->y;
+  dst->x = x * affine[0] + y * affine[2] + affine[4];
+  dst->y = x * affine[1] + y * affine[3] + affine[5];
+}
+
+/**
+ * art_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
+art_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];
+}
+
+/**
+ * art_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
+art_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
+
+/* It's ridiculous I have to write this myself. This is hardcoded to
+   six digits of precision, which is good enough for PostScript.
+
+   The return value is the number of characters (i.e. strlen (str)).
+   It is no more than 12. */
+static int
+art_ftoa (char str[80], double x)
+{
+  char *p = str;
+  int i, j;
+
+  p = str;
+  if (fabs (x) < EPSILON / 2)
+    {
+      strcpy (str, "0");
+      return 1;
+    }
+  if (x < 0)
+    {
+      *p++ = '-';
+      x = -x;
+    }
+  if ((int)floor ((x + EPSILON / 2) < 1))
+    {
+      *p++ = '0';
+      *p++ = '.';
+      i = sprintf (p, "%06d", (int)floor ((x + EPSILON / 2) * 1e6));
+      while (i && p[i - 1] == '0')
+	i--;
+      if (i == 0)
+	i--;
+      p += i;
+    }
+  else if (x < 1e6)
+    {
+      i = sprintf (p, "%d", (int)floor (x + EPSILON / 2));
+      p += i;
+      if (i < 6)
+	{
+	  int ix;
+
+	  *p++ = '.';
+	  x -= floor (x + EPSILON / 2);
+	  for (j = i; j < 6; j++)
+	    x *= 10;
+	  ix = floor (x + 0.5);
+
+	  for (j = 0; j < i; j++)
+	    ix *= 10;
+
+	  /* A cheap hack, this routine can round wrong for fractions
+	     near one. */
+	  if (ix == 1000000)
+	    ix = 999999;
+
+	  sprintf (p, "%06d", ix);
+	  i = 6 - i;
+	  while (i && p[i - 1] == '0')
+	    i--;
+	  if (i == 0)
+	    i--;
+	  p += i;
+	}
+    }
+  else
+    p += sprintf (p, "%g", x);
+
+  *p = '\0';
+  return p - str;
+}
+
+
+
+#include <stdlib.h>
+/**
+ * art_affine_to_string: Convert affine transformation to concise PostScript string representation.
+ * @str: Where to store the resulting string.
+ * @src: The affine transform.
+ *
+ * Converts an affine transform into a bit of PostScript code that
+ * implements the transform. Special cases of scaling, rotation, and
+ * translation are detected, and the corresponding PostScript
+ * operators used (this greatly aids understanding the output
+ * generated). The identity transform is mapped to the null string.
+ **/
+void
+art_affine_to_string (char str[128], const double src[6])
+{
+  char tmp[80];
+  int i, ix;
+
+#if 0
+  for (i = 0; i < 1000; i++)
+    {
+      double d = rand () * .1 / RAND_MAX;
+      art_ftoa (tmp, d);
+      printf ("%g %f %s\n", d, d, tmp);
+    }
+#endif
+  if (fabs (src[4]) < EPSILON && fabs (src[5]) < EPSILON)
+    {
+      /* could be scale or rotate */
+      if (fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON)
+	{
+	  /* scale */
+	  if (fabs (src[0] - 1) < EPSILON && fabs (src[3] - 1) < EPSILON)
+	    {
+	      /* identity transform */
+	      str[0] = '\0';
+	      return;
+	    }
+	  else
+	    {
+	      ix = 0;
+	      ix += art_ftoa (str + ix, src[0]);
+	      str[ix++] = ' ';
+	      ix += art_ftoa (str + ix, src[3]);
+	      strcpy (str + ix, " scale");
+	      return;
+	    }
+	}
+      else
+	{
+	  /* could be rotate */
+	  if (fabs (src[0] - src[3]) < EPSILON &&
+	      fabs (src[1] + src[2]) < EPSILON &&
+	      fabs (src[0] * src[0] + src[1] * src[1] - 1) < 2 * EPSILON)
+	    {
+	      double theta;
+
+	      theta = (180 / M_PI) * atan2 (src[1], src[0]);
+	      art_ftoa (tmp, theta);
+	      sprintf (str, "%s rotate", tmp);
+	      return;
+	    }
+	}
+    }
+  else
+    {
+      /* could be translate */
+      if (fabs (src[0] - 1) < EPSILON && fabs (src[1]) < EPSILON &&
+	  fabs (src[2]) < EPSILON && fabs (src[3] - 1) < EPSILON)
+	{
+	  ix = 0;
+	  ix += art_ftoa (str + ix, src[4]);
+	  str[ix++] = ' ';
+	  ix += art_ftoa (str + ix, src[5]);
+	  strcpy (str + ix, " translate");
+	  return;
+	}
+    }
+
+  ix = 0;
+  str[ix++] = '[';
+  str[ix++] = ' ';
+  for (i = 0; i < 6; i++)
+    {
+      ix += art_ftoa (str + ix, src[i]);
+      str[ix++] = ' ';
+    }
+  strcpy (str + ix, "] concat");
+}
+
+/**
+ * art_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
+art_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;
+}
+
+/**
+ * art_affine_identity: Set up the identity matrix.
+ * @dst: Where to store the resulting affine transform.
+ *
+ * Sets up an identity matrix.
+ **/
+void
+art_affine_identity (double dst[6])
+{
+  dst[0] = 1;
+  dst[1] = 0;
+  dst[2] = 0;
+  dst[3] = 1;
+  dst[4] = 0;
+  dst[5] = 0;
+}
+
+
+/**
+ * art_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
+art_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;
+}
+
+/**
+ * art_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
+art_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;
+}
+
+/**
+ * art_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
+art_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;
+}
+
+/**
+ * art_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
+art_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;
+}
+
+/**
+ * art_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
+art_affine_expansion (const double src[6])
+{
+  return sqrt (fabs (src[0] * src[3] - src[1] * src[2]));
+}
+
+/**
+ * art_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
+art_affine_rectilinear (const double src[6])
+{
+  return ((fabs (src[1]) < EPSILON && fabs (src[2]) < EPSILON) ||
+	  (fabs (src[0]) < EPSILON && fabs (src[3]) < EPSILON));
+}
+
+/**
+ * art_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
+art_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/src/libart_lgpl/art_affine.h b/src/libart_lgpl/art_affine.h
new file mode 100644
index 0000000..d54eb33
--- /dev/null
+++ b/src/libart_lgpl/art_affine.h
@@ -0,0 +1,92 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_AFFINE_H__
+#define __ART_AFFINE_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_point.h"
+#else
+#include <libart_lgpl/art_point.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_affine_point (ArtPoint *dst, const ArtPoint *src,
+		  const double affine[6]);
+
+void
+art_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
+art_affine_flip (double dst_affine[6], const double src_affine[6],
+                 int horz, int vert);
+
+void
+art_affine_to_string (char str[128], const double src[6]);
+
+void
+art_affine_multiply (double dst[6],
+		     const double src1[6], const double src2[6]);
+
+/* set up the identity matrix */
+void
+art_affine_identity (double dst[6]);
+
+/* set up a scaling matrix */
+void
+art_affine_scale (double dst[6], double sx, double sy);
+
+/* set up a rotation matrix; theta is given in degrees */
+void
+art_affine_rotate (double dst[6], double theta);
+
+/* set up a shearing matrix; theta is given in degrees */
+void
+art_affine_shear (double dst[6], double theta);
+
+/* set up a translation matrix */
+void
+art_affine_translate (double dst[6], double tx, double ty);
+
+
+/* find the affine's "expansion factor", i.e. the scale amount */
+double
+art_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
+art_affine_rectilinear (const double src[6]);
+
+/* Determine whether two affine transformations are equal within grid allignment */
+int
+art_affine_equal (double matrix1[6], double matrix2[6]);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_AFFINE_H__ */
diff --git a/src/libart_lgpl/art_alphagamma.c b/src/libart_lgpl/art_alphagamma.c
new file mode 100644
index 0000000..ba10ed2
--- /dev/null
+++ b/src/libart_lgpl/art_alphagamma.c
@@ -0,0 +1,84 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Some functions to build alphagamma tables */
+
+#include "config.h"
+#include "art_alphagamma.h"
+
+#include <math.h>
+
+/**
+ * art_alphagamma_new: Create a new #ArtAlphaGamma.
+ * @gamma: Gamma value.
+ *
+ * Create a new #ArtAlphaGamma for a specific value of @gamma. When
+ * correctly implemented (which is generally not the case in libart),
+ * alpha compositing with an alphagamma parameter is equivalent to
+ * applying the gamma transformation to source images, doing the alpha
+ * compositing (in linear intensity space), then applying the inverse
+ * gamma transformation, bringing it back to a gamma-adjusted
+ * intensity space.
+ *
+ * Return value: The newly created #ArtAlphaGamma.
+ **/
+ArtAlphaGamma *
+art_alphagamma_new (double gamma)
+{
+  int tablesize;
+  ArtAlphaGamma *alphagamma;
+  int i;
+  int *table;
+  art_u8 *invtable;
+  double s, r_gamma;
+
+  tablesize = ceil (gamma * 8);
+  if (tablesize < 10)
+    tablesize = 10;
+
+  alphagamma = (ArtAlphaGamma *)art_alloc (sizeof(ArtAlphaGamma) +
+					   ((1 << tablesize) - 1) *
+					   sizeof(art_u8));
+  alphagamma->gamma = gamma;
+  alphagamma->invtable_size = tablesize;
+
+  table = alphagamma->table;
+  for (i = 0; i < 256; i++)
+    table[i] = (int)floor (((1 << tablesize) - 1) *
+			   pow (i * (1.0 / 255), gamma) + 0.5);
+
+  invtable = alphagamma->invtable;
+  s = 1.0 / ((1 << tablesize) - 1);
+  r_gamma = 1.0 / gamma;
+  for (i = 0; i < 1 << tablesize; i++)
+    invtable[i] = (int)floor (255 * pow (i * s, r_gamma) + 0.5);
+
+  return alphagamma;
+}
+
+/**
+ * art_alphagamma_free: Free an #ArtAlphaGamma.
+ * @alphagamma: An #ArtAlphaGamma.
+ *
+ * Frees the #ArtAlphaGamma.
+ **/
+void
+art_alphagamma_free (ArtAlphaGamma *alphagamma)
+{
+  art_free (alphagamma);
+}
diff --git a/src/libart_lgpl/art_alphagamma.h b/src/libart_lgpl/art_alphagamma.h
new file mode 100644
index 0000000..874096e
--- /dev/null
+++ b/src/libart_lgpl/art_alphagamma.h
@@ -0,0 +1,54 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_ALPHAGAMMA_H__
+#define __ART_ALPHAGAMMA_H__
+
+/* Alphagamma tables */
+
+#ifdef LIBART_COMPILATION
+#include "art_misc.h"
+#else
+#include <libart_lgpl/art_misc.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtAlphaGamma ArtAlphaGamma;
+
+struct _ArtAlphaGamma {
+  /*< private >*/
+  double gamma;
+  int invtable_size;
+  int table[256];
+  art_u8 invtable[1];
+};
+
+ArtAlphaGamma *
+art_alphagamma_new (double gamma);
+
+void
+art_alphagamma_free (ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_H__ */
diff --git a/src/libart_lgpl/art_bpath.c b/src/libart_lgpl/art_bpath.c
new file mode 100644
index 0000000..5ce519d
--- /dev/null
+++ b/src/libart_lgpl/art_bpath.c
@@ -0,0 +1,91 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for bezier paths */
+
+#include "config.h"
+#include "art_bpath.h"
+
+#include <math.h>
+
+
+/**
+ * art_bpath_affine_transform: Affine transform an #ArtBpath.
+ * @src: The source #ArtBpath.
+ * @matrix: The affine transform.
+ *
+ * Affine transform the bezpath, returning a newly allocated #ArtBpath
+ * (allocated using art_alloc()).
+ *
+ * Result (x', y') = (matrix[0] * x + matrix[2] * y + matrix[4],
+ *                    matrix[1] * x + matrix[3] * y + matrix[5])
+ *
+ * Return value: the transformed #ArtBpath.
+ **/
+ArtBpath *
+art_bpath_affine_transform (const ArtBpath *src, const double matrix[6])
+{
+  int i;
+  int size;
+  ArtBpath *new;
+  ArtPathcode code;
+  double x, y;
+
+  for (i = 0; src[i].code != ART_END; i++);
+  size = i;
+
+  new = art_new (ArtBpath, size + 1);
+
+  for (i = 0; i < size; i++)
+    {
+      code = src[i].code;
+      new[i].code = code;
+      if (code == ART_CURVETO)
+	{
+	  x = src[i].x1;
+	  y = src[i].y1;
+	  new[i].x1 = matrix[0] * x + matrix[2] * y + matrix[4];
+	  new[i].y1 = matrix[1] * x + matrix[3] * y + matrix[5];
+	  x = src[i].x2;
+	  y = src[i].y2;
+	  new[i].x2 = matrix[0] * x + matrix[2] * y + matrix[4];
+	  new[i].y2 = matrix[1] * x + matrix[3] * y + matrix[5];
+	}
+      else
+	{
+	  new[i].x1 = 0;
+	  new[i].y1 = 0;
+	  new[i].x2 = 0;
+	  new[i].y2 = 0;
+	}
+      x = src[i].x3;
+      y = src[i].y3;
+      new[i].x3 = matrix[0] * x + matrix[2] * y + matrix[4];
+      new[i].y3 = matrix[1] * x + matrix[3] * y + matrix[5];
+    }
+  new[i].code = ART_END;
+  new[i].x1 = 0;
+  new[i].y1 = 0;
+  new[i].x2 = 0;
+  new[i].y2 = 0;
+  new[i].x3 = 0;
+  new[i].y3 = 0;
+
+  return new;
+}
+
diff --git a/src/libart_lgpl/art_bpath.h b/src/libart_lgpl/art_bpath.h
new file mode 100644
index 0000000..2ef947b
--- /dev/null
+++ b/src/libart_lgpl/art_bpath.h
@@ -0,0 +1,58 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_BPATH_H__
+#define __ART_BPATH_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_pathcode.h"
+#else
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_point.h>
+#include <libart_lgpl/art_pathcode.h>
+#endif
+
+/* Basic data structures and constructors for bezier paths */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtBpath ArtBpath;
+
+struct _ArtBpath {
+  /*< public >*/
+  ArtPathcode code;
+  double x1;
+  double y1;
+  double x2;
+  double y2;
+  double x3;
+  double y3;
+};
+
+ArtBpath *
+art_bpath_affine_transform (const ArtBpath *src, const double matrix[6]);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_BPATH_H__ */
diff --git a/src/libart_lgpl/art_filterlevel.h b/src/libart_lgpl/art_filterlevel.h
new file mode 100644
index 0000000..7ce640b
--- /dev/null
+++ b/src/libart_lgpl/art_filterlevel.h
@@ -0,0 +1,67 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_FILTERLEVEL_H__
+#define __ART_FILTERLEVEL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+  ART_FILTER_NEAREST,
+  ART_FILTER_TILES,
+  ART_FILTER_BILINEAR,
+  ART_FILTER_HYPER
+} ArtFilterLevel;
+
+/* NEAREST is nearest neighbor. It is the fastest and lowest quality.
+
+   TILES is an accurate simulation of the PostScript image operator
+   without any interpolation enabled; each pixel is rendered as a tiny
+   parallelogram of solid color, the edges of which are implemented
+   with antialiasing. It resembles nearest neighbor for enlargement,
+   and bilinear for reduction.
+
+   BILINEAR is bilinear interpolation. For enlargement, it is
+   equivalent to point-sampling the ideal bilinear-interpolated
+   image. For reduction, it is equivalent to laying down small tiles
+   and integrating over the coverage area.
+
+   HYPER is the highest quality reconstruction function. It is derived
+   from the hyperbolic filters in Wolberg's "Digital Image Warping,"
+   and is formally defined as the hyperbolic-filter sampling the ideal
+   hyperbolic-filter interpolated image (the filter is designed to be
+   idempotent for 1:1 pixel mapping). It is the slowest and highest
+   quality.
+
+   Note: at this stage of implementation, most filter modes are likely
+   not to be implemented.
+
+   Note: cubic filtering is missing from this list, because there isn't
+   much point - hyper is just as fast to implement and slightly better
+   in quality.
+
+*/
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_PATHCODE_H__ */
diff --git a/src/libart_lgpl/art_gray_svp.c b/src/libart_lgpl/art_gray_svp.c
new file mode 100644
index 0000000..cb82049
--- /dev/null
+++ b/src/libart_lgpl/art_gray_svp.c
@@ -0,0 +1,122 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Render a sorted vector path into a graymap. */
+
+#include "config.h"
+#include "art_gray_svp.h"
+
+#include <string.h>	/* for memset */
+#include "art_misc.h"
+
+#include "art_svp.h"
+#include "art_svp_render_aa.h"
+
+typedef struct _ArtGraySVPData ArtGraySVPData;
+
+struct _ArtGraySVPData {
+  art_u8 *buf;
+  int rowstride;
+  int x0, x1;
+};
+
+static void
+art_gray_svp_callback (void *callback_data, int y,
+		       int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtGraySVPData *data = (ArtGraySVPData *)callback_data;
+  art_u8 *linebuf;
+  int run_x0, run_x1;
+  int running_sum = start;
+  int x0, x1;
+  int k;
+
+#if 0
+  printf ("start = %d", start);
+  running_sum = start;
+  for (k = 0; k < n_steps; k++)
+    {
+      running_sum += steps[k].delta;
+      printf (" %d:%d", steps[k].x, running_sum >> 16);
+    }
+  printf ("\n");
+#endif
+
+  linebuf = data->buf;
+  x0 = data->x0;
+  x1 = data->x1;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > x0)
+	memset (linebuf, running_sum >> 16, run_x1 - x0);
+
+      for (k = 0; k < n_steps - 1; k++)
+	{
+	  running_sum += steps[k].delta;
+	  run_x0 = run_x1;
+	  run_x1 = steps[k + 1].x;
+	  if (run_x1 > run_x0)
+	    memset (linebuf + run_x0 - x0, running_sum >> 16, run_x1 - run_x0);
+	}
+      running_sum += steps[k].delta;
+      if (x1 > run_x1)
+	memset (linebuf + run_x1 - x0, running_sum >> 16, x1 - run_x1);
+    }
+  else
+    {
+      memset (linebuf, running_sum >> 16, x1 - x0);
+    }
+
+  data->buf += data->rowstride;
+}
+
+/**
+ * art_gray_svp_aa: Render the vector path into the bytemap.
+ * @svp: The SVP to render.
+ * @x0: The view window's left coord.
+ * @y0: The view window's top coord.
+ * @x1: The view window's right coord.
+ * @y1: The view window's bottom coord.
+ * @buf: The buffer where the bytemap is stored.
+ * @rowstride: the rowstride for @buf.
+ *
+ * Each pixel gets a value proportional to the area within the pixel
+ * overlapping the (filled) SVP. Pixel (x, y) is stored at:
+ *
+ *    @buf[(y - * @y0) * @rowstride + (x - @x0)]
+ *
+ * All pixels @x0 <= x < @x1, @y0 <= y < @y1 are generated. A
+ * stored value of zero is no coverage, and a value of 255 is full
+ * coverage. The area within the pixel (x, y) is the region covered
+ * by [x..x+1] and [y..y+1].
+ **/
+void
+art_gray_svp_aa (const ArtSVP *svp,
+		 int x0, int y0, int x1, int y1,
+		 art_u8 *buf, int rowstride)
+{
+  ArtGraySVPData data;
+
+  data.buf = buf;
+  data.rowstride = rowstride;
+  data.x0 = x0;
+  data.x1 = x1;
+  art_svp_render_aa (svp, x0, y0, x1, y1, art_gray_svp_callback, &data);
+}
diff --git a/src/libart_lgpl/art_gray_svp.h b/src/libart_lgpl/art_gray_svp.h
new file mode 100644
index 0000000..e6c79b9
--- /dev/null
+++ b/src/libart_lgpl/art_gray_svp.h
@@ -0,0 +1,45 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Render a sorted vector path into a graymap. */
+
+#ifndef __ART_GRAY_SVP_H__
+#define __ART_GRAY_SVP_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_misc.h"
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_gray_svp_aa (const ArtSVP *svp,
+		 int x0, int y0, int x1, int y1,
+		 art_u8 *buf, int rowstride);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_GRAY_SVP_H__ */
diff --git a/src/libart_lgpl/art_misc.c b/src/libart_lgpl/art_misc.c
new file mode 100644
index 0000000..8d68e9f
--- /dev/null
+++ b/src/libart_lgpl/art_misc.c
@@ -0,0 +1,92 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Various utility functions RLL finds useful. */
+
+#include "config.h"
+#include "art_misc.h"
+
+#ifdef HAVE_UINSTD_H
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+
+/**
+ * art_die: Print the error message to stderr and exit with a return code of 1.
+ * @fmt: The printf-style format for the error message.
+ *
+ * Used for dealing with severe errors.
+ **/
+void
+art_die (const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+  exit (1);
+}
+
+/**
+ * art_warn: Print the warning message to stderr.
+ * @fmt: The printf-style format for the warning message.
+ *
+ * Used for generating warnings.
+ **/
+void
+art_warn (const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+}
+
+/**
+ * art_dprint: Print the debug message to stderr.
+ * @fmt: The printf-style format for the debug message.
+ *
+ * Used for generating debug output.
+ **/
+void
+art_dprint (const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+}
+
+void *art_alloc(size_t size)
+{
+  return malloc(size);
+}
+
+void art_free(void *ptr)
+{
+  free(ptr);
+}
+
+void *art_realloc(void *ptr, size_t size)
+{
+  return realloc(ptr, size);
+}
diff --git a/src/libart_lgpl/art_misc.h b/src/libart_lgpl/art_misc.h
new file mode 100644
index 0000000..31fa10f
--- /dev/null
+++ b/src/libart_lgpl/art_misc.h
@@ -0,0 +1,101 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Simple macros to set up storage allocation and basic types for libart
+   functions. */
+
+#ifndef __ART_MISC_H__
+#define __ART_MISC_H__
+
+#include <stdlib.h> /* for malloc, etc. */
+
+/* The art_config.h file is automatically generated by
+   gen_art_config.c and contains definitions of
+   ART_SIZEOF_{CHAR,SHORT,INT,LONG} and art_u{8,16,32}. */
+#ifdef LIBART_COMPILATION
+#include "art_config.h"
+#else
+#include <libart_lgpl/art_config.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *art_alloc(size_t size);
+void art_free(void *ptr);
+void *art_realloc(void *ptr, size_t size);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/* These aren't, strictly speaking, configuration macros, but they're
+   damn handy to have around, and may be worth playing with for
+   debugging. */
+#define art_new(type, n) ((type *)art_alloc ((n) * sizeof(type)))
+
+#define art_renew(p, type, n) ((type *)art_realloc (p, (n) * sizeof(type)))
+
+/* This one must be used carefully - in particular, p and max should
+   be variables. They can also be pstruct->el lvalues. */
+#define art_expand(p, type, max) do { if(max) { p = art_renew (p, type, max <<= 1); } else { max = 1; p = art_new(type, 1); } } while (0)
+
+typedef int art_boolean;
+#define ART_FALSE 0
+#define ART_TRUE 1
+
+/* define pi */
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif  /*  M_PI  */
+
+#ifndef M_SQRT2
+#define M_SQRT2         1.41421356237309504880  /* sqrt(2) */
+#endif  /* M_SQRT2 */
+
+/* Provide macros to feature the GCC function attribute.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4))
+#define ART_GNUC_PRINTF( format_idx, arg_idx )    \
+  __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#define ART_GNUC_NORETURN                         \
+  __attribute__((__noreturn__))
+#else   /* !__GNUC__ */
+#define ART_GNUC_PRINTF( format_idx, arg_idx )
+#define ART_GNUC_NORETURN
+#endif  /* !__GNUC__ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ART_GNUC_NORETURN
+art_die (const char *fmt, ...) ART_GNUC_PRINTF (1, 2);
+
+void
+art_warn (const char *fmt, ...) ART_GNUC_PRINTF (1, 2);
+
+void
+art_dprint (const char *fmt, ...) ART_GNUC_PRINTF (1, 2);
+
+#ifdef __cplusplus
+}
+#endif
+
+#define ART_USE_NEW_INTERSECTOR
+
+#endif /* __ART_MISC_H__ */
diff --git a/src/libart_lgpl/art_pathcode.h b/src/libart_lgpl/art_pathcode.h
new file mode 100644
index 0000000..2dfba24
--- /dev/null
+++ b/src/libart_lgpl/art_pathcode.h
@@ -0,0 +1,38 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_PATHCODE_H__
+#define __ART_PATHCODE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+  ART_MOVETO,
+  ART_MOVETO_OPEN,
+  ART_CURVETO,
+  ART_LINETO,
+  ART_END
+} ArtPathcode;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_PATHCODE_H__ */
diff --git a/src/libart_lgpl/art_pixbuf.c b/src/libart_lgpl/art_pixbuf.c
new file mode 100644
index 0000000..4fa641b
--- /dev/null
+++ b/src/libart_lgpl/art_pixbuf.c
@@ -0,0 +1,284 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_pixbuf.h"
+
+#include "art_misc.h"
+#include <string.h>
+
+/**
+ * art_pixbuf_new_rgb_dnotify: Create a new RGB #ArtPixBuf with explicit destroy notification.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ * @dfunc_data: The private data passed to @dfunc.
+ * @dfunc: The destroy notification function.
+ *
+ * Creates a generic data structure for holding a buffer of RGB
+ * pixels.  It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * @dfunc is called with @dfunc_data and @pixels as arguments when the
+ * #ArtPixBuf is destroyed. Using a destroy notification function
+ * allows a wide range of memory management disciplines for the pixel
+ * memory. A NULL value for @dfunc is also allowed and means that no
+ * special action will be taken on destruction.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgb_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+			    void *dfunc_data, ArtDestroyNotify dfunc)
+{
+  ArtPixBuf *pixbuf;
+
+  pixbuf = art_new (ArtPixBuf, 1);
+
+  pixbuf->format = ART_PIX_RGB;
+  pixbuf->n_channels = 3;
+  pixbuf->has_alpha = 0;
+  pixbuf->bits_per_sample = 8;
+
+  pixbuf->pixels = (art_u8 *) pixels;
+  pixbuf->width = width;
+  pixbuf->height = height;
+  pixbuf->rowstride = rowstride;
+  pixbuf->destroy_data = dfunc_data;
+  pixbuf->destroy = dfunc;
+
+  return pixbuf;
+}
+
+/**
+ * art_pixbuf_new_rgba_dnotify: Create a new RGBA #ArtPixBuf with explicit destroy notification.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ * @dfunc_data: The private data passed to @dfunc.
+ * @dfunc: The destroy notification function.
+ *
+ * Creates a generic data structure for holding a buffer of RGBA
+ * pixels.  It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * @dfunc is called with @dfunc_data and @pixels as arguments when the
+ * #ArtPixBuf is destroyed. Using a destroy notification function
+ * allows a wide range of memory management disciplines for the pixel
+ * memory. A NULL value for @dfunc is also allowed and means that no
+ * special action will be taken on destruction.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgba_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+			     void *dfunc_data, ArtDestroyNotify dfunc)
+{
+  ArtPixBuf *pixbuf;
+
+  pixbuf = art_new (ArtPixBuf, 1);
+
+  pixbuf->format = ART_PIX_RGB;
+  pixbuf->n_channels = 4;
+  pixbuf->has_alpha = 1;
+  pixbuf->bits_per_sample = 8;
+
+  pixbuf->pixels = (art_u8 *) pixels;
+  pixbuf->width = width;
+  pixbuf->height = height;
+  pixbuf->rowstride = rowstride;
+  pixbuf->destroy_data = dfunc_data;
+  pixbuf->destroy = dfunc;
+
+  return pixbuf;
+}
+
+/**
+ * art_pixbuf_new_const_rgb: Create a new RGB #ArtPixBuf with constant pixel data.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGB
+ * pixels.  It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * No action is taken when the #ArtPixBuf is destroyed. Thus, this
+ * function is useful when the pixel data is constant or statically
+ * allocated.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_const_rgb (const art_u8 *pixels, int width, int height, int rowstride)
+{
+  return art_pixbuf_new_rgb_dnotify ((art_u8 *) pixels, width, height, rowstride, NULL, NULL);
+}
+
+/**
+ * art_pixbuf_new_const_rgba: Create a new RGBA #ArtPixBuf with constant pixel data.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGBA
+ * pixels.  It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * No action is taken when the #ArtPixBuf is destroyed. Thus, this
+ * function is suitable when the pixel data is constant or statically
+ * allocated.
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_const_rgba (const art_u8 *pixels, int width, int height, int rowstride)
+{
+  return art_pixbuf_new_rgba_dnotify ((art_u8 *) pixels, width, height, rowstride, NULL, NULL);
+}
+
+static void
+art_pixel_destroy (void *func_data, void *data)
+{
+  art_free (data);
+}
+
+/**
+ * art_pixbuf_new_rgb: Create a new RGB #ArtPixBuf.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGB
+ * pixels.  It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * The @pixels buffer is freed with art_free() when the #ArtPixBuf is
+ * destroyed. Thus, this function is suitable when the pixel data is
+ * allocated with art_alloc().
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgb (art_u8 *pixels, int width, int height, int rowstride)
+{
+  return art_pixbuf_new_rgb_dnotify (pixels, width, height, rowstride, NULL, art_pixel_destroy);
+}
+
+/**
+ * art_pixbuf_new_rgba: Create a new RGBA #ArtPixBuf.
+ * @pixels: A buffer containing the actual pixel data.
+ * @width: The width of the pixbuf.
+ * @height: The height of the pixbuf.
+ * @rowstride: The rowstride of the pixbuf.
+ *
+ * Creates a generic data structure for holding a buffer of RGBA
+ * pixels.  It is possible to think of an #ArtPixBuf as a
+ * virtualization over specific pixel buffer formats.
+ *
+ * The @pixels buffer is freed with art_free() when the #ArtPixBuf is
+ * destroyed. Thus, this function is suitable when the pixel data is
+ * allocated with art_alloc().
+ *
+ * Return value: The newly created #ArtPixBuf.
+ **/
+ArtPixBuf *
+art_pixbuf_new_rgba (art_u8 *pixels, int width, int height, int rowstride)
+{
+  return art_pixbuf_new_rgba_dnotify (pixels, width, height, rowstride, NULL, art_pixel_destroy);
+}
+
+/**
+ * art_pixbuf_free: Destroy an #ArtPixBuf.
+ * @pixbuf: The #ArtPixBuf to be destroyed.
+ *
+ * Destroys the #ArtPixBuf, calling the destroy notification function
+ * (if non-NULL) so that the memory for the pixel buffer can be
+ * properly reclaimed.
+ **/
+void
+art_pixbuf_free (ArtPixBuf *pixbuf)
+{
+  ArtDestroyNotify destroy = pixbuf->destroy;
+  void *destroy_data = pixbuf->destroy_data;
+  art_u8 *pixels = pixbuf->pixels;
+
+  pixbuf->pixels = NULL;
+  pixbuf->destroy = NULL;
+  pixbuf->destroy_data = NULL;
+
+  if (destroy)
+    destroy (destroy_data, pixels);
+
+  art_free (pixbuf);
+}
+
+/**
+ * art_pixbuf_free_shallow:
+ * @pixbuf: The #ArtPixBuf to be destroyed.
+ *
+ * Destroys the #ArtPixBuf without calling the destroy notification function.
+ *
+ * This function is deprecated. Use the _dnotify variants for
+ * allocation instead.
+ **/
+void
+art_pixbuf_free_shallow (ArtPixBuf *pixbuf)
+{
+  art_free (pixbuf);
+}
+
+/**
+ * art_pixbuf_duplicate: Duplicate a pixbuf.
+ * @pixbuf: The #ArtPixBuf to duplicate.
+ *
+ * Duplicates a pixbuf, including duplicating the buffer.
+ *
+ * Return value: The duplicated pixbuf.
+ **/
+ArtPixBuf *
+art_pixbuf_duplicate (const ArtPixBuf *pixbuf)
+{
+  ArtPixBuf *result;
+  int size;
+
+  result = art_new (ArtPixBuf, 1);
+
+  result->format = pixbuf->format;
+  result->n_channels = pixbuf->n_channels;
+  result->has_alpha = pixbuf->has_alpha;
+  result->bits_per_sample = pixbuf->bits_per_sample;
+
+  size = (pixbuf->height - 1) * pixbuf->rowstride +
+    pixbuf->width * ((pixbuf->n_channels * pixbuf->bits_per_sample + 7) >> 3);
+  result->pixels = art_alloc (size);
+  memcpy (result->pixels, pixbuf->pixels, size);
+
+  result->width = pixbuf->width;
+  result->height = pixbuf->height;
+  result->rowstride = pixbuf->rowstride;
+  result->destroy_data = NULL;
+  result->destroy = art_pixel_destroy;
+
+  return result;
+}
diff --git a/src/libart_lgpl/art_pixbuf.h b/src/libart_lgpl/art_pixbuf.h
new file mode 100644
index 0000000..28c4a07
--- /dev/null
+++ b/src/libart_lgpl/art_pixbuf.h
@@ -0,0 +1,103 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_PIXBUF_H__
+#define __ART_PIXBUF_H__
+
+/* A generic data structure for holding a buffer of pixels. One way
+   to think about this module is as a virtualization over specific
+   pixel buffer formats. */
+
+#ifdef LIBART_COMPILATION
+#include "art_misc.h"
+#else
+#include <libart_lgpl/art_misc.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef void (*ArtDestroyNotify) (void *func_data, void *data);
+
+typedef struct _ArtPixBuf ArtPixBuf;
+
+typedef enum {
+  ART_PIX_RGB
+  /* gray, cmyk, lab, ... ? */
+} ArtPixFormat;
+
+
+/* The pixel buffer consists of width * height pixels, each of which
+   has n_channels samples. It is stored in simple packed format. */
+
+struct _ArtPixBuf {
+  /*< public >*/
+  ArtPixFormat format;
+  int n_channels;
+  int has_alpha;
+  int bits_per_sample;
+
+  art_u8 *pixels;
+  int width;
+  int height;
+  int rowstride;
+  void *destroy_data;
+  ArtDestroyNotify destroy;
+};
+
+/* allocate an ArtPixBuf from art_alloc()ed pixels (automated destruction) */
+ArtPixBuf *
+art_pixbuf_new_rgb (art_u8 *pixels, int width, int height, int rowstride);
+
+ArtPixBuf *
+art_pixbuf_new_rgba (art_u8 *pixels, int width, int height, int rowstride);
+
+/* allocate an ArtPixBuf from constant pixels (no destruction) */
+ArtPixBuf *
+art_pixbuf_new_const_rgb (const art_u8 *pixels, int width, int height, int rowstride);
+
+ArtPixBuf *
+art_pixbuf_new_const_rgba (const art_u8 *pixels, int width, int height, int rowstride);
+
+/* allocate an ArtPixBuf and notify creator upon destruction */
+ArtPixBuf *
+art_pixbuf_new_rgb_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+			    void *dfunc_data, ArtDestroyNotify dfunc);
+
+ArtPixBuf *
+art_pixbuf_new_rgba_dnotify (art_u8 *pixels, int width, int height, int rowstride,
+			     void *dfunc_data, ArtDestroyNotify dfunc);
+
+/* free an ArtPixBuf with destroy notification */
+void
+art_pixbuf_free (ArtPixBuf *pixbuf);
+
+/* deprecated function, use the _dnotify variants for allocation instead */
+void
+art_pixbuf_free_shallow (ArtPixBuf *pixbuf);
+
+ArtPixBuf *
+art_pixbuf_duplicate (const ArtPixBuf *pixbuf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_point.h b/src/libart_lgpl/art_point.h
new file mode 100644
index 0000000..910270c
--- /dev/null
+++ b/src/libart_lgpl/art_point.h
@@ -0,0 +1,37 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_POINT_H__
+#define __ART_POINT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtPoint ArtPoint;
+
+struct _ArtPoint {
+  /*< public >*/
+  double x, y;
+};
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_POINT_H__ */
diff --git a/src/libart_lgpl/art_rect.c b/src/libart_lgpl/art_rect.c
new file mode 100644
index 0000000..30bf0a7
--- /dev/null
+++ b/src/libart_lgpl/art_rect.c
@@ -0,0 +1,214 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rect.h"
+
+#include <math.h>
+
+#ifndef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+/* rectangle primitives stolen from gzilla */
+
+/**
+ * art_irect_copy: Make a copy of an integer rectangle.
+ * @dest: Where the copy is stored.
+ * @src: The source rectangle.
+ *
+ * Copies the rectangle.
+ **/
+void
+art_irect_copy (ArtIRect *dest, const ArtIRect *src) {
+  dest->x0 = src->x0;
+  dest->y0 = src->y0;
+  dest->x1 = src->x1;
+  dest->y1 = src->y1;
+}
+
+/**
+ * art_irect_union: Find union of two integer rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the smallest rectangle that includes @src1 and @src2.
+ **/
+void
+art_irect_union (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2) {
+  if (art_irect_empty (src1)) {
+    art_irect_copy (dest, src2);
+  } else if (art_irect_empty (src2)) {
+    art_irect_copy (dest, src1);
+  } else {
+    dest->x0 = MIN (src1->x0, src2->x0);
+    dest->y0 = MIN (src1->y0, src2->y0);
+    dest->x1 = MAX (src1->x1, src2->x1);
+    dest->y1 = MAX (src1->y1, src2->y1);
+  }
+}
+
+/**
+ * art_irect_intersection: Find intersection of two integer rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the intersection of @src1 and @src2.
+ **/
+void
+art_irect_intersect (ArtIRect *dest, const ArtIRect *src1, const ArtIRect *src2) {
+  dest->x0 = MAX (src1->x0, src2->x0);
+  dest->y0 = MAX (src1->y0, src2->y0);
+  dest->x1 = MIN (src1->x1, src2->x1);
+  dest->y1 = MIN (src1->y1, src2->y1);
+}
+
+/**
+ * art_irect_empty: Determine whether integer rectangle is empty.
+ * @src: The source rectangle.
+ *
+ * Return value: TRUE if @src is an empty rectangle, FALSE otherwise.
+ **/
+int
+art_irect_empty (const ArtIRect *src) {
+  return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+#if 0
+gboolean irect_point_inside (ArtIRect *rect, GzwPoint *point) {
+  return (point->x >= rect->x0 && point->y >= rect->y0 &&
+	  point->x < rect->x1 && point->y < rect->y1);
+}
+#endif
+
+/**
+ * art_drect_copy: Make a copy of a rectangle.
+ * @dest: Where the copy is stored.
+ * @src: The source rectangle.
+ *
+ * Copies the rectangle.
+ **/
+void
+art_drect_copy (ArtDRect *dest, const ArtDRect *src) {
+  dest->x0 = src->x0;
+  dest->y0 = src->y0;
+  dest->x1 = src->x1;
+  dest->y1 = src->y1;
+}
+
+/**
+ * art_drect_union: Find union of two rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the smallest rectangle that includes @src1 and @src2.
+ **/
+void
+art_drect_union (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2) {
+  if (art_drect_empty (src1)) {
+    art_drect_copy (dest, src2);
+  } else if (art_drect_empty (src2)) {
+    art_drect_copy (dest, src1);
+  } else {
+    dest->x0 = MIN (src1->x0, src2->x0);
+    dest->y0 = MIN (src1->y0, src2->y0);
+    dest->x1 = MAX (src1->x1, src2->x1);
+    dest->y1 = MAX (src1->y1, src2->y1);
+  }
+}
+
+/**
+ * art_drect_intersection: Find intersection of two rectangles.
+ * @dest: Where the result is stored.
+ * @src1: A source rectangle.
+ * @src2: Another source rectangle.
+ *
+ * Finds the intersection of @src1 and @src2.
+ **/
+void
+art_drect_intersect (ArtDRect *dest, const ArtDRect *src1, const ArtDRect *src2) {
+  dest->x0 = MAX (src1->x0, src2->x0);
+  dest->y0 = MAX (src1->y0, src2->y0);
+  dest->x1 = MIN (src1->x1, src2->x1);
+  dest->y1 = MIN (src1->y1, src2->y1);
+}
+
+/**
+ * art_irect_empty: Determine whether rectangle is empty.
+ * @src: The source rectangle.
+ *
+ * Return value: TRUE if @src is an empty rectangle, FALSE otherwise.
+ **/
+int
+art_drect_empty (const ArtDRect *src) {
+  return (src->x1 <= src->x0 || src->y1 <= src->y0);
+}
+
+/**
+ * art_drect_affine_transform: Affine transform rectangle.
+ * @dst: Where to store the result.
+ * @src: The source rectangle.
+ * @matrix: The affine transformation.
+ *
+ * Find the smallest rectangle enclosing the affine transformed @src.
+ * The result is exactly the affine transformation of @src when
+ * @matrix specifies a rectilinear affine transformation, otherwise it
+ * is a conservative approximation.
+ **/
+void
+art_drect_affine_transform (ArtDRect *dst, const ArtDRect *src, const double matrix[6])
+{
+  double x00, y00, x10, y10;
+  double x01, y01, x11, y11;
+
+  x00 = src->x0 * matrix[0] + src->y0 * matrix[2] + matrix[4];
+  y00 = src->x0 * matrix[1] + src->y0 * matrix[3] + matrix[5];
+  x10 = src->x1 * matrix[0] + src->y0 * matrix[2] + matrix[4];
+  y10 = src->x1 * matrix[1] + src->y0 * matrix[3] + matrix[5];
+  x01 = src->x0 * matrix[0] + src->y1 * matrix[2] + matrix[4];
+  y01 = src->x0 * matrix[1] + src->y1 * matrix[3] + matrix[5];
+  x11 = src->x1 * matrix[0] + src->y1 * matrix[2] + matrix[4];
+  y11 = src->x1 * matrix[1] + src->y1 * matrix[3] + matrix[5];
+  dst->x0 = MIN (MIN (x00, x10), MIN (x01, x11));
+  dst->y0 = MIN (MIN (y00, y10), MIN (y01, y11));
+  dst->x1 = MAX (MAX (x00, x10), MAX (x01, x11));
+  dst->y1 = MAX (MAX (y00, y10), MAX (y01, y11));
+}
+
+/**
+ * art_drect_to_irect: Convert rectangle to integer rectangle.
+ * @dst: Where to store resulting integer rectangle.
+ * @src: The source rectangle.
+ *
+ * Find the smallest integer rectangle that encloses @src.
+ **/
+void
+art_drect_to_irect (ArtIRect *dst, ArtDRect *src)
+{
+  dst->x0 = floor (src->x0);
+  dst->y0 = floor (src->y0);
+  dst->x1 = ceil (src->x1);
+  dst->y1 = ceil (src->y1);
+}
diff --git a/src/libart_lgpl/art_rect.h b/src/libart_lgpl/art_rect.h
new file mode 100644
index 0000000..f4642f6
--- /dev/null
+++ b/src/libart_lgpl/art_rect.h
@@ -0,0 +1,77 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RECT_H__
+#define __ART_RECT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _ArtDRect ArtDRect;
+typedef struct _ArtIRect ArtIRect;
+
+struct _ArtDRect {
+  /*< public >*/
+  double x0, y0, x1, y1;
+};
+
+struct _ArtIRect {
+  /*< public >*/
+  int x0, y0, x1, y1;
+};
+
+/* Make a copy of the rectangle. */
+void art_irect_copy (ArtIRect *dest, const ArtIRect *src);
+
+/* Find the smallest rectangle that includes both source rectangles. */
+void art_irect_union (ArtIRect *dest,
+		      const ArtIRect *src1, const ArtIRect *src2);
+
+/* Return the intersection of the two rectangles */
+void art_irect_intersect (ArtIRect *dest,
+			  const ArtIRect *src1, const ArtIRect *src2);
+
+/* Return true if the rectangle is empty. */
+int art_irect_empty (const ArtIRect *src);
+
+/* Make a copy of the rectangle. */
+void art_drect_copy (ArtDRect *dest, const ArtDRect *src);
+
+/* Find the smallest rectangle that includes both source rectangles. */
+void art_drect_union (ArtDRect *dest,
+		      const ArtDRect *src1, const ArtDRect *src2);
+
+/* Return the intersection of the two rectangles */
+void art_drect_intersect (ArtDRect *dest,
+			  const ArtDRect *src1, const ArtDRect *src2);
+
+/* Return true if the rectangle is empty. */
+int art_drect_empty (const ArtDRect *src);
+
+void
+art_drect_affine_transform (ArtDRect *dst, const ArtDRect *src,
+			   const double matrix[6]);
+
+void art_drect_to_irect (ArtIRect *dst, ArtDRect *src);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rect_svp.c b/src/libart_lgpl/art_rect_svp.c
new file mode 100644
index 0000000..67bc606
--- /dev/null
+++ b/src/libart_lgpl/art_rect_svp.c
@@ -0,0 +1,81 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rect_svp.h"
+
+#include "art_misc.h"
+#include "art_svp.h"
+#include "art_rect.h"
+
+#ifndef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+/**
+ * art_drect_svp: Find the bounding box of a sorted vector path.
+ * @bbox: Where to store the bounding box.
+ * @svp: The SVP.
+ *
+ * Finds the bounding box of the SVP.
+ **/
+void
+art_drect_svp (ArtDRect *bbox, const ArtSVP *svp)
+{
+  int i;
+
+  if (svp->n_segs == 0)
+    {
+      bbox->x0 = 0;
+      bbox->y0 = 0;
+      bbox->x1 = 0;
+      bbox->y1 = 0;
+      return;
+    }
+
+  art_drect_copy (bbox, &svp->segs[0].bbox);
+
+  for (i = 1; i < svp->n_segs; i++)
+    {
+      bbox->x0 = MIN (bbox->x0, svp->segs[i].bbox.x0);
+      bbox->y0 = MIN (bbox->y0, svp->segs[i].bbox.y0);
+      bbox->x1 = MAX (bbox->x1, svp->segs[i].bbox.x1);
+      bbox->y1 = MAX (bbox->y1, svp->segs[i].bbox.y1);
+    }
+}
+
+/**
+ * art_drect_svp_union: Compute the bounding box of the svp and union it in to the existing bounding box.
+ * @bbox: Initial boundin box and where to store the bounding box.
+ * @svp: The SVP.
+ *
+ * Finds the bounding box of the SVP, computing its union with an
+ * existing bbox.
+ **/
+void
+art_drect_svp_union (ArtDRect *bbox, const ArtSVP *svp)
+{
+  ArtDRect svp_bbox;
+
+  art_drect_svp (&svp_bbox, svp);
+  art_drect_union (bbox, bbox, &svp_bbox);
+}
diff --git a/src/libart_lgpl/art_rect_svp.h b/src/libart_lgpl/art_rect_svp.h
new file mode 100644
index 0000000..e514180
--- /dev/null
+++ b/src/libart_lgpl/art_rect_svp.h
@@ -0,0 +1,46 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RECT_SVP_H__
+#define __ART_RECT_SVP_H__
+
+/* Find the bounding box of a sorted vector path. */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_drect_svp (ArtDRect *bbox, const ArtSVP *svp);
+
+/* Compute the bounding box of the svp and union it in to the
+   existing bounding box. */
+void
+art_drect_svp_union (ArtDRect *bbox, const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RECT_SVP_H__ */
diff --git a/src/libart_lgpl/art_rect_uta.c b/src/libart_lgpl/art_rect_uta.c
new file mode 100644
index 0000000..beea6de
--- /dev/null
+++ b/src/libart_lgpl/art_rect_uta.c
@@ -0,0 +1,133 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rect_uta.h"
+
+/* Functions to decompose a microtile array into a list of rectangles. */
+
+/**
+ * art_rect_list_from_uta: Decompose uta into list of rectangles.
+ * @uta: The source uta.
+ * @max_width: The maximum width of the resulting rectangles.
+ * @max_height: The maximum height of the resulting rectangles.
+ * @p_nrects: Where to store the number of returned rectangles.
+ *
+ * Allocates a new list of rectangles, sets * p_nrects to the number
+ * in the list. This list should be freed with art_free().
+ *
+ * Each rectangle bounded in size by (@max_width, @max_height).
+ * However, these bounds must be at least the size of one tile.
+ *
+ * This routine provides a precise implementation, i.e. the rectangles
+ * cover exactly the same area as the uta. It is thus appropriate in
+ * cases where the overhead per rectangle is small compared with the
+ * cost of filling in extra pixels.
+ *
+ * Return value: An array containing the resulting rectangles.
+ **/
+ArtIRect *
+art_rect_list_from_uta (ArtUta *uta, int max_width, int max_height,
+			int *p_nrects)
+{
+  ArtIRect *rects;
+  int n_rects, n_rects_max;
+  int x, y;
+  int width, height;
+  int ix;
+  int left_ix;
+  ArtUtaBbox *utiles;
+  ArtUtaBbox bb;
+  int x0, y0, x1, y1;
+  int *glom;
+  int glom_rect;
+
+  n_rects = 0;
+  n_rects_max = 1;
+  rects = art_new (ArtIRect, n_rects_max);
+
+  width = uta->width;
+  height = uta->height;
+  utiles = uta->utiles;
+
+  glom = art_new (int, width * height);
+  for (ix = 0; ix < width * height; ix++)
+    glom[ix] = -1;
+
+  ix = 0;
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      {
+	bb = utiles[ix];
+	if (bb)
+	  {
+	    x0 = ((uta->x0 + x) << ART_UTILE_SHIFT) + ART_UTA_BBOX_X0(bb);
+	    y0 = ((uta->y0 + y) << ART_UTILE_SHIFT) + ART_UTA_BBOX_Y0(bb);
+	    y1 = ((uta->y0 + y) << ART_UTILE_SHIFT) + ART_UTA_BBOX_Y1(bb);
+
+	    left_ix = ix;
+	    /* now try to extend to the right */
+	    while (x != width - 1 &&
+		   ART_UTA_BBOX_X1(bb) == ART_UTILE_SIZE &&
+		   (((bb & 0xffffff) ^ utiles[ix + 1]) & 0xffff00ff) == 0 &&
+		   (((uta->x0 + x + 1) << ART_UTILE_SHIFT) +
+		    ART_UTA_BBOX_X1(utiles[ix + 1]) -
+		    x0) <= max_width)
+	      {
+		bb = utiles[ix + 1];
+		ix++;
+		x++;
+	      }
+	    x1 = ((uta->x0 + x) << ART_UTILE_SHIFT) + ART_UTA_BBOX_X1(bb);
+
+
+	    /* if rectangle nonempty */
+	    if ((x1 ^ x0) | (y1 ^ y0))
+	      {
+		/* try to glom onto an existing rectangle */
+		glom_rect = glom[left_ix];
+		if (glom_rect != -1 &&
+		    x0 == rects[glom_rect].x0 &&
+		    x1 == rects[glom_rect].x1 &&
+		    y0 == rects[glom_rect].y1 &&
+		    y1 - rects[glom_rect].y0 <= max_height)
+		  {
+		    rects[glom_rect].y1 = y1;
+		  }
+		else
+		  {
+		    if (n_rects == n_rects_max)
+		      art_expand (rects, ArtIRect, n_rects_max);
+		    rects[n_rects].x0 = x0;
+		    rects[n_rects].y0 = y0;
+		    rects[n_rects].x1 = x1;
+		    rects[n_rects].y1 = y1;
+		    glom_rect = n_rects;
+		    n_rects++;
+		  }
+		if (y != height - 1)
+		  glom[left_ix + width] = glom_rect;
+	      }
+	  }
+	ix++;
+      }
+
+  art_free (glom);
+  *p_nrects = n_rects;
+  return rects;
+}
diff --git a/src/libart_lgpl/art_rect_uta.h b/src/libart_lgpl/art_rect_uta.h
new file mode 100644
index 0000000..e9a1585
--- /dev/null
+++ b/src/libart_lgpl/art_rect_uta.h
@@ -0,0 +1,42 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RECT_UTA_H__
+#define __ART_RECT_UTA_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_rect.h"
+#include "art_uta.h"
+#else
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_uta.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtIRect *
+art_rect_list_from_uta (ArtUta *uta, int max_width, int max_height,
+			int *p_nrects);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RECT_UTA_H__ */
diff --git a/src/libart_lgpl/art_render.c b/src/libart_lgpl/art_render.c
new file mode 100644
index 0000000..83068cf
--- /dev/null
+++ b/src/libart_lgpl/art_render.c
@@ -0,0 +1,1382 @@
+/*
+ * art_render.c: Modular rendering architecture.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_render.h"
+
+#include "art_rgb.h"
+
+typedef struct _ArtRenderPriv ArtRenderPriv;
+
+struct _ArtRenderPriv {
+  ArtRender super;
+
+  ArtImageSource *image_source;
+
+  int n_mask_source;
+  ArtMaskSource **mask_source;
+
+  int n_callbacks;
+  ArtRenderCallback **callbacks;
+};
+
+ArtRender *
+art_render_new (int x0, int y0, int x1, int y1,
+		art_u8 *pixels, int rowstride,
+		int n_chan, int depth, ArtAlphaType alpha_type,
+		ArtAlphaGamma *alphagamma)
+{
+  ArtRenderPriv *priv;
+  ArtRender *result;
+
+  priv = art_new (ArtRenderPriv, 1);
+  result = &priv->super;
+
+  if (n_chan > ART_MAX_CHAN)
+    {
+      art_warn ("art_render_new: n_chan = %d, exceeds %d max\n",
+		n_chan, ART_MAX_CHAN);
+      return NULL;
+    }
+  if (depth > ART_MAX_DEPTH)
+    {
+      art_warn ("art_render_new: depth = %d, exceeds %d max\n",
+		depth, ART_MAX_DEPTH);
+      return NULL;
+    }
+  if (x0 >= x1)
+    {
+      art_warn ("art_render_new: x0 >= x1 (x0 = %d, x1 = %d)\n", x0, x1);
+      return NULL;
+    }
+  result->x0 = x0;
+  result->y0 = y0;
+  result->x1 = x1;
+  result->y1 = y1;
+  result->pixels = pixels;
+  result->rowstride = rowstride;
+  result->n_chan = n_chan;
+  result->depth = depth;
+  result->alpha_type = alpha_type;
+
+  result->clear = ART_FALSE;
+  result->opacity = 0x10000;
+  result->compositing_mode = ART_COMPOSITE_NORMAL;
+  result->alphagamma = alphagamma;
+
+  result->alpha_buf = NULL;
+  result->image_buf = NULL;
+
+  result->run = NULL;
+  result->span_x = NULL;
+
+  result->need_span = ART_FALSE;
+
+  priv->image_source = NULL;
+
+  priv->n_mask_source = 0;
+  priv->mask_source = NULL;
+
+  return result;
+}
+
+/* todo on clear routines: I haven't really figured out what to do
+   with clearing the alpha channel. It _should_ be possible to clear
+   to an arbitrary RGBA color. */
+
+/**
+ * art_render_clear: Set clear color.
+ * @clear_color: Color with which to clear dest.
+ *
+ * Sets clear color, equivalent to actually clearing the destination
+ * buffer before rendering. This is the most general form.
+ **/
+void
+art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color)
+{
+  int i;
+  int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+
+  render->clear = ART_TRUE;
+  for (i = 0; i < n_ch; i++)
+    render->clear_color[i] = clear_color[i];
+}
+
+/**
+ * art_render_clear_rgb: Set clear color, given in RGB format.
+ * @clear_rgb: Clear color, in 0xRRGGBB format.
+ *
+ * Sets clear color, equivalent to actually clearing the destination
+ * buffer before rendering.
+ **/
+void
+art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb)
+{
+  if (render->n_chan != 3)
+    art_warn ("art_render_clear_rgb: called on render with %d channels, only works with 3\n",
+	      render->n_chan);
+  else
+    {
+      int r, g, b;
+
+      render->clear = ART_TRUE;
+      r = clear_rgb >> 16;
+      g = (clear_rgb >> 8) & 0xff;
+      b = clear_rgb & 0xff;
+      render->clear_color[0] = ART_PIX_MAX_FROM_8(r);
+      render->clear_color[1] = ART_PIX_MAX_FROM_8(g);
+      render->clear_color[2] = ART_PIX_MAX_FROM_8(b);
+    }
+}
+
+static void
+art_render_nop_done (ArtRenderCallback *self, ArtRender *render)
+{
+}
+
+static void
+art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render,
+			      art_u8 *dest, int y)
+{
+  int width = render->x1 - render->x0;
+  art_u8 r, g, b;
+  ArtPixMaxDepth color_max;
+
+  color_max = render->clear_color[0];
+  r = ART_PIX_8_FROM_MAX (color_max);
+  color_max = render->clear_color[1];
+  g = ART_PIX_8_FROM_MAX (color_max);
+  color_max = render->clear_color[2];
+  b = ART_PIX_8_FROM_MAX (color_max);
+
+  art_rgb_fill_run (dest, r, g, b, width);
+}
+
+static void
+art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render,
+			   art_u8 *dest, int y)
+{
+  int width = render->x1 - render->x0;
+  int i, j;
+  int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+  int ix;
+  art_u8 color[ART_MAX_CHAN + 1];
+
+  for (j = 0; j < n_ch; j++)
+    {
+      ArtPixMaxDepth color_max = render->clear_color[j];
+      color[j] = ART_PIX_8_FROM_MAX (color_max);
+    }
+
+  ix = 0;
+  for (i = 0; i < width; i++)
+    for (j = 0; j < n_ch; j++)
+      dest[ix++] = color[j];
+}
+
+const ArtRenderCallback art_render_clear_rgb8_obj =
+{
+  art_render_clear_render_rgb8,
+  art_render_nop_done
+};
+
+const ArtRenderCallback art_render_clear_8_obj =
+{
+  art_render_clear_render_8,
+  art_render_nop_done
+};
+
+#if ART_MAX_DEPTH >= 16
+
+static void
+art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render,
+			    art_u8 *dest, int y)
+{
+  int width = render->x1 - render->x0;
+  int i, j;
+  int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+  int ix;
+  art_u16 *dest_16 = (art_u16 *)dest;
+  art_u8 color[ART_MAX_CHAN + 1];
+
+  for (j = 0; j < n_ch; j++)
+    {
+      int color_16 = render->clear_color[j];
+      color[j] = color_16;
+    }
+
+  ix = 0;
+  for (i = 0; i < width; i++)
+    for (j = 0; j < n_ch; j++)
+      dest_16[ix++] = color[j];
+}
+
+const ArtRenderCallback art_render_clear_16_obj =
+{
+  art_render_clear_render_16,
+  art_render_nop_done
+};
+
+#endif /* ART_MAX_DEPTH >= 16 */
+
+/* todo: inline */
+static ArtRenderCallback *
+art_render_choose_clear_callback (ArtRender *render)
+{
+  ArtRenderCallback *clear_callback;
+
+  if (render->depth == 8)
+    {
+      if (render->n_chan == 3 &&
+	  render->alpha_type == ART_ALPHA_NONE)
+	clear_callback = (ArtRenderCallback *)&art_render_clear_rgb8_obj;
+      else
+	clear_callback = (ArtRenderCallback *)&art_render_clear_8_obj;
+    }
+#if ART_MAX_DEPTH >= 16
+  else if (render->depth == 16)
+    clear_callback = (ArtRenderCallback *)&art_render_clear_16_obj;
+#endif
+  else
+    {
+      art_die ("art_render_choose_clear_callback: inconsistent render->depth = %d\n",
+	       render->depth);
+    }
+  return clear_callback;
+}
+
+#if 0
+/* todo: get around to writing this */
+static void
+art_render_composite_render_noa_8_norm (ArtRenderCallback *self, ArtRender *render,
+					art_u8 *dest, int y)
+{
+  int width = render->x1 - render->x0;
+
+}
+#endif
+
+/* This is the most general form of the function. It is slow but
+   (hopefully) correct. Actually, I'm still worried about roundoff
+   errors in the premul case - it seems to me that an off-by-one could
+   lead to overflow. */
+static void
+art_render_composite (ArtRenderCallback *self, ArtRender *render,
+					art_u8 *dest, int y)
+{
+  ArtRenderMaskRun *run = render->run;
+  art_u32 depth = render->depth;
+  int n_run = render->n_run;
+  int x0 = render->x0;
+  int x;
+  int run_x0, run_x1;
+  art_u8 *alpha_buf = render->alpha_buf;
+  art_u8 *image_buf = render->image_buf;
+  int i, j;
+  art_u32 tmp;
+  art_u32 run_alpha;
+  art_u32 alpha;
+  int image_ix;
+  art_u16 src[ART_MAX_CHAN + 1];
+  art_u16 dst[ART_MAX_CHAN + 1];
+  int n_chan = render->n_chan;
+  ArtAlphaType alpha_type = render->alpha_type;
+  int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
+  int dst_pixstride = n_ch * (depth >> 3);
+  int buf_depth = render->buf_depth;
+  ArtAlphaType buf_alpha = render->buf_alpha;
+  int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
+  int buf_pixstride = buf_n_ch * (buf_depth >> 3);
+  art_u8 *bufptr;
+  art_u32 src_alpha;
+  art_u32 src_mul;
+  art_u8 *dstptr;
+  art_u32 dst_alpha;
+  art_u32 dst_mul;
+
+  image_ix = 0;
+  for (i = 0; i < n_run - 1; i++)
+    {
+      run_x0 = run[i].x;
+      run_x1 = run[i + 1].x;
+      tmp = run[i].alpha;
+      if (tmp < 0x8100)
+	continue;
+
+      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+      bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
+      dstptr = dest + (run_x0 - x0) * dst_pixstride;
+      for (x = run_x0; x < run_x1; x++)
+	{
+	  if (alpha_buf)
+	    {
+	      if (depth == 8)
+		{
+		  tmp = run_alpha * alpha_buf[x - x0] + 0x80;
+		  /* range 0x80 .. 0xff0080 */
+		  alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+		}
+	      else /* (depth == 16) */
+		{
+		  tmp = ((art_u16 *)alpha_buf)[x - x0];
+		  tmp = (run_alpha * tmp + 0x8000) >> 8;
+		  /* range 0x80 .. 0xffff80 */
+		  alpha = (tmp + (tmp >> 16)) >> 8;
+		}
+	    }
+	  else
+	    alpha = run_alpha;
+	  /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
+
+	  /* convert (src pixel * alpha) to premul alpha form,
+	     store in src as 0..0xffff range */
+	  if (buf_alpha == ART_ALPHA_NONE)
+	    {
+	      src_alpha = alpha;
+	      src_mul = src_alpha;
+	    }
+	  else
+	    {
+	      if (buf_depth == 8)
+		{
+		  tmp = alpha * bufptr[n_chan] + 0x80;
+		  /* range 0x80 .. 0xff0080 */
+		  src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+		}
+	      else /* (depth == 16) */
+		{
+		  tmp = ((art_u16 *)bufptr)[n_chan];
+		  tmp = (alpha * tmp + 0x8000) >> 8;
+		  /* range 0x80 .. 0xffff80 */
+		  src_alpha = (tmp + (tmp >> 16)) >> 8;
+		}
+	      if (buf_alpha == ART_ALPHA_SEPARATE)
+		src_mul = src_alpha;
+	      else /* buf_alpha == (ART_ALPHA_PREMUL) */
+		src_mul = alpha;
+	    }
+	  /* src_alpha is the (alpha of the source pixel * alpha),
+	     range 0..0x10000 */
+
+	  if (buf_depth == 8)
+	    {
+	      src_mul *= 0x101;
+	      for (j = 0; j < n_chan; j++)
+		src[j] = (bufptr[j] * src_mul + 0x8000) >> 16;
+	    }
+	  else if (buf_depth == 16)
+	    {
+	      for (j = 0; j < n_chan; j++)
+		src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16;
+	    }
+	  bufptr += buf_pixstride;
+
+	  /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range
+             0..0x10000) now contain the source pixel with
+             premultiplied alpha */
+
+	  /* convert dst pixel to premul alpha form,
+	     store in dst as 0..0xffff range */
+	  if (alpha_type == ART_ALPHA_NONE)
+	    {
+	      dst_alpha = 0x10000;
+	      dst_mul = dst_alpha;
+	    }
+	  else
+	    {
+	      if (depth == 8)
+		{
+		  tmp = dstptr[n_chan];
+		  /* range 0..0xff */
+		  dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+		}
+	      else /* (depth == 16) */
+		{
+		  tmp = ((art_u16 *)dstptr)[n_chan];
+		  dst_alpha = (tmp + (tmp >> 15));
+		}
+	      if (alpha_type == ART_ALPHA_SEPARATE)
+		dst_mul = dst_alpha;
+	      else /* (alpha_type == ART_ALPHA_PREMUL) */
+		dst_mul = 0x10000;
+	    }
+	  /* dst_alpha is the alpha of the dest pixel,
+	     range 0..0x10000 */
+
+	  if (depth == 8)
+	    {
+	      dst_mul *= 0x101;
+	      for (j = 0; j < n_chan; j++)
+		dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16;
+	    }
+	  else if (buf_depth == 16)
+	    {
+	      for (j = 0; j < n_chan; j++)
+		dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16;
+	    }
+
+	  /* do the compositing, dst = (src over dst) */
+	  for (j = 0; j < n_chan; j++)
+	    {
+	      art_u32 srcv, dstv;
+	      art_u32 tmp;
+
+	      srcv = src[j];
+	      dstv = dst[j];
+	      tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv;
+	      tmp -= tmp >> 16;
+	      dst[j] = tmp;
+	    }
+
+	  if (alpha_type == ART_ALPHA_NONE)
+	    {
+	      if (depth == 8)
+		dst_mul = 0xff;
+	      else /* (depth == 16) */
+		dst_mul = 0xffff;
+	    }
+	  else
+	    {
+	      if (src_alpha >= 0x10000)
+		dst_alpha = 0x10000;
+	      else
+		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+	      if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
+		{
+		  if (depth == 8)
+		    dst_mul = 0xff;
+		  else /* (depth == 16) */
+		    dst_mul = 0xffff;
+		}
+	      else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
+		{
+		  if (depth == 8)
+		    dst_mul = 0xff0000 / dst_alpha;
+		  else /* (depth == 16) */
+		    dst_mul = 0xffff0000 / dst_alpha;
+		}
+	    }
+	  if (depth == 8)
+	    {
+	      for (j = 0; j < n_chan; j++)
+		dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16;
+	      if (alpha_type != ART_ALPHA_NONE)
+		dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
+	    }
+	  else if (depth == 16)
+	    {
+	      for (j = 0; j < n_chan; j++)
+		((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16;
+	      if (alpha_type != ART_ALPHA_NONE)
+		((art_u16 *)dstptr)[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16;
+	    }
+	  dstptr += dst_pixstride;
+	}
+    }
+}
+
+const ArtRenderCallback art_render_composite_obj =
+{
+  art_render_composite,
+  art_render_nop_done
+};
+
+static void
+art_render_composite_8 (ArtRenderCallback *self, ArtRender *render,
+			art_u8 *dest, int y)
+{
+  ArtRenderMaskRun *run = render->run;
+  int n_run = render->n_run;
+  int x0 = render->x0;
+  int x;
+  int run_x0, run_x1;
+  art_u8 *alpha_buf = render->alpha_buf;
+  art_u8 *image_buf = render->image_buf;
+  int i, j;
+  art_u32 tmp;
+  art_u32 run_alpha;
+  art_u32 alpha;
+  int image_ix;
+  int n_chan = render->n_chan;
+  ArtAlphaType alpha_type = render->alpha_type;
+  int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
+  int dst_pixstride = n_ch;
+  ArtAlphaType buf_alpha = render->buf_alpha;
+  int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
+  int buf_pixstride = buf_n_ch;
+  art_u8 *bufptr;
+  art_u32 src_alpha;
+  art_u32 src_mul;
+  art_u8 *dstptr;
+  art_u32 dst_alpha;
+  art_u32 dst_mul, dst_save_mul;
+
+  image_ix = 0;
+  for (i = 0; i < n_run - 1; i++)
+    {
+      run_x0 = run[i].x;
+      run_x1 = run[i + 1].x;
+      tmp = run[i].alpha;
+      if (tmp < 0x10000)
+	continue;
+
+      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+      bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
+      dstptr = dest + (run_x0 - x0) * dst_pixstride;
+      for (x = run_x0; x < run_x1; x++)
+	{
+	  if (alpha_buf)
+	    {
+	      tmp = run_alpha * alpha_buf[x - x0] + 0x80;
+	      /* range 0x80 .. 0xff0080 */
+	      alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+	    }
+	  else
+	    alpha = run_alpha;
+	  /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
+
+	  /* convert (src pixel * alpha) to premul alpha form,
+	     store in src as 0..0xffff range */
+	  if (buf_alpha == ART_ALPHA_NONE)
+	    {
+	      src_alpha = alpha;
+	      src_mul = src_alpha;
+	    }
+	  else
+	    {
+	      tmp = alpha * bufptr[n_chan] + 0x80;
+	      /* range 0x80 .. 0xff0080 */
+	      src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+
+	      if (buf_alpha == ART_ALPHA_SEPARATE)
+		src_mul = src_alpha;
+	      else /* buf_alpha == (ART_ALPHA_PREMUL) */
+		src_mul = alpha;
+	    }
+	  /* src_alpha is the (alpha of the source pixel * alpha),
+	     range 0..0x10000 */
+
+	  src_mul *= 0x101;
+
+	  if (alpha_type == ART_ALPHA_NONE)
+	    {
+	      dst_alpha = 0x10000;
+	      dst_mul = dst_alpha;
+	    }
+	  else
+	    {
+	      tmp = dstptr[n_chan];
+	      /* range 0..0xff */
+	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+	      if (alpha_type == ART_ALPHA_SEPARATE)
+		dst_mul = dst_alpha;
+	      else /* (alpha_type == ART_ALPHA_PREMUL) */
+		dst_mul = 0x10000;
+	    }
+	  /* dst_alpha is the alpha of the dest pixel,
+	     range 0..0x10000 */
+
+	  dst_mul *= 0x101;
+
+	  if (alpha_type == ART_ALPHA_NONE)
+	    {
+	      dst_save_mul = 0xff;
+	    }
+	  else
+	    {
+	      if (src_alpha >= 0x10000)
+		dst_alpha = 0x10000;
+	      else
+		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+	      if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
+		{
+		  dst_save_mul = 0xff;
+		}
+	      else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
+		{
+		  dst_save_mul = 0xff0000 / dst_alpha;
+		}
+	    }
+
+	  for (j = 0; j < n_chan; j++)
+	    {
+	      art_u32 src, dst;
+	      art_u32 tmp;
+
+	      src = (bufptr[j] * src_mul + 0x8000) >> 16;
+	      dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+	      tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
+	      tmp -= tmp >> 16;
+	      dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+	    }
+	  if (alpha_type != ART_ALPHA_NONE)
+	    dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+	  bufptr += buf_pixstride;
+	  dstptr += dst_pixstride;
+	}
+    }
+}
+
+const ArtRenderCallback art_render_composite_8_obj =
+{
+  art_render_composite_8,
+  art_render_nop_done
+};
+
+
+/* Assumes:
+ * alpha_buf is NULL
+ * buf_alpha = ART_ALPHA_NONE  (source)
+ * alpha_type = ART_ALPHA_SEPARATE (dest)
+ * n_chan = 3;
+ */
+static void
+art_render_composite_8_opt1 (ArtRenderCallback *self, ArtRender *render,
+			     art_u8 *dest, int y)
+{
+  ArtRenderMaskRun *run = render->run;
+  int n_run = render->n_run;
+  int x0 = render->x0;
+  int x;
+  int run_x0, run_x1;
+  art_u8 *image_buf = render->image_buf;
+  int i, j;
+  art_u32 tmp;
+  art_u32 run_alpha;
+  int image_ix;
+  art_u8 *bufptr;
+  art_u32 src_mul;
+  art_u8 *dstptr;
+  art_u32 dst_alpha;
+  art_u32 dst_mul, dst_save_mul;
+
+  image_ix = 0;
+  for (i = 0; i < n_run - 1; i++)
+    {
+      run_x0 = run[i].x;
+      run_x1 = run[i + 1].x;
+      tmp = run[i].alpha;
+      if (tmp < 0x10000)
+	continue;
+
+      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+      bufptr = image_buf + (run_x0 - x0) * 3;
+      dstptr = dest + (run_x0 - x0) * 4;
+      if (run_alpha == 0x10000)
+	{
+	  for (x = run_x0; x < run_x1; x++)
+	    {
+	      *dstptr++ = *bufptr++;
+	      *dstptr++ = *bufptr++;
+	      *dstptr++ = *bufptr++;
+	      *dstptr++ = 0xff;
+	    }
+	}
+      else
+	{
+	  for (x = run_x0; x < run_x1; x++)
+	    {
+	      src_mul = run_alpha * 0x101;
+
+	      tmp = dstptr[3];
+	      /* range 0..0xff */
+	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+	      dst_mul = dst_alpha;
+	      /* dst_alpha is the alpha of the dest pixel,
+		 range 0..0x10000 */
+
+	      dst_mul *= 0x101;
+
+	      dst_alpha += ((((0x10000 - dst_alpha) * run_alpha) >> 8) + 0x80) >> 8;
+	      if (dst_alpha == 0)
+		  dst_save_mul = 0xff;
+	      else /* (dst_alpha != 0) */
+		  dst_save_mul = 0xff0000 / dst_alpha;
+
+	      for (j = 0; j < 3; j++)
+		{
+		  art_u32 src, dst;
+		  art_u32 tmp;
+
+		  src = (bufptr[j] * src_mul + 0x8000) >> 16;
+		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+		  tmp = ((dst * (0x10000 - run_alpha) + 0x8000) >> 16) + src;
+		  tmp -= tmp >> 16;
+		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+		}
+	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+	      bufptr += 3;
+	      dstptr += 4;
+	    }
+	}
+    }
+}
+
+
+const ArtRenderCallback art_render_composite_8_opt1_obj =
+{
+  art_render_composite_8_opt1,
+  art_render_nop_done
+};
+
+/* Assumes:
+ * alpha_buf is NULL
+ * buf_alpha = ART_ALPHA_PREMUL  (source)
+ * alpha_type = ART_ALPHA_SEPARATE (dest)
+ * n_chan = 3;
+ */
+static void
+art_render_composite_8_opt2 (ArtRenderCallback *self, ArtRender *render,
+			     art_u8 *dest, int y)
+{
+  ArtRenderMaskRun *run = render->run;
+  int n_run = render->n_run;
+  int x0 = render->x0;
+  int x;
+  int run_x0, run_x1;
+  art_u8 *image_buf = render->image_buf;
+  int i, j;
+  art_u32 tmp;
+  art_u32 run_alpha;
+  int image_ix;
+  art_u8 *bufptr;
+  art_u32 src_alpha;
+  art_u32 src_mul;
+  art_u8 *dstptr;
+  art_u32 dst_alpha;
+  art_u32 dst_mul, dst_save_mul;
+
+  image_ix = 0;
+  for (i = 0; i < n_run - 1; i++)
+    {
+      run_x0 = run[i].x;
+      run_x1 = run[i + 1].x;
+      tmp = run[i].alpha;
+      if (tmp < 0x10000)
+	continue;
+
+      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+      bufptr = image_buf + (run_x0 - x0) * 4;
+      dstptr = dest + (run_x0 - x0) * 4;
+      if (run_alpha == 0x10000)
+	{
+	  for (x = run_x0; x < run_x1; x++)
+	    {
+	      src_alpha = (bufptr[3] << 8) + bufptr[3] + (bufptr[3] >> 7);
+	      /* src_alpha is the (alpha of the source pixel),
+		 range 0..0x10000 */
+
+	      dst_alpha = (dstptr[3] << 8) + dstptr[3] + (dstptr[3] >> 7);
+	      /* dst_alpha is the alpha of the dest pixel,
+		 range 0..0x10000 */
+
+	      dst_mul = dst_alpha*0x101;
+
+	      if (src_alpha >= 0x10000)
+		dst_alpha = 0x10000;
+	      else
+		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+
+	      if (dst_alpha == 0)
+		  dst_save_mul = 0xff;
+	      else /* dst_alpha != 0) */
+		  dst_save_mul = 0xff0000 / dst_alpha;
+
+	      for (j = 0; j < 3; j++)
+		{
+		  art_u32 src, dst;
+		  art_u32 tmp;
+
+		  src = (bufptr[j] << 8) |  bufptr[j];
+		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+		  tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
+		  tmp -= tmp >> 16;
+		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+		}
+	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+	      bufptr += 4;
+	      dstptr += 4;
+	    }
+	}
+      else
+	{
+	  for (x = run_x0; x < run_x1; x++)
+	    {
+	      tmp = run_alpha * bufptr[3] + 0x80;
+	      /* range 0x80 .. 0xff0080 */
+	      src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+	      /* src_alpha is the (alpha of the source pixel * alpha),
+		 range 0..0x10000 */
+
+	      src_mul = run_alpha * 0x101;
+
+	      tmp = dstptr[3];
+	      /* range 0..0xff */
+	      dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+	      dst_mul = dst_alpha;
+	      /* dst_alpha is the alpha of the dest pixel,
+		 range 0..0x10000 */
+
+	      dst_mul *= 0x101;
+
+	      if (src_alpha >= 0x10000)
+		dst_alpha = 0x10000;
+	      else
+		dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+
+	      if (dst_alpha == 0)
+		{
+		  dst_save_mul = 0xff;
+		}
+	      else /* dst_alpha != 0) */
+		{
+		  dst_save_mul = 0xff0000 / dst_alpha;
+		}
+
+	      for (j = 0; j < 3; j++)
+		{
+		  art_u32 src, dst;
+		  art_u32 tmp;
+
+		  src = (bufptr[j] * src_mul + 0x8000) >> 16;
+		  dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+		  tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
+		  tmp -= tmp >> 16;
+		  dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+		}
+	      dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+	      bufptr += 4;
+	      dstptr += 4;
+	    }
+	}
+    }
+}
+
+const ArtRenderCallback art_render_composite_8_opt2_obj =
+{
+  art_render_composite_8_opt2,
+  art_render_nop_done
+};
+
+
+/* todo: inline */
+static ArtRenderCallback *
+art_render_choose_compositing_callback (ArtRender *render)
+{
+  if (render->depth == 8 && render->buf_depth == 8)
+    {
+      if (render->n_chan == 3 &&
+	  render->alpha_buf == NULL &&
+	  render->alpha_type == ART_ALPHA_SEPARATE)
+	{
+	  if (render->buf_alpha == ART_ALPHA_NONE)
+	    return (ArtRenderCallback *)&art_render_composite_8_opt1_obj;
+	  else if (render->buf_alpha == ART_ALPHA_PREMUL)
+	    return (ArtRenderCallback *)&art_render_composite_8_opt2_obj;
+	}
+
+      return (ArtRenderCallback *)&art_render_composite_8_obj;
+    }
+  return (ArtRenderCallback *)&art_render_composite_obj;
+}
+
+/**
+ * art_render_invoke_callbacks: Invoke the callbacks in the render object.
+ * @render: The render object.
+ * @y: The current Y coordinate value.
+ *
+ * Invokes the callbacks of the render object in the appropriate
+ * order.  Drivers should call this routine once per scanline.
+ *
+ * todo: should management of dest devolve to this routine? very
+ * plausibly yes.
+ **/
+void
+art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y)
+{
+  ArtRenderPriv *priv = (ArtRenderPriv *)render;
+  int i;
+
+  for (i = 0; i < priv->n_callbacks; i++)
+    {
+      ArtRenderCallback *callback;
+
+      callback = priv->callbacks[i];
+      callback->render (callback, render, dest, y);
+    }
+}
+
+/**
+ * art_render_invoke: Perform the requested rendering task.
+ * @render: The render object.
+ *
+ * Invokes the renderer and all sources associated with it, to perform
+ * the requested rendering task.
+ **/
+void
+art_render_invoke (ArtRender *render)
+{
+  ArtRenderPriv *priv = (ArtRenderPriv *)render;
+  int width;
+  int best_driver, best_score;
+  int i;
+  int n_callbacks, n_callbacks_max;
+  ArtImageSource *image_source;
+  ArtImageSourceFlags image_flags;
+  int buf_depth;
+  ArtAlphaType buf_alpha;
+  art_boolean first = ART_TRUE;
+
+  if (render == NULL)
+    {
+      art_warn ("art_render_invoke: called with render == NULL\n");
+      return;
+    }
+  if (priv->image_source == NULL)
+    {
+      art_warn ("art_render_invoke: no image source given\n");
+      return;
+    }
+
+  width = render->x1 - render->x0;
+
+  render->run = art_new (ArtRenderMaskRun, width + 1);
+
+  /* Elect a mask source as driver. */
+  best_driver = -1;
+  best_score = 0;
+  for (i = 0; i < priv->n_mask_source; i++)
+    {
+      int score;
+      ArtMaskSource *mask_source;
+
+      mask_source = priv->mask_source[i];
+      score = mask_source->can_drive (mask_source, render);
+      if (score > best_score)
+	{
+	  best_score = score;
+	  best_driver = i;
+	}
+    }
+
+  /* Allocate alpha buffer if needed. */
+  if (priv->n_mask_source > 1 ||
+      (priv->n_mask_source == 1 && best_driver < 0))
+    {
+      render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3);
+    }
+
+  /* Negotiate image rendering and compositing. */
+  image_source = priv->image_source;
+  image_source->negotiate (image_source, render, &image_flags, &buf_depth,
+			   &buf_alpha);
+
+  /* Build callback list. */
+  n_callbacks_max = priv->n_mask_source + 3;
+  priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max);
+  n_callbacks = 0;
+  for (i = 0; i < priv->n_mask_source; i++)
+    if (i != best_driver)
+      {
+	ArtMaskSource *mask_source = priv->mask_source[i];
+
+	mask_source->prepare (mask_source, render, first);
+	first = ART_FALSE;
+	priv->callbacks[n_callbacks++] = &mask_source->super;
+      }
+
+  if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR))
+    priv->callbacks[n_callbacks++] =
+      art_render_choose_clear_callback (render);
+
+  priv->callbacks[n_callbacks++] = &image_source->super;
+
+  /* Allocate image buffer and add compositing callback if needed. */
+  if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE))
+    {
+      int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) *
+		     buf_depth) >> 3;
+      render->buf_depth = buf_depth;
+      render->buf_alpha = buf_alpha;
+      render->image_buf = art_new (art_u8, width * bytespp);
+      priv->callbacks[n_callbacks++] =
+	art_render_choose_compositing_callback (render);
+    }
+
+  priv->n_callbacks = n_callbacks;
+
+  if (render->need_span)
+    render->span_x = art_new (int, width + 1);
+
+  /* Invoke the driver */
+  if (best_driver >= 0)
+    {
+      ArtMaskSource *driver;
+
+      driver = priv->mask_source[best_driver];
+      driver->invoke_driver (driver, render);
+    }
+  else
+    {
+      art_u8 *dest_ptr = render->pixels;
+      int y;
+
+      /* Dummy driver */
+      render->n_run = 2;
+      render->run[0].x = render->x0;
+      render->run[0].alpha = 0x8000 + 0xff * render->opacity;
+      render->run[1].x = render->x1;
+      render->run[1].alpha = 0x8000;
+      if (render->need_span)
+	{
+	  render->n_span = 2;
+	  render->span_x[0] = render->x0;
+	  render->span_x[1] = render->x1;
+	}
+      for (y = render->y0; y < render->y1; y++)
+	{
+	  art_render_invoke_callbacks (render, dest_ptr, y);
+	  dest_ptr += render->rowstride;
+	}
+    }
+
+  if (priv->mask_source != NULL)
+    art_free (priv->mask_source);
+
+  /* clean up callbacks */
+  for (i = 0; i < priv->n_callbacks; i++)
+    {
+      ArtRenderCallback *callback;
+
+      callback = priv->callbacks[i];
+      callback->done (callback, render);
+    }
+
+  /* Tear down object */
+  if (render->alpha_buf != NULL)
+    art_free (render->alpha_buf);
+  if (render->image_buf != NULL)
+    art_free (render->image_buf);
+  art_free (render->run);
+  if (render->span_x != NULL)
+    art_free (render->span_x);
+  art_free (priv->callbacks);
+  art_free (render);
+}
+
+/**
+ * art_render_mask_solid: Add a solid translucent mask.
+ * @render: The render object.
+ * @opacity: Opacity in [0..0x10000] form.
+ *
+ * Adds a translucent mask to the rendering object.
+ **/
+void
+art_render_mask_solid (ArtRender *render, int opacity)
+{
+  art_u32 old_opacity = render->opacity;
+  art_u32 new_opacity_tmp;
+
+  if (opacity == 0x10000)
+    /* avoid potential overflow */
+    return;
+  new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000;
+  render->opacity = new_opacity_tmp >> 16;
+}
+
+/**
+ * art_render_add_mask_source: Add a mask source to the render object.
+ * @render: Render object.
+ * @mask_source: Mask source to add.
+ *
+ * This routine adds a mask source to the render object. In general,
+ * client api's for adding mask sources should just take a render object,
+ * then the mask source creation function should call this function.
+ * Clients should never have to call this function directly, unless of
+ * course they're creating custom mask sources.
+ **/
+void
+art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source)
+{
+  ArtRenderPriv *priv = (ArtRenderPriv *)render;
+  int n_mask_source = priv->n_mask_source++;
+
+  if (n_mask_source == 0)
+    priv->mask_source = art_new (ArtMaskSource *, 1);
+  /* This predicate is true iff n_mask_source is a power of two */
+  else if (!(n_mask_source & (n_mask_source - 1)))
+    priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *,
+				   n_mask_source << 1);
+
+  priv->mask_source[n_mask_source] = mask_source;
+}
+
+/**
+ * art_render_add_image_source: Add a mask source to the render object.
+ * @render: Render object.
+ * @image_source: Image source to add.
+ *
+ * This routine adds an image source to the render object. In general,
+ * client api's for adding image sources should just take a render
+ * object, then the mask source creation function should call this
+ * function.  Clients should never have to call this function
+ * directly, unless of course they're creating custom image sources.
+ **/
+void
+art_render_add_image_source (ArtRender *render, ArtImageSource *image_source)
+{
+  ArtRenderPriv *priv = (ArtRenderPriv *)render;
+
+  if (priv->image_source != NULL)
+    {
+      art_warn ("art_render_add_image_source: image source already present.\n");
+      return;
+    }
+  priv->image_source = image_source;
+}
+
+/* Solid image source object and methods. Perhaps this should go into a
+   separate file. */
+
+typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
+
+struct _ArtImageSourceSolid {
+  ArtImageSource super;
+  ArtPixMaxDepth color[ART_MAX_CHAN];
+  art_u32 *rgbtab;
+  art_boolean init;
+};
+
+static void
+art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render)
+{
+  ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+
+  if (z->rgbtab != NULL)
+    art_free (z->rgbtab);
+  art_free (self);
+}
+
+static void
+art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render)
+{
+  ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+  ArtPixMaxDepth color_max;
+  int r_fg, g_fg, b_fg;
+  int r_bg, g_bg, b_bg;
+  int r, g, b;
+  int dr, dg, db;
+  int i;
+  int tmp;
+  art_u32 *rgbtab;
+
+  rgbtab = art_new (art_u32, 256);
+  z->rgbtab = rgbtab;
+
+  color_max = self->color[0];
+  r_fg = ART_PIX_8_FROM_MAX (color_max);
+  color_max = self->color[1];
+  g_fg = ART_PIX_8_FROM_MAX (color_max);
+  color_max = self->color[2];
+  b_fg = ART_PIX_8_FROM_MAX (color_max);
+
+  color_max = render->clear_color[0];
+  r_bg = ART_PIX_8_FROM_MAX (color_max);
+  color_max = render->clear_color[1];
+  g_bg = ART_PIX_8_FROM_MAX (color_max);
+  color_max = render->clear_color[2];
+  b_bg = ART_PIX_8_FROM_MAX (color_max);
+
+  r = (r_bg << 16) + 0x8000;
+  g = (g_bg << 16) + 0x8000;
+  b = (b_bg << 16) + 0x8000;
+  tmp = ((r_fg - r_bg) << 16) + 0x80;
+  dr = (tmp + (tmp >> 8)) >> 8;
+  tmp = ((g_fg - g_bg) << 16) + 0x80;
+  dg = (tmp + (tmp >> 8)) >> 8;
+  tmp = ((b_fg - b_bg) << 16) + 0x80;
+  db = (tmp + (tmp >> 8)) >> 8;
+
+  for (i = 0; i < 256; i++)
+    {
+      rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
+      r += dr;
+      g += dg;
+      b += db;
+    }
+}
+
+static void
+art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render,
+				  art_u8 *dest, int y)
+{
+  ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+  ArtRenderMaskRun *run = render->run;
+  int n_run = render->n_run;
+  art_u32 *rgbtab = z->rgbtab;
+  art_u32 rgb;
+  int x0 = render->x0;
+  int x1 = render->x1;
+  int run_x0, run_x1;
+  int i;
+  int ix;
+
+  if (n_run > 0)
+    {
+      run_x1 = run[0].x;
+      if (run_x1 > x0)
+	{
+	  rgb = rgbtab[0];
+	  art_rgb_fill_run (dest,
+			    rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+			    run_x1 - x0);
+	}
+      for (i = 0; i < n_run - 1; i++)
+	{
+	  run_x0 = run_x1;
+	  run_x1 = run[i + 1].x;
+	  rgb = rgbtab[(run[i].alpha >> 16) & 0xff];
+	  ix = (run_x0 - x0) * 3;
+#define OPTIMIZE_LEN_1
+#ifdef OPTIMIZE_LEN_1
+	  if (run_x1 - run_x0 == 1)
+	    {
+	      dest[ix] = rgb >> 16;
+	      dest[ix + 1] = (rgb >> 8) & 0xff;
+	      dest[ix + 2] = rgb & 0xff;
+	    }
+	  else
+	    {
+	      art_rgb_fill_run (dest + ix,
+				rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+				run_x1 - run_x0);
+	    }
+#else
+	  art_rgb_fill_run (dest + ix,
+			    rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+			    run_x1 - run_x0);
+#endif
+	}
+    }
+  else
+    {
+      run_x1 = x0;
+    }
+  if (run_x1 < x1)
+    {
+      rgb = rgbtab[0];
+      art_rgb_fill_run (dest + (run_x1 - x0) * 3,
+			rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+			x1 - run_x1);
+    }
+}
+
+static void
+art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render,
+			     art_u8 *dest, int y)
+{
+  ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+  int width = render->x1 - render->x0;
+  art_u8 r, g, b;
+  ArtPixMaxDepth color_max;
+
+  /* todo: replace this simple test with real sparseness */
+  if (z->init)
+    return;
+  z->init = ART_TRUE;
+
+  color_max = z->color[0];
+  r = ART_PIX_8_FROM_MAX (color_max);
+  color_max = z->color[1];
+  g = ART_PIX_8_FROM_MAX (color_max);
+  color_max = z->color[2];
+  b = ART_PIX_8_FROM_MAX (color_max);
+
+  art_rgb_fill_run (render->image_buf, r, g, b, width);
+}
+
+static void
+art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render,
+				  ArtImageSourceFlags *p_flags,
+				  int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+  ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+  ArtImageSourceFlags flags = 0;
+  static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render,
+			     art_u8 *dest, int y);
+
+  render_cbk = NULL;
+
+  if (render->depth == 8 && render->n_chan == 3 &&
+      render->alpha_type == ART_ALPHA_NONE)
+    {
+      if (render->clear)
+	{
+	  render_cbk = art_render_image_solid_rgb8_opaq;
+	  flags |= ART_IMAGE_SOURCE_CAN_CLEAR | ART_IMAGE_SOURCE_CAN_COMPOSITE;
+	  art_render_image_solid_rgb8_opaq_init (z, render);
+	}
+    }
+  if (render_cbk == NULL)
+    {
+      if (render->depth == 8)
+	{
+	  render_cbk = art_render_image_solid_rgb8;
+	  *p_buf_depth = 8;
+	  *p_alpha = ART_ALPHA_NONE; /* todo */
+	}
+    }
+  /* todo: general case */
+  self->super.render = render_cbk;
+  *p_flags = flags;
+}
+
+/**
+ * art_render_image_solid: Add a solid color image source.
+ * @render: The render object.
+ * @color: Color.
+ *
+ * Adds an image source with the solid color given by @color. The
+ * color need not be retained in memory after this call.
+ **/
+void
+art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color)
+{
+  ArtImageSourceSolid *image_source;
+  int i;
+
+  image_source = art_new (ArtImageSourceSolid, 1);
+  image_source->super.super.render = NULL;
+  image_source->super.super.done = art_render_image_solid_done;
+  image_source->super.negotiate = art_render_image_solid_negotiate;
+
+  for (i = 0; i < render->n_chan; i++)
+    image_source->color[i] = color[i];
+
+  image_source->rgbtab = NULL;
+  image_source->init = ART_FALSE;
+
+  art_render_add_image_source (render, &image_source->super);
+}
diff --git a/src/libart_lgpl/art_render.h b/src/libart_lgpl/art_render.h
new file mode 100644
index 0000000..1fc17a4
--- /dev/null
+++ b/src/libart_lgpl/art_render.h
@@ -0,0 +1,182 @@
+/*
+ * art_render.h: Modular rendering architecture.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RENDER_H__
+#define __ART_RENDER_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_alphagamma.h"
+#else
+#include <libart_lgpl/art_alphagamma.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Render object */
+
+#ifndef ART_MAX_DEPTH
+#define ART_MAX_DEPTH 16
+#endif
+
+#if ART_MAX_DEPTH == 16
+typedef art_u16 ArtPixMaxDepth;
+#define ART_PIX_MAX_FROM_8(x) ((x) | ((x) << 8))
+#define ART_PIX_8_FROM_MAX(x) (((x) + 0x80 - (((x) + 0x80) >> 8)) >> 8)
+#else
+#if ART_MAX_DEPTH == 8
+typedef art_u8 ArtPixMaxDepth;
+#define ART_PIX_MAX_FROM_8(x) (x)
+#define ART_PIX_8_FROM_MAX(x) (x)
+#else
+#error ART_MAX_DEPTH must be either 8 or 16
+#endif
+#endif
+
+#define ART_MAX_CHAN 16
+
+typedef struct _ArtRender ArtRender;
+typedef struct _ArtRenderCallback ArtRenderCallback;
+typedef struct _ArtRenderMaskRun ArtRenderMaskRun;
+typedef struct _ArtImageSource ArtImageSource;
+typedef struct _ArtMaskSource ArtMaskSource;
+
+typedef enum {
+  ART_ALPHA_NONE      = 0,
+  ART_ALPHA_SEPARATE  = 1,
+  ART_ALPHA_PREMUL    = 2
+} ArtAlphaType;
+
+typedef enum {
+  ART_COMPOSITE_NORMAL,
+  ART_COMPOSITE_MULTIPLY,
+  /* todo: more */
+  ART_COMPOSITE_CUSTOM
+} ArtCompositingMode;
+
+typedef enum {
+  ART_IMAGE_SOURCE_CAN_CLEAR = 1,
+  ART_IMAGE_SOURCE_CAN_COMPOSITE = 2
+} ArtImageSourceFlags;
+
+struct _ArtRenderMaskRun {
+  int x;
+  int alpha;
+};
+
+struct _ArtRenderCallback {
+  void (*render) (ArtRenderCallback *self, ArtRender *render,
+		  art_u8 *dest, int y);
+  void (*done) (ArtRenderCallback *self, ArtRender *render);
+};
+
+struct _ArtImageSource {
+  ArtRenderCallback super;
+  void (*negotiate) (ArtImageSource *self, ArtRender *render,
+		     ArtImageSourceFlags *p_flags,
+		     int *p_buf_depth, ArtAlphaType *p_alpha_type);
+};
+
+struct _ArtMaskSource {
+  ArtRenderCallback super;
+  int (*can_drive) (ArtMaskSource *self, ArtRender *render);
+  /* For each mask source, ::prepare() is invoked if it is not
+     a driver, or ::invoke_driver() if it is. */
+  void (*invoke_driver) (ArtMaskSource *self, ArtRender *render);
+  void (*prepare) (ArtMaskSource *self, ArtRender *render, art_boolean first);
+};
+
+struct _ArtRender {
+  /* parameters of destination image */
+  int x0, y0;
+  int x1, y1;
+  art_u8 *pixels;
+  int rowstride;
+  int n_chan;
+  int depth;
+  ArtAlphaType alpha_type;
+
+  art_boolean clear;
+  ArtPixMaxDepth clear_color[ART_MAX_CHAN + 1];
+  art_u32 opacity; /* [0..0x10000] */
+
+  ArtCompositingMode compositing_mode;
+
+  ArtAlphaGamma *alphagamma;
+
+  art_u8 *alpha_buf;
+
+  /* parameters of intermediate buffer */
+  int buf_depth;
+  ArtAlphaType buf_alpha;
+  art_u8 *image_buf;
+
+  /* driving alpha scanline data */
+  /* A "run" is a contiguous sequence of x values with the same alpha value. */
+  int n_run;
+  ArtRenderMaskRun *run;
+
+  /* A "span" is a contiguous sequence of x values with non-zero alpha. */
+  int n_span;
+  int *span_x;
+
+  art_boolean need_span;
+};
+
+ArtRender *
+art_render_new (int x0, int y0, int x1, int y1,
+		art_u8 *pixels, int rowstride,
+		int n_chan, int depth, ArtAlphaType alpha_type,
+		ArtAlphaGamma *alphagamma);
+
+void
+art_render_invoke (ArtRender *render);
+
+void
+art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color);
+
+void
+art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb);
+
+void
+art_render_mask_solid (ArtRender *render, int opacity);
+
+void
+art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color);
+
+/* The next two functions are for custom mask sources only. */
+void
+art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source);
+
+void
+art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y);
+
+/* The following function is for custom image sources only. */
+void
+art_render_add_image_source (ArtRender *render, ArtImageSource *image_source);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_H__ */
+
diff --git a/src/libart_lgpl/art_render_gradient.c b/src/libart_lgpl/art_render_gradient.c
new file mode 100644
index 0000000..f02bf2a
--- /dev/null
+++ b/src/libart_lgpl/art_render_gradient.c
@@ -0,0 +1,715 @@
+/*
+ * art_render_gradient.c: Gradient image source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph acm org>
+ *          Alexander Larsson <alla lysator liu se>
+ */
+
+#include "config.h"
+#include "art_render_gradient.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+/* Hack to find out how to define alloca on different platforms.
+ * Modified version of glib/galloca.h.
+ */
+
+#ifdef  __GNUC__
+/* GCC does the right thing */
+# undef alloca
+# define alloca(size)   __builtin_alloca (size)
+#elif defined (HAVE_ALLOCA_H)
+/* a native and working alloca.h is there */
+# include <alloca.h>
+#else /* !__GNUC__ && !HAVE_ALLOCA_H */
+# ifdef _MSC_VER
+#  include <malloc.h>
+#  define alloca _alloca
+# else /* !_MSC_VER */
+#  ifdef _AIX
+ #pragma alloca
+#  else /* !_AIX */
+#   ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+#   endif /* !alloca */
+#  endif /* !_AIX */
+# endif /* !_MSC_VER */
+#endif /* !__GNUC__ && !HAVE_ALLOCA_H */
+
+#undef DEBUG_SPEW
+
+typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin;
+typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad;
+
+/* The stops will be copied right after this structure */
+struct _ArtImageSourceGradLin {
+  ArtImageSource super;
+  ArtGradientLinear gradient;
+  ArtGradientStop stops[1];
+};
+
+/* The stops will be copied right after this structure */
+struct _ArtImageSourceGradRad {
+  ArtImageSource super;
+  ArtGradientRadial gradient;
+  double a;
+  ArtGradientStop stops[1];
+};
+
+#define EPSILON 1e-6
+
+#ifndef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+static void
+art_rgba_gradient_run (art_u8 *buf,
+		       art_u8 *color1,
+		       art_u8 *color2,
+		       int len)
+{
+  int i;
+  int r, g, b, a;
+  int dr, dg, db, da;
+
+#ifdef DEBUG_SPEW
+  printf ("gradient run from %3d %3d %3d %3d to %3d %3d %3d %3d in %d pixels\n",
+	  color1[0], color1[1], color1[2], color1[3],
+	  color2[0], color2[1], color2[2], color2[3],
+	  len);
+#endif
+
+  r = (color1[0] << 16) + 0x8000;
+  g = (color1[1] << 16) + 0x8000;
+  b = (color1[2] << 16) + 0x8000;
+  a = (color1[3] << 16) + 0x8000;
+  dr = ((color2[0] - color1[0]) << 16) / len;
+  dg = ((color2[1] - color1[1]) << 16) / len;
+  db = ((color2[2] - color1[2]) << 16) / len;
+  da = ((color2[3] - color1[3]) << 16) / len;
+
+  for (i = 0; i < len; i++)
+    {
+      *buf++ = (r>>16);
+      *buf++ = (g>>16);
+      *buf++ = (b>>16);
+      *buf++ = (a>>16);
+
+      r += dr;
+      g += dg;
+      b += db;
+      a += da;
+    }
+}
+
+static void
+calc_color_at (ArtGradientStop *stops,
+	       int n_stops,
+	       ArtGradientSpread spread,
+	       double offset,
+	       double offset_fraction,
+	       int favor_start,
+	       int ix,
+	       art_u8 *color)
+{
+  double off0, off1;
+  int j;
+
+  if (spread == ART_GRADIENT_PAD)
+    {
+      if (offset < 0.0)
+	{
+	  color[0] = ART_PIX_8_FROM_MAX (stops[0].color[0]);
+	  color[1] = ART_PIX_8_FROM_MAX (stops[0].color[1]);
+	  color[2] = ART_PIX_8_FROM_MAX (stops[0].color[2]);
+	  color[3] = ART_PIX_8_FROM_MAX (stops[0].color[3]);
+	  return;
+	}
+      if (offset >= 1.0)
+	{
+	  color[0] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[0]);
+	  color[1] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[1]);
+	  color[2] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[2]);
+	  color[3] = ART_PIX_8_FROM_MAX (stops[n_stops-1].color[3]);
+	  return;
+	}
+    }
+
+  if (ix > 0 && ix < n_stops)
+    {
+      off0 = stops[ix - 1].offset;
+      off1 = stops[ix].offset;
+      if (fabs (off1 - off0) > EPSILON)
+	{
+	  double interp;
+	  double o;
+	  o = offset_fraction;
+
+	  if ((fabs (o) < EPSILON) && (!favor_start))
+	    o = 1.0;
+	  else if ((fabs (o-1.0) < EPSILON) && (favor_start))
+	    o = 0.0;
+
+	  /*
+	  if (offset_fraction == 0.0  && !favor_start)
+	    offset_fraction = 1.0;
+	  */
+
+	  interp = (o - off0) / (off1 - off0);
+	  for (j = 0; j < 4; j++)
+	    {
+	      int z0, z1;
+	      int z;
+	      z0 = stops[ix - 1].color[j];
+	      z1 = stops[ix].color[j];
+	      z = floor (z0 + (z1 - z0) * interp + 0.5);
+	      color[j] = ART_PIX_8_FROM_MAX (z);
+	    }
+	  return;
+	}
+      /* If offsets are too close to safely do the division, just
+	 pick the ix color. */
+      color[0] = ART_PIX_8_FROM_MAX (stops[ix].color[0]);
+      color[1] = ART_PIX_8_FROM_MAX (stops[ix].color[1]);
+      color[2] = ART_PIX_8_FROM_MAX (stops[ix].color[2]);
+      color[3] = ART_PIX_8_FROM_MAX (stops[ix].color[3]);
+      return;
+    }
+
+  printf ("WARNING! bad ix %d in calc_color_at() [internal error]\n", ix);
+  assert (0);
+}
+
+static void
+art_render_gradient_linear_render_8 (ArtRenderCallback *self,
+				     ArtRender *render,
+				     art_u8 *dest, int y)
+{
+  ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self;
+  const ArtGradientLinear *gradient = &(z->gradient);
+  int i;
+  int width = render->x1 - render->x0;
+  int len;
+  double offset, d_offset;
+  double offset_fraction;
+  int next_stop;
+  int ix;
+  art_u8 color1[4], color2[4];
+  int n_stops = gradient->n_stops;
+  int extra_stops;
+  ArtGradientStop *stops = gradient->stops;
+  ArtGradientStop *tmp_stops;
+  art_u8 *bufp = render->image_buf;
+  ArtGradientSpread spread = gradient->spread;
+
+#ifdef DEBUG_SPEW
+  printf ("x1: %d, x2: %d, y: %d\n", render->x0, render->x1, y);
+  printf ("spread: %d, stops:", gradient->spread);
+  for (i=0;i<n_stops;i++)
+    {
+      printf ("%f, ", gradient->stops[i].offset);
+    }
+  printf ("\n");
+  printf ("a: %f, b: %f, c: %f\n", gradient->a, gradient->b, gradient->c);
+#endif
+
+  offset = render->x0 * gradient->a + y * gradient->b + gradient->c;
+  d_offset = gradient->a;
+
+  /* We need to force the gradient to extend the whole 0..1 segment,
+     because the rest of the code doesn't handle partial gradients
+     correctly */
+  if ((gradient->stops[0].offset > EPSILON /* == 0.0 */) ||
+      (gradient->stops[n_stops-1].offset < (1.0 - EPSILON)))
+  {
+    extra_stops = 0;
+    tmp_stops = stops = alloca (sizeof (ArtGradientStop) * (n_stops + 2));
+    if (gradient->stops[0].offset > EPSILON /* 0.0 */)
+      {
+	memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop));
+	tmp_stops[0].offset = 0.0;
+	tmp_stops += 1;
+	extra_stops++;
+      }
+    memcpy (tmp_stops, gradient->stops, sizeof (ArtGradientStop) * n_stops);
+    if (gradient->stops[n_stops-1].offset < (1.0 - EPSILON))
+      {
+	tmp_stops += n_stops;
+	memcpy (tmp_stops, &gradient->stops[n_stops-1], sizeof (ArtGradientStop));
+	tmp_stops[0].offset = 1.0;
+	extra_stops++;
+      }
+    n_stops += extra_stops;
+
+
+#ifdef DEBUG_SPEW
+    printf ("start/stop modified stops:");
+    for (i=0;i<n_stops;i++)
+      {
+	printf ("%f, ", stops[i].offset);
+      }
+    printf ("\n");
+#endif
+
+  }
+
+  if (spread == ART_GRADIENT_REFLECT)
+    {
+      tmp_stops = stops;
+      stops = alloca (sizeof (ArtGradientStop) * n_stops * 2);
+      memcpy (stops, tmp_stops, sizeof (ArtGradientStop) * n_stops);
+
+      for (i = 0; i< n_stops; i++)
+	{
+	  stops[n_stops * 2 - 1 - i].offset = (1.0 - stops[i].offset / 2.0);
+	  memcpy (stops[n_stops * 2 - 1 - i].color, stops[i].color, sizeof (stops[i].color));
+	  stops[i].offset = stops[i].offset / 2.0;
+	}
+
+      spread = ART_GRADIENT_REPEAT;
+      offset = offset / 2.0;
+      d_offset = d_offset / 2.0;
+
+      n_stops = 2 * n_stops;
+
+#ifdef DEBUG_SPEW
+      printf ("reflect modified stops:");
+      for (i=0;i<n_stops;i++)
+	{
+	  printf ("%f, ", stops[i].offset);
+	}
+      printf ("\n");
+#endif
+    }
+
+  offset_fraction = offset - floor (offset);
+#ifdef DEBUG_SPEW
+  printf ("inital offset: %f, fraction: %f d_offset: %f\n", offset, offset_fraction, d_offset);
+#endif
+  /* ix is selected so that offset_fraction is
+     stops[ix-1] <= offset_fraction <= stops[ix]
+     If offset_fraction is equal to one of the edges, ix
+     is selected so the the section of the line extending
+     in the same direction as d_offset is between ix-1 and ix.
+  */
+  for (ix = 0; ix < n_stops; ix++)
+    if (stops[ix].offset > offset_fraction ||
+	(d_offset < 0.0 && fabs (stops[ix].offset - offset_fraction) < EPSILON))
+      break;
+  if (ix == 0)
+    ix = n_stops - 1;
+  else if (ix == n_stops)
+    ix = n_stops - 1;
+
+#ifdef DEBUG_SPEW
+  printf ("Initial ix: %d\n", ix);
+#endif
+
+  assert (ix > 0);
+  assert (ix < n_stops);
+  assert ((stops[ix-1].offset <= offset_fraction + EPSILON) ||
+	  ((stops[ix].offset > (1.0 - EPSILON)) && (offset_fraction < EPSILON /* == 0.0*/)));
+  assert (offset_fraction <= stops[ix].offset);
+  /* FIXME: These asserts may be broken, it is for now
+     safer to not use them.  Should be fixed!
+     See bug #121850
+  assert ((offset_fraction != stops[ix-1].offset) ||
+	  (d_offset >= 0.0));
+  assert ((offset_fraction != stops[ix].offset) ||
+	  (d_offset <= 0.0));
+  */
+
+  while (width > 0)
+    {
+#ifdef DEBUG_SPEW
+      printf ("ix: %d\n", ix);
+      printf ("start offset: %f\n", offset);
+#endif
+      calc_color_at (stops, n_stops,
+		     spread,
+		     offset,
+		     offset_fraction,
+		     (d_offset > -EPSILON),
+		     ix,
+		     color1);
+
+      if (d_offset > 0)
+	next_stop = ix;
+      else
+	next_stop = ix-1;
+
+#ifdef DEBUG_SPEW
+      printf ("next_stop: %d\n", next_stop);
+#endif
+      if (fabs (d_offset) > EPSILON)
+	{
+	  double o;
+	  o = offset_fraction;
+
+	  if ((fabs (o) <= EPSILON) && (ix == n_stops - 1))
+	    o = 1.0;
+	  else if ((fabs (o-1.0) <= EPSILON) && (ix == 1))
+	    o = 0.0;
+
+#ifdef DEBUG_SPEW
+	  printf ("o: %f\n", o);
+#endif
+	  len = (int)floor (fabs ((stops[next_stop].offset - o) / d_offset)) + 1;
+	  len = MAX (len, 0);
+	  len = MIN (len, width);
+	}
+      else
+	{
+	  len = width;
+	}
+#ifdef DEBUG_SPEW
+      printf ("len: %d\n", len);
+#endif
+      if (len > 0)
+	{
+	  offset = offset + (len-1) * d_offset;
+	  offset_fraction = offset - floor (offset);
+#ifdef DEBUG_SPEW
+	  printf ("end offset: %f, fraction: %f\n", offset, offset_fraction);
+#endif
+	  calc_color_at (stops, n_stops,
+			 spread,
+			 offset,
+			 offset_fraction,
+			 (d_offset < EPSILON),
+			 ix,
+			 color2);
+
+	  art_rgba_gradient_run (bufp,
+				 color1,
+				 color2,
+				 len);
+	  offset += d_offset;
+	  offset_fraction = offset - floor (offset);
+	}
+
+      if (d_offset > 0)
+	{
+	  do
+	    {
+	      ix++;
+	      if (ix == n_stops)
+		ix = 1;
+	      /* Note: offset_fraction can actually be one here on x86 machines that
+		 does calculations with extended precision, but later rounds to 64bit.
+		 This happens if the 80bit offset_fraction is larger than the
+		 largest 64bit double that is less than one.
+	      */
+	    }
+	  while (!((stops[ix-1].offset <= offset_fraction &&
+		   offset_fraction < stops[ix].offset) ||
+		   (ix == 1 && offset_fraction > (1.0 - EPSILON))));
+	}
+      else
+	{
+	  do
+	    {
+	      ix--;
+	      if (ix == 0)
+		ix = n_stops - 1;
+	    }
+	  while (!((stops[ix-1].offset < offset_fraction &&
+		    offset_fraction <= stops[ix].offset) ||
+		   (ix == n_stops - 1 && offset_fraction < EPSILON /* == 0.0*/)));
+	}
+
+      bufp += 4*len;
+      width -= len;
+    }
+}
+
+
+/**
+ * art_render_gradient_setpix: Set a gradient pixel.
+ * @render: The render object.
+ * @dst: Pointer to destination (where to store pixel).
+ * @n_stops: Number of stops in @stops.
+ * @stops: The stops for the gradient.
+ * @offset: The offset.
+ *
+ * @n_stops must be > 0.
+ *
+ * Sets a gradient pixel, storing it at @dst.
+ **/
+static void
+art_render_gradient_setpix (ArtRender *render,
+			    art_u8 *dst,
+			    int n_stops, ArtGradientStop *stops,
+			    double offset)
+{
+  int ix;
+  int j;
+  double off0, off1;
+  int n_ch = render->n_chan + 1;
+
+  for (ix = 0; ix < n_stops; ix++)
+    if (stops[ix].offset > offset)
+      break;
+  /* stops[ix - 1].offset < offset < stops[ix].offset */
+  if (ix > 0 && ix < n_stops)
+    {
+      off0 = stops[ix - 1].offset;
+      off1 = stops[ix].offset;
+      if (fabs (off1 - off0) > EPSILON)
+	{
+	  double interp;
+
+	  interp = (offset - off0) / (off1 - off0);
+	  for (j = 0; j < n_ch; j++)
+	    {
+	      int z0, z1;
+	      int z;
+	      z0 = stops[ix - 1].color[j];
+	      z1 = stops[ix].color[j];
+	      z = floor (z0 + (z1 - z0) * interp + 0.5);
+	      if (render->buf_depth == 8)
+		dst[j] = ART_PIX_8_FROM_MAX (z);
+	      else /* (render->buf_depth == 16) */
+		((art_u16 *)dst)[j] = z;
+	    }
+	  return;
+	}
+    }
+  else if (ix == n_stops)
+    ix--;
+
+  for (j = 0; j < n_ch; j++)
+    {
+      int z;
+      z = stops[ix].color[j];
+      if (render->buf_depth == 8)
+	dst[j] = ART_PIX_8_FROM_MAX (z);
+      else /* (render->buf_depth == 16) */
+	((art_u16 *)dst)[j] = z;
+    }
+}
+
+static void
+art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render)
+{
+  art_free (self);
+}
+
+static void
+art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render,
+				   art_u8 *dest, int y)
+{
+  ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self;
+  const ArtGradientLinear *gradient = &(z->gradient);
+  int pixstride = (render->n_chan + 1) * (render->depth >> 3);
+  int x;
+  int width = render->x1 - render->x0;
+  double offset, d_offset;
+  double actual_offset;
+  int n_stops = gradient->n_stops;
+  ArtGradientStop *stops = gradient->stops;
+  art_u8 *bufp = render->image_buf;
+  ArtGradientSpread spread = gradient->spread;
+
+  offset = render->x0 * gradient->a + y * gradient->b + gradient->c;
+  d_offset = gradient->a;
+
+  for (x = 0; x < width; x++)
+    {
+      if (spread == ART_GRADIENT_PAD)
+	actual_offset = offset;
+      else if (spread == ART_GRADIENT_REPEAT)
+	actual_offset = offset - floor (offset);
+      else /* (spread == ART_GRADIENT_REFLECT) */
+	{
+	  double tmp;
+
+	  tmp = offset - 2 * floor (0.5 * offset);
+	  actual_offset = tmp > 1 ? 2 - tmp : tmp;
+	}
+      art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset);
+      offset += d_offset;
+      bufp += pixstride;
+    }
+}
+
+static void
+art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render,
+				      ArtImageSourceFlags *p_flags,
+				      int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+  if (render->depth == 8 &&
+      render->n_chan == 3)
+    {
+      self->super.render = art_render_gradient_linear_render_8;
+      *p_flags = 0;
+      *p_buf_depth = 8;
+      *p_alpha = ART_ALPHA_PREMUL;
+      return;
+    }
+
+  self->super.render = art_render_gradient_linear_render;
+  *p_flags = 0;
+  *p_buf_depth = render->depth;
+  *p_alpha = ART_ALPHA_PREMUL;
+}
+
+/**
+ * art_render_gradient_linear: Add a linear gradient image source.
+ * @render: The render object.
+ * @gradient: The linear gradient.
+ *
+ * Adds the linear gradient @gradient as the image source for rendering
+ * in the render object @render.
+ **/
+void
+art_render_gradient_linear (ArtRender *render,
+			    const ArtGradientLinear *gradient,
+			    ArtFilterLevel level)
+{
+  ArtImageSourceGradLin *image_source = art_alloc (sizeof (ArtImageSourceGradLin) +
+						   sizeof (ArtGradientStop) * (gradient->n_stops - 1));
+
+  image_source->super.super.render = NULL;
+  image_source->super.super.done = art_render_gradient_linear_done;
+  image_source->super.negotiate = art_render_gradient_linear_negotiate;
+
+  /* copy the gradient into the structure */
+  image_source->gradient = *gradient;
+  image_source->gradient.stops = image_source->stops;
+  memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops);
+
+  art_render_add_image_source (render, &image_source->super);
+}
+
+static void
+art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render)
+{
+  art_free (self);
+}
+
+static void
+art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render,
+				   art_u8 *dest, int y)
+{
+  ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self;
+  const ArtGradientRadial *gradient = &(z->gradient);
+  int pixstride = (render->n_chan + 1) * (render->depth >> 3);
+  int x;
+  int x0 = render->x0;
+  int width = render->x1 - x0;
+  int n_stops = gradient->n_stops;
+  ArtGradientStop *stops = gradient->stops;
+  art_u8 *bufp = render->image_buf;
+  double fx = gradient->fx;
+  double fy = gradient->fy;
+  double dx, dy;
+  const double *affine = gradient->affine;
+  double aff0 = affine[0];
+  double aff1 = affine[1];
+  const double a = z->a;
+  const double arecip = 1.0 / a;
+  double b, db;
+  double c, dc, ddc;
+  double b_a, db_a;
+  double rad, drad, ddrad;
+
+  dx = x0 * aff0 + y * affine[2] + affine[4] - fx;
+  dy = x0 * aff1 + y * affine[3] + affine[5] - fy;
+  b = dx * fx + dy * fy;
+  db = aff0 * fx + aff1 * fy;
+  c = dx * dx + dy * dy;
+  dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1;
+  ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1;
+
+  b_a = b * arecip;
+  db_a = db * arecip;
+
+  rad = b_a * b_a + c * arecip;
+  drad = 2 * b_a * db_a + db_a * db_a + dc * arecip;
+  ddrad = 2 * db_a * db_a + ddc * arecip;
+
+  for (x = 0; x < width; x++)
+    {
+      double z;
+
+      if (rad > 0)
+	z = b_a + sqrt (rad);
+      else
+	z = b_a;
+      art_render_gradient_setpix (render, bufp, n_stops, stops, z);
+      bufp += pixstride;
+      b_a += db_a;
+      rad += drad;
+      drad += ddrad;
+    }
+}
+
+static void
+art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render,
+				      ArtImageSourceFlags *p_flags,
+				      int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+  self->super.render = art_render_gradient_radial_render;
+  *p_flags = 0;
+  *p_buf_depth = render->depth;
+  *p_alpha = ART_ALPHA_PREMUL;
+}
+
+/**
+ * art_render_gradient_radial: Add a radial gradient image source.
+ * @render: The render object.
+ * @gradient: The radial gradient.
+ *
+ * Adds the radial gradient @gradient as the image source for rendering
+ * in the render object @render.
+ **/
+void
+art_render_gradient_radial (ArtRender *render,
+			    const ArtGradientRadial *gradient,
+			    ArtFilterLevel level)
+{
+  ArtImageSourceGradRad *image_source = art_alloc (sizeof (ArtImageSourceGradRad) +
+						   sizeof (ArtGradientStop) * (gradient->n_stops - 1));
+  double fx = gradient->fx;
+  double fy = gradient->fy;
+
+  image_source->super.super.render = NULL;
+  image_source->super.super.done = art_render_gradient_radial_done;
+  image_source->super.negotiate = art_render_gradient_radial_negotiate;
+
+  /* copy the gradient into the structure */
+  image_source->gradient = *gradient;
+  image_source->gradient.stops = image_source->stops;
+  memcpy (image_source->gradient.stops, gradient->stops, sizeof (ArtGradientStop) * gradient->n_stops);
+
+  /* todo: sanitycheck fx, fy? */
+  image_source->a = 1 - fx * fx - fy * fy;
+
+  art_render_add_image_source (render, &image_source->super);
+}
diff --git a/src/libart_lgpl/art_render_gradient.h b/src/libart_lgpl/art_render_gradient.h
new file mode 100644
index 0000000..2ae76bf
--- /dev/null
+++ b/src/libart_lgpl/art_render_gradient.h
@@ -0,0 +1,85 @@
+/*
+ * art_render_gradient.h: Gradient image source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph acm org>
+ *          Alexander Larsson <alla lysator liu se>
+ */
+
+#ifndef __ART_RENDER_GRADIENT_H__
+#define __ART_RENDER_GRADIENT_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_filterlevel.h"
+#include "art_render.h"
+#else
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_render.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtGradientLinear ArtGradientLinear;
+typedef struct _ArtGradientRadial ArtGradientRadial;
+typedef struct _ArtGradientStop ArtGradientStop;
+
+typedef enum {
+  ART_GRADIENT_PAD,
+  ART_GRADIENT_REFLECT,
+  ART_GRADIENT_REPEAT
+} ArtGradientSpread;
+
+struct _ArtGradientLinear {
+  double a;
+  double b;
+  double c;
+  ArtGradientSpread spread;
+  int n_stops;
+  ArtGradientStop *stops;
+};
+
+struct _ArtGradientRadial {
+  double affine[6]; /* transforms user coordinates to unit circle */
+  double fx, fy;    /* focal point in unit circle coords */
+  int n_stops;
+  ArtGradientStop *stops;
+};
+
+struct _ArtGradientStop {
+  double offset;
+  ArtPixMaxDepth color[ART_MAX_CHAN + 1];
+};
+
+void
+art_render_gradient_linear (ArtRender *render,
+			    const ArtGradientLinear *gradient,
+			    ArtFilterLevel level);
+
+void
+art_render_gradient_radial (ArtRender *render,
+			    const ArtGradientRadial *gradient,
+			    ArtFilterLevel level);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_GRADIENT_H__ */
diff --git a/src/libart_lgpl/art_render_mask.c b/src/libart_lgpl/art_render_mask.c
new file mode 100644
index 0000000..4cb6116
--- /dev/null
+++ b/src/libart_lgpl/art_render_mask.c
@@ -0,0 +1,167 @@
+/*
+ * art_render_mask.c: Alpha mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph acm org>
+ */
+
+#include "config.h"
+#include "art_render_mask.h"
+
+#include <string.h>
+
+
+typedef struct _ArtMaskSourceMask ArtMaskSourceMask;
+
+struct _ArtMaskSourceMask {
+  ArtMaskSource super;
+  ArtRender *render;
+  art_boolean first;
+  int x0;
+  int y0;
+  int x1;
+  int y1;
+  const art_u8 *mask_buf;
+  int rowstride;
+};
+
+static void
+art_render_mask_done (ArtRenderCallback *self, ArtRender *render)
+{
+  art_free (self);
+}
+
+static int
+art_render_mask_can_drive (ArtMaskSource *self, ArtRender *render)
+{
+  return 0;
+}
+
+static void
+art_render_mask_render (ArtRenderCallback *self, ArtRender *render,
+			art_u8 *dest, int y)
+{
+  ArtMaskSourceMask *z = (ArtMaskSourceMask *)self;
+  int x0 = render->x0, x1 = render->x1;
+  int z_x0 = z->x0, z_x1 = z->x1;
+  int width = x1 - x0;
+  int z_width = z_x1 - z_x0;
+  art_u8 *alpha_buf = render->alpha_buf;
+
+  if (y < z->y0 || y >= z->y1 || z_width <= 0)
+    memset (alpha_buf, 0, width);
+  else
+    {
+      const art_u8 *src_line = z->mask_buf + (y - z->y0) * z->rowstride;
+      art_u8 *dst_line = alpha_buf + z_x0 - x0;
+
+      if (z_x0 > x0)
+	memset (alpha_buf, 0, z_x0 - x0);
+
+      if (z->first)
+	memcpy (dst_line, src_line, z_width);
+      else
+	{
+	  int x;
+
+	  for (x = 0; x < z_width; x++)
+	    {
+	      int v;
+	      v = src_line[x];
+	      if (v)
+		{
+		  v = v * dst_line[x] + 0x80;
+		  v = (v + (v >> 8)) >> 8;
+		  dst_line[x] = v;
+		}
+	      else
+		{
+		  dst_line[x] = 0;
+		}
+	    }
+	}
+
+      if (z_x1 < x1)
+	memset (alpha_buf + z_x1 - x0, 0, x1 - z_x1);
+    }
+}
+
+static void
+art_render_mask_prepare (ArtMaskSource *self, ArtRender *render,
+			 art_boolean first)
+{
+  ArtMaskSourceMask *z = (ArtMaskSourceMask *)self;
+  self->super.render = art_render_mask_render;
+  z->first = first;
+}
+
+/**
+ * art_render_mask: Use an alpha buffer as a render mask source.
+ * @render: Render object.
+ * @x0: Left coordinate of mask rect.
+ * @y0: Top coordinate of mask rect.
+ * @x1: Right coordinate of mask rect.
+ * @y1: Bottom coordinate of mask rect.
+ * @mask_buf: Buffer containing 8bpp alpha mask data.
+ * @rowstride: Rowstride of @mask_buf.
+ *
+ * Adds @mask_buf to the render object as a mask. Note: @mask_buf must
+ * remain allocated until art_render_invoke() is called on @render.
+ **/
+void
+art_render_mask (ArtRender *render,
+		 int x0, int y0, int x1, int y1,
+		 const art_u8 *mask_buf, int rowstride)
+{
+  ArtMaskSourceMask *mask_source;
+
+  if (x0 < render->x0)
+    {
+      mask_buf += render->x0 - x0;
+      x0 = render->x0;
+    }
+  if (x1 > render->x1)
+    x1 = render->x1;
+
+  if (y0 < render->y0)
+    {
+      mask_buf += (render->y0 - y0) * rowstride;
+      y0 = render->y0;
+    }
+  if (y1 > render->y1)
+    y1 = render->y1;
+
+  mask_source = art_new (ArtMaskSourceMask, 1);
+
+  mask_source->super.super.render = NULL;
+  mask_source->super.super.done = art_render_mask_done;
+  mask_source->super.can_drive = art_render_mask_can_drive;
+  mask_source->super.invoke_driver = NULL;
+  mask_source->super.prepare = art_render_mask_prepare;
+  mask_source->render = render;
+  mask_source->x0 = x0;
+  mask_source->y0 = y0;
+  mask_source->x1 = x1;
+  mask_source->y1 = y1;
+  mask_source->mask_buf = mask_buf;
+  mask_source->rowstride = rowstride;
+
+  art_render_add_mask_source (render, &mask_source->super);
+
+}
diff --git a/src/libart_lgpl/art_render_mask.h b/src/libart_lgpl/art_render_mask.h
new file mode 100644
index 0000000..76a4c86
--- /dev/null
+++ b/src/libart_lgpl/art_render_mask.h
@@ -0,0 +1,46 @@
+/*
+ * art_render_mask.h: Alpha mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph acm org>
+ */
+
+#ifndef __ART_RENDER_MASK_H__
+#define __ART_RENDER_MASK_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_render.h"
+#else
+#include <libart_lgpl/art_render.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_render_mask (ArtRender *render,
+		 int x0, int y0, int x1, int y1,
+		 const art_u8 *mask_buf, int rowstride);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_MASK_H__ */
diff --git a/src/libart_lgpl/art_render_svp.c b/src/libart_lgpl/art_render_svp.c
new file mode 100644
index 0000000..67c0459
--- /dev/null
+++ b/src/libart_lgpl/art_render_svp.c
@@ -0,0 +1,420 @@
+/*
+ * art_render_gradient.c: SVP mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph acm org>
+ */
+
+#include "art_render_svp.h"
+#include "art_svp_render_aa.h"
+
+typedef struct _ArtMaskSourceSVP ArtMaskSourceSVP;
+
+struct _ArtMaskSourceSVP {
+  ArtMaskSource super;
+  ArtRender *render;
+  const ArtSVP *svp;
+  art_u8 *dest_ptr;
+};
+
+static void
+art_render_svp_done (ArtRenderCallback *self, ArtRender *render)
+{
+  art_free (self);
+}
+
+static int
+art_render_svp_can_drive (ArtMaskSource *self, ArtRender *render)
+{
+  return 10;
+}
+
+/* The basic art_render_svp_callback function is repeated four times,
+   for all combinations of non-unit opacity and generating spans. In
+   general, I'd consider this bad style, but in this case I plead
+   a measurable performance improvement. */
+
+static void
+art_render_svp_callback (void *callback_data, int y,
+			 int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+  ArtRender *render = z->render;
+  int n_run = 0;
+  int i;
+  int running_sum = start;
+  int x0 = render->x0;
+  int x1 = render->x1;
+  int run_x0, run_x1;
+  ArtRenderMaskRun *run = render->run;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > x0 && running_sum > 0x80ff)
+	{
+	  run[0].x = x0;
+	  run[0].alpha = running_sum;
+	  n_run++;
+	}
+
+      for (i = 0; i < n_steps - 1; i++)
+	{
+          running_sum += steps[i].delta;
+          run_x0 = run_x1;
+          run_x1 = steps[i + 1].x;
+	  if (run_x1 > run_x0)
+	    {
+	      run[n_run].x = run_x0;
+	      run[n_run].alpha = running_sum;
+	      n_run++;
+	    }
+	}
+      if (x1 > run_x1)
+	{
+	  running_sum += steps[n_steps - 1].delta;
+	  run[n_run].x = run_x1;
+	  run[n_run].alpha = running_sum;
+	  n_run++;
+	}
+      if (running_sum > 0x80ff)
+	{
+	  run[n_run].x = x1;
+	  run[n_run].alpha = 0x8000;
+	  n_run++;
+	}
+    }
+  else if ((running_sum >> 16) > 0)
+    {
+      run[0].x = x0;
+      run[0].alpha = running_sum;
+      run[1].x = x1;
+      run[1].alpha = running_sum;
+      n_run = 2;
+    }
+
+  render->n_run = n_run;
+
+  art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+  z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_span (void *callback_data, int y,
+			      int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+  ArtRender *render = z->render;
+  int n_run = 0;
+  int n_span = 0;
+  int i;
+  int running_sum = start;
+  int x0 = render->x0;
+  int x1 = render->x1;
+  int run_x0, run_x1;
+  ArtRenderMaskRun *run = render->run;
+  int *span_x = render->span_x;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > x0 && running_sum > 0x80ff)
+	{
+	  run[0].x = x0;
+	  run[0].alpha = running_sum;
+	  n_run++;
+	  span_x[0] = x0;
+	  n_span++;
+	}
+
+      for (i = 0; i < n_steps - 1; i++)
+	{
+          running_sum += steps[i].delta;
+          run_x0 = run_x1;
+          run_x1 = steps[i + 1].x;
+	  if (run_x1 > run_x0)
+	    {
+	      run[n_run].x = run_x0;
+	      run[n_run].alpha = running_sum;
+	      n_run++;
+	      if ((n_span & 1) != (running_sum > 0x80ff))
+		span_x[n_span++] = run_x0;
+	    }
+	}
+      if (x1 > run_x1)
+	{
+	  running_sum += steps[n_steps - 1].delta;
+	  run[n_run].x = run_x1;
+	  run[n_run].alpha = running_sum;
+	  n_run++;
+	  if ((n_span & 1) != (running_sum > 0x80ff))
+	    span_x[n_span++] = run_x1;
+	}
+      if (running_sum > 0x80ff)
+	{
+	  run[n_run].x = x1;
+	  run[n_run].alpha = 0x8000;
+	  n_run++;
+	  span_x[n_span++] = x1;
+	}
+    }
+  else if ((running_sum >> 16) > 0)
+    {
+      run[0].x = x0;
+      run[0].alpha = running_sum;
+      run[1].x = x1;
+      run[1].alpha = running_sum;
+      n_run = 2;
+      span_x[0] = x0;
+      span_x[1] = x1;
+      n_span = 2;
+    }
+
+  render->n_run = n_run;
+  render->n_span = n_span;
+
+  art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+  z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_opacity (void *callback_data, int y,
+				 int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+  ArtRender *render = z->render;
+  int n_run = 0;
+  int i;
+  art_u32 running_sum;
+  int x0 = render->x0;
+  int x1 = render->x1;
+  int run_x0, run_x1;
+  ArtRenderMaskRun *run = render->run;
+  art_u32 opacity = render->opacity;
+  art_u32 alpha;
+
+  running_sum = start - 0x7f80;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+      if (run_x1 > x0 && alpha > 0x80ff)
+	{
+	  run[0].x = x0;
+	  run[0].alpha = alpha;
+	  n_run++;
+	}
+
+      for (i = 0; i < n_steps - 1; i++)
+	{
+          running_sum += steps[i].delta;
+          run_x0 = run_x1;
+          run_x1 = steps[i + 1].x;
+	  if (run_x1 > run_x0)
+	    {
+	      run[n_run].x = run_x0;
+	      alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+	      run[n_run].alpha = alpha;
+	      n_run++;
+	    }
+	}
+      if (x1 > run_x1)
+	{
+	  running_sum += steps[n_steps - 1].delta;
+	  run[n_run].x = run_x1;
+	  alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+	  run[n_run].alpha = alpha;
+	  n_run++;
+	}
+      if (alpha > 0x80ff)
+	{
+	  run[n_run].x = x1;
+	  run[n_run].alpha = 0x8000;
+	  n_run++;
+	}
+    }
+  else if ((running_sum >> 16) > 0)
+    {
+      run[0].x = x0;
+      run[0].alpha = running_sum;
+      run[1].x = x1;
+      run[1].alpha = running_sum;
+      n_run = 2;
+    }
+
+  render->n_run = n_run;
+
+  art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+  z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_opacity_span (void *callback_data, int y,
+				      int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+  ArtRender *render = z->render;
+  int n_run = 0;
+  int n_span = 0;
+  int i;
+  art_u32 running_sum;
+  int x0 = render->x0;
+  int x1 = render->x1;
+  int run_x0, run_x1;
+  ArtRenderMaskRun *run = render->run;
+  int *span_x = render->span_x;
+  art_u32 opacity = render->opacity;
+  art_u32 alpha;
+
+  running_sum = start - 0x7f80;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+      if (run_x1 > x0 && alpha > 0x80ff)
+	{
+	  run[0].x = x0;
+	  run[0].alpha = alpha;
+	  n_run++;
+	  span_x[0] = x0;
+	  n_span++;
+	}
+
+      for (i = 0; i < n_steps - 1; i++)
+	{
+          running_sum += steps[i].delta;
+          run_x0 = run_x1;
+          run_x1 = steps[i + 1].x;
+	  if (run_x1 > run_x0)
+	    {
+	      run[n_run].x = run_x0;
+	      alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+	      run[n_run].alpha = alpha;
+	      n_run++;
+	      if ((n_span & 1) != (alpha > 0x80ff))
+		span_x[n_span++] = run_x0;
+	    }
+	}
+      if (x1 > run_x1)
+	{
+	  running_sum += steps[n_steps - 1].delta;
+	  run[n_run].x = run_x1;
+	  alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+	  run[n_run].alpha = alpha;
+	  n_run++;
+	  if ((n_span & 1) != (alpha > 0x80ff))
+	    span_x[n_span++] = run_x1;
+	}
+      if (alpha > 0x80ff)
+	{
+	  run[n_run].x = x1;
+	  run[n_run].alpha = 0x8000;
+	  n_run++;
+	  span_x[n_span++] = x1;
+	}
+    }
+  else if ((running_sum >> 16) > 0)
+    {
+      run[0].x = x0;
+      run[0].alpha = running_sum;
+      run[1].x = x1;
+      run[1].alpha = running_sum;
+      n_run = 2;
+      span_x[0] = x0;
+      span_x[1] = x1;
+      n_span = 2;
+    }
+
+  render->n_run = n_run;
+  render->n_span = n_span;
+
+  art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+  z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_invoke_driver (ArtMaskSource *self, ArtRender *render)
+{
+  ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)self;
+  void (*callback) (void *callback_data,
+		    int y,
+		    int start,
+		    ArtSVPRenderAAStep *steps, int n_steps);
+
+  z->dest_ptr = render->pixels;
+  if (render->opacity == 0x10000)
+    {
+      if (render->need_span)
+	callback = art_render_svp_callback_span;
+      else
+	callback = art_render_svp_callback;
+    }
+  else
+    {
+      if (render->need_span)
+	callback = art_render_svp_callback_opacity_span;
+      else
+	callback = art_render_svp_callback_opacity;
+    }
+
+  art_svp_render_aa (z->svp,
+		     render->x0, render->y0,
+		     render->x1, render->y1, callback,
+		     self);
+  art_render_svp_done (&self->super, render);
+}
+
+static void
+art_render_svp_prepare (ArtMaskSource *self, ArtRender *render,
+			art_boolean first)
+{
+  /* todo */
+  art_die ("art_render_svp non-driver mode not yet implemented.\n");
+}
+
+/**
+ * art_render_svp: Use an SVP as a render mask source.
+ * @render: Render object.
+ * @svp: SVP.
+ *
+ * Adds @svp to the render object as a mask. Note: @svp must remain
+ * allocated until art_render_invoke() is called on @render.
+ **/
+void
+art_render_svp (ArtRender *render, const ArtSVP *svp)
+{
+  ArtMaskSourceSVP *mask_source;
+  mask_source = art_new (ArtMaskSourceSVP, 1);
+
+  mask_source->super.super.render = NULL;
+  mask_source->super.super.done = art_render_svp_done;
+  mask_source->super.can_drive = art_render_svp_can_drive;
+  mask_source->super.invoke_driver = art_render_svp_invoke_driver;
+  mask_source->super.prepare = art_render_svp_prepare;
+  mask_source->render = render;
+  mask_source->svp = svp;
+
+  art_render_add_mask_source (render, &mask_source->super);
+}
diff --git a/src/libart_lgpl/art_render_svp.h b/src/libart_lgpl/art_render_svp.h
new file mode 100644
index 0000000..0f0ee7a
--- /dev/null
+++ b/src/libart_lgpl/art_render_svp.h
@@ -0,0 +1,46 @@
+/*
+ * art_render_svp.h: SVP mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph acm org>
+ */
+
+#ifndef __ART_RENDER_SVP_H__
+#define __ART_RENDER_SVP_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_render.h"
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_render.h>
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_render_svp (ArtRender *render, const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_SVP_H__ */
diff --git a/src/libart_lgpl/art_rgb.c b/src/libart_lgpl/art_rgb.c
new file mode 100644
index 0000000..c007775
--- /dev/null
+++ b/src/libart_lgpl/art_rgb.c
@@ -0,0 +1,174 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb.h"
+
+#include <string.h>	/* for memset */
+
+/* Basic operators for manipulating 24-bit packed RGB buffers. */
+
+#define COLOR_RUN_COMPLEX
+
+#ifdef COLOR_RUN_SIMPLE
+/* This is really slow. Is there any way we might speed it up?
+   Two ideas:
+
+   First, maybe we should be working at 32-bit alignment. Then,
+   this can be a simple loop over word stores.
+
+   Second, we can keep working at 24-bit alignment, but have some
+   intelligence about storing. For example, we can iterate over
+   4-pixel chunks (aligned at 4 pixels), with an inner loop
+   something like:
+
+   *buf++ = v1;
+   *buf++ = v2;
+   *buf++ = v3;
+
+   One source of extra complexity is the need to make sure linebuf is
+   aligned to a 32-bit boundary.
+
+   This second alternative has some complexity to it, but is
+   appealing because it really minimizes the memory bandwidth. */
+void
+art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, gint n)
+{
+  int i;
+
+  if (r == g && g == b)
+    {
+      memset (buf, g, n + n + n);
+    }
+  else
+    {
+      for (i = 0; i < n; i++)
+	{
+	  *buf++ = r;
+	  *buf++ = g;
+	  *buf++ = b;
+	}
+    }
+}
+#endif
+
+#ifdef COLOR_RUN_COMPLEX
+/* This implements the second of the two ideas above. The test results
+   are _very_ encouraging - it seems the speed is within 10% of
+   memset, which is quite good! */
+/**
+ * art_rgb_fill_run: fill a buffer a solid RGB color.
+ * @buf: Buffer to fill.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @n: Number of RGB triples to fill.
+ *
+ * Fills a buffer with @n copies of the (@r, @g, @b) triple. Thus,
+ * locations @buf (inclusive) through @buf + 3 * @n (exclusive) are
+ * written.
+ *
+ * The implementation of this routine is very highly optimized.
+ **/
+void
+art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+  int i;
+  unsigned int v1, v2, v3;
+
+  if (r == g && g == b)
+    {
+      memset (buf, g, n + n + n);
+    }
+  else
+    {
+      if (n < 8)
+	{
+	  for (i = 0; i < n; i++)
+	    {
+	      *buf++ = r;
+	      *buf++ = g;
+	      *buf++ = b;
+	    }
+	} else {
+	  /* handle prefix up to byte alignment */
+	  /* I'm worried about this cast on sizeof(long) != sizeof(uchar *)
+	     architectures, but it _should_ work. */
+	  for (i = 0; ((unsigned long)buf) & 3; i++)
+	    {
+	      *buf++ = r;
+	      *buf++ = g;
+	      *buf++ = b;
+	    }
+#ifndef WORDS_BIGENDIAN
+	  v1 = r | (g << 8) | (b << 16) | (r << 24);
+	  v3 = (v1 << 8) | b;
+	  v2 = (v3 << 8) | g;
+#else
+	  v1 = (r << 24) | (g << 16) | (b << 8) | r;
+	  v2 = (v1 << 8) | g;
+	  v3 = (v2 << 8) | b;
+#endif
+	  for (; i < n - 3; i += 4)
+	    {
+	      ((art_u32 *)buf)[0] = v1;
+	      ((art_u32 *)buf)[1] = v2;
+	      ((art_u32 *)buf)[2] = v3;
+	      buf += 12;
+	    }
+	  /* handle postfix */
+	  for (; i < n; i++)
+	    {
+	      *buf++ = r;
+	      *buf++ = g;
+	      *buf++ = b;
+	    }
+	}
+    }
+}
+#endif
+
+/**
+ * art_rgb_run_alpha: Render semitransparent color over RGB buffer.
+ * @buf: Buffer for rendering.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @alpha: Alpha, range 0..256.
+ * @n: Number of RGB triples to render.
+ *
+ * Renders a sequential run of solid (@r, @g, @b) color over @buf with
+ * opacity @alpha.
+ **/
+void
+art_rgb_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+  int i;
+  int v;
+
+  for (i = 0; i < n; i++)
+    {
+      v = *buf;
+      *buf++ = v + (((r - v) * alpha + 0x80) >> 8);
+      v = *buf;
+      *buf++ = v + (((g - v) * alpha + 0x80) >> 8);
+      v = *buf;
+      *buf++ = v + (((b - v) * alpha + 0x80) >> 8);
+    }
+}
+
diff --git a/src/libart_lgpl/art_rgb.h b/src/libart_lgpl/art_rgb.h
new file mode 100644
index 0000000..d193f4a
--- /dev/null
+++ b/src/libart_lgpl/art_rgb.h
@@ -0,0 +1,43 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_H__
+#define __ART_RGB_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_misc.h"
+#else
+#include <libart_lgpl/art_misc.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n);
+
+void
+art_rgb_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha,
+		   int n);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rgb_a_affine.c b/src/libart_lgpl/art_rgb_a_affine.c
new file mode 100644
index 0000000..720dfdd
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_a_affine.c
@@ -0,0 +1,148 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_a_affine.h"
+
+#include <math.h>
+
+#include "art_affine.h"
+#include "art_point.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed alpha only images
+   over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+   affine transform. */
+
+/**
+ * art_rgb_a_affine: Affine transform source Alpha image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image alpha buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @rgb: RGB foreground color, in 0xRRGGBB.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the solid color rgb with alpha specified by the
+ * source image stored in @src, compositing over the area of destination
+ * image @dst specified by the rectangle (@x0, @y0) - (@x1, @y1).
+ * As usual in libart, the left and top edges of this rectangle are
+ * included, and the right and bottom edges are excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be
+ * done in a gamma-corrected color space. In the current
+ * implementation, it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_a_affine (art_u8 *dst,
+		  int x0, int y0, int x1, int y1, int dst_rowstride,
+		  const art_u8 *src,
+		  int src_width, int src_height, int src_rowstride,
+		  art_u32 rgb,
+		  const double affine[6],
+		  ArtFilterLevel level,
+		  ArtAlphaGamma *alphagamma)
+{
+  /* Note: this is a slow implementation, and is missing all filter
+     levels other than NEAREST. It is here for clarity of presentation
+     and to establish the interface. */
+  int x, y;
+  double inv[6];
+  art_u8 *dst_p, *dst_linestart;
+  const art_u8 *src_p;
+  ArtPoint pt, src_pt;
+  int src_x, src_y;
+  int alpha;
+  art_u8 bg_r, bg_g, bg_b;
+  art_u8 fg_r, fg_g, fg_b;
+  int tmp;
+  int run_x0, run_x1;
+  art_u8 r, g, b;
+
+  r = (rgb>>16)&0xff;
+  g = (rgb>>8)&0xff;
+  b = (rgb)&0xff;
+
+  dst_linestart = dst;
+  art_affine_invert (inv, affine);
+  for (y = y0; y < y1; y++)
+    {
+      pt.y = y + 0.5;
+      run_x0 = x0;
+      run_x1 = x1;
+      art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+			  inv);
+      dst_p = dst_linestart + (run_x0 - x0) * 3;
+      for (x = run_x0; x < run_x1; x++)
+	{
+	  pt.x = x + 0.5;
+	  art_affine_point (&src_pt, &pt, inv);
+	  src_x = floor (src_pt.x);
+	  src_y = floor (src_pt.y);
+	  src_p = src + (src_y * src_rowstride) + src_x;
+	  if (src_x >= 0 && src_x < src_width &&
+	      src_y >= 0 && src_y < src_height)
+	    {
+
+	  alpha = *src_p;
+	  if (alpha)
+	    {
+	      if (alpha == 255)
+		{
+		  dst_p[0] = r;
+		  dst_p[1] = g;
+		  dst_p[2] = b;
+		}
+	      else
+		{
+		  bg_r = dst_p[0];
+		  bg_g = dst_p[1];
+		  bg_b = dst_p[2];
+
+		  tmp = (r - bg_r) * alpha;
+		  fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+		  tmp = (g - bg_g) * alpha;
+		  fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+		  tmp = (b - bg_b) * alpha;
+		  fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+		  dst_p[0] = fg_r;
+		  dst_p[1] = fg_g;
+		  dst_p[2] = fg_b;
+		}
+	    }
+	    } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; }
+	  dst_p += 3;
+	}
+      dst_linestart += dst_rowstride;
+    }
+}
diff --git a/src/libart_lgpl/art_rgb_a_affine.h b/src/libart_lgpl/art_rgb_a_affine.h
new file mode 100644
index 0000000..0188634
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_a_affine.h
@@ -0,0 +1,51 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_A_AFFINE_H__
+#define __ART_RGB_A_AFFINE_H__
+
+/* This module handles compositing of affine-transformed alpha only images
+   over rgb pixel buffers. */
+
+#ifdef LIBART_COMPILATION
+#include "art_filterlevel.h"
+#include "art_alphagamma.h"
+#else
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_a_affine (art_u8 *dst,
+		  int x0, int y0, int x1, int y1, int dst_rowstride,
+		  const art_u8 *src,
+		  int src_width, int src_height, int src_rowstride,
+		  art_u32 rgb,
+		  const double affine[6],
+		  ArtFilterLevel level,
+		  ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rgb_affine.c b/src/libart_lgpl/art_rgb_affine.c
new file mode 100644
index 0000000..0cc8fdb
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_affine.c
@@ -0,0 +1,105 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed rgb images
+   over rgb pixel buffers. */
+
+/**
+ * art_rgb_affine: Affine transform source RGB image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image RGB buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges
+ * of this rectangle are included, and the right and bottom edges are
+ * excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be done
+ * in a gamma-corrected color space. Since the source image is opaque RGB,
+ * this argument only affects the edges. In the current implementation,
+ * it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride,
+		const art_u8 *src,
+		int src_width, int src_height, int src_rowstride,
+		const double affine[6],
+		ArtFilterLevel level,
+		ArtAlphaGamma *alphagamma)
+{
+  /* Note: this is a slow implementation, and is missing all filter
+     levels other than NEAREST. It is here for clarity of presentation
+     and to establish the interface. */
+  int x, y;
+  double inv[6];
+  art_u8 *dst_p, *dst_linestart;
+  const art_u8 *src_p;
+  ArtPoint pt, src_pt;
+  int src_x, src_y;
+  int run_x0, run_x1;
+
+  dst_linestart = dst;
+  art_affine_invert (inv, affine);
+  for (y = y0; y < y1; y++)
+    {
+      pt.y = y + 0.5;
+      run_x0 = x0;
+      run_x1 = x1;
+      art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+			  inv);
+      dst_p = dst_linestart + (run_x0 - x0) * 3;
+      for (x = run_x0; x < run_x1; x++)
+	{
+	  pt.x = x + 0.5;
+	  art_affine_point (&src_pt, &pt, inv);
+	  src_x = floor (src_pt.x);
+	  src_y = floor (src_pt.y);
+	  src_p = src + (src_y * src_rowstride) + src_x * 3;
+	  dst_p[0] = src_p[0];
+	  dst_p[1] = src_p[1];
+	  dst_p[2] = src_p[2];
+	  dst_p += 3;
+	}
+      dst_linestart += dst_rowstride;
+    }
+}
diff --git a/src/libart_lgpl/art_rgb_affine.h b/src/libart_lgpl/art_rgb_affine.h
new file mode 100644
index 0000000..47657ac
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_affine.h
@@ -0,0 +1,49 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_AFFINE_H__
+#define __ART_RGB_AFFINE_H__
+
+/* This module handles compositing of affine-transformed rgb images
+   over rgb pixel buffers. */
+
+#ifdef LIBART_COMPILATION
+#include "art_filterlevel.h"
+#include "art_alphagamma.h"
+#else
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_affine (art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride,
+		const art_u8 *src,
+		int src_width, int src_height, int src_rowstride,
+		const double affine[6],
+		ArtFilterLevel level,
+		ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rgb_affine_private.c b/src/libart_lgpl/art_rgb_affine_private.c
new file mode 100644
index 0000000..ff07c01
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_affine_private.c
@@ -0,0 +1,126 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_affine_private.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+
+/* Private functions for the rgb affine image compositors - primarily,
+   the determination of runs, eliminating the need for source image
+   bbox calculation in the inner loop. */
+
+/* Determine a "run", such that the inverse affine of all pixels from
+   (x0, y) inclusive to (x1, y) exclusive fit within the bounds
+   of the source image.
+
+   Initial values of x0, x1, and result values stored in first two
+   pointer arguments.
+*/
+
+#define EPSILON 1e-6
+
+void
+art_rgb_affine_run (int *p_x0, int *p_x1, int y,
+		    int src_width, int src_height,
+		    const double affine[6])
+{
+  int x0, x1;
+  double z;
+  double x_intercept;
+  int xi;
+
+  x0 = *p_x0;
+  x1 = *p_x1;
+
+  /* do left and right edges */
+  if (affine[0] > EPSILON)
+    {
+      z = affine[2] * (y + 0.5) + affine[4];
+      x_intercept = -z / affine[0];
+      xi = ceil (x_intercept + EPSILON - 0.5);
+      if (xi > x0)
+	x0 = xi;
+      x_intercept = (-z + src_width) / affine[0];
+      xi = ceil (x_intercept - EPSILON - 0.5);
+      if (xi < x1)
+	x1 = xi;
+    }
+  else if (affine[0] < -EPSILON)
+    {
+      z = affine[2] * (y + 0.5) + affine[4];
+      x_intercept = (-z + src_width) / affine[0];
+      xi = ceil (x_intercept + EPSILON - 0.5);
+      if (xi > x0)
+	x0 = xi;
+      x_intercept = -z / affine[0];
+      xi = ceil (x_intercept - EPSILON - 0.5);
+      if (xi < x1)
+	x1 = xi;
+    }
+  else
+    {
+      z = affine[2] * (y + 0.5) + affine[4];
+      if (z < 0 || z >= src_width)
+	{
+	  *p_x1 = *p_x0;
+	  return;
+	}
+    }
+
+  /* do top and bottom edges */
+  if (affine[1] > EPSILON)
+    {
+      z = affine[3] * (y + 0.5) + affine[5];
+      x_intercept = -z / affine[1];
+      xi = ceil (x_intercept + EPSILON - 0.5);
+      if (xi > x0)
+	x0 = xi;
+      x_intercept = (-z + src_height) / affine[1];
+      xi = ceil (x_intercept - EPSILON - 0.5);
+      if (xi < x1)
+	x1 = xi;
+    }
+  else if (affine[1] < -EPSILON)
+    {
+      z = affine[3] * (y + 0.5) + affine[5];
+      x_intercept = (-z + src_height) / affine[1];
+      xi = ceil (x_intercept + EPSILON - 0.5);
+      if (xi > x0)
+	x0 = xi;
+      x_intercept = -z / affine[1];
+      xi = ceil (x_intercept - EPSILON - 0.5);
+      if (xi < x1)
+	x1 = xi;
+    }
+  else
+    {
+      z = affine[3] * (y + 0.5) + affine[5];
+      if (z < 0 || z >= src_height)
+	{
+	  *p_x1 = *p_x0;
+	  return;
+	}
+    }
+
+  *p_x0 = x0;
+  *p_x1 = x1;
+}
diff --git a/src/libart_lgpl/art_rgb_affine_private.h b/src/libart_lgpl/art_rgb_affine_private.h
new file mode 100644
index 0000000..92d0cec
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_affine_private.h
@@ -0,0 +1,38 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_AFFINE_PRIVATE_H__
+#define __ART_RGB_AFFINE_PRIVATE_H__
+
+/* This module handles compositing of affine-transformed rgb images
+   over rgb pixel buffers. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_affine_run (int *p_x0, int *p_x1, int y,
+		    int src_width, int src_height,
+		    const double affine[6]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rgb_bitmap_affine.c b/src/libart_lgpl/art_rgb_bitmap_affine.c
new file mode 100644
index 0000000..c111ad3
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_bitmap_affine.c
@@ -0,0 +1,197 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_bitmap_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed bitmap images
+   over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+   affine transform. Foreground color is given and assumed to be
+   opaque, background color is assumed to be fully transparent. */
+
+static void
+art_rgb_bitmap_affine_opaque (art_u8 *dst,
+			      int x0, int y0, int x1, int y1,
+			      int dst_rowstride,
+			      const art_u8 *src,
+			      int src_width, int src_height, int src_rowstride,
+			      art_u32 rgb,
+			      const double affine[6],
+			      ArtFilterLevel level,
+			      ArtAlphaGamma *alphagamma)
+{
+  /* Note: this is a slow implementation, and is missing all filter
+     levels other than NEAREST. It is here for clarity of presentation
+     and to establish the interface. */
+  int x, y;
+  double inv[6];
+  art_u8 *dst_p, *dst_linestart;
+  const art_u8 *src_p;
+  ArtPoint pt, src_pt;
+  int src_x, src_y;
+  art_u8 r, g, b;
+  int run_x0, run_x1;
+
+  r = rgb >> 16;
+  g = (rgb >> 8) & 0xff;
+  b = rgb & 0xff;
+  dst_linestart = dst;
+  art_affine_invert (inv, affine);
+  for (y = y0; y < y1; y++)
+    {
+      pt.y = y + 0.5;
+      run_x0 = x0;
+      run_x1 = x1;
+      art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+			  inv);
+      dst_p = dst_linestart + (run_x0 - x0) * 3;
+      for (x = run_x0; x < run_x1; x++)
+	{
+	  pt.x = x + 0.5;
+	  art_affine_point (&src_pt, &pt, inv);
+	  src_x = floor (src_pt.x);
+	  src_y = floor (src_pt.y);
+	  src_p = src + (src_y * src_rowstride) + (src_x >> 3);
+	  if (*src_p & (128 >> (src_x & 7)))
+	    {
+	      dst_p[0] = r;
+	      dst_p[1] = g;
+	      dst_p[2] = b;
+	    }
+	  dst_p += 3;
+	}
+      dst_linestart += dst_rowstride;
+    }
+}
+/* Composite the source image over the destination image, applying the
+   affine transform. Foreground color is given, background color is
+   assumed to be fully transparent. */
+
+/**
+ * art_rgb_bitmap_affine: Affine transform source bitmap image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image bitmap buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @rgba: RGBA foreground color, in 0xRRGGBBAA.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1).
+ *
+ * The source bitmap stored with MSB as the leftmost pixel. Source 1
+ * bits correspond to the semitransparent color @rgba, while source 0
+ * bits are transparent.
+ *
+ * See art_rgb_affine() for a description of additional parameters.
+ **/
+void
+art_rgb_bitmap_affine (art_u8 *dst,
+		       int x0, int y0, int x1, int y1, int dst_rowstride,
+		       const art_u8 *src,
+		       int src_width, int src_height, int src_rowstride,
+		       art_u32 rgba,
+		       const double affine[6],
+		       ArtFilterLevel level,
+		       ArtAlphaGamma *alphagamma)
+{
+  /* Note: this is a slow implementation, and is missing all filter
+     levels other than NEAREST. It is here for clarity of presentation
+     and to establish the interface. */
+  int x, y;
+  double inv[6];
+  art_u8 *dst_p, *dst_linestart;
+  const art_u8 *src_p;
+  ArtPoint pt, src_pt;
+  int src_x, src_y;
+  int alpha;
+  art_u8 bg_r, bg_g, bg_b;
+  art_u8 fg_r, fg_g, fg_b;
+  art_u8 r, g, b;
+  int run_x0, run_x1;
+
+  alpha = rgba & 0xff;
+  if (alpha == 0xff)
+    {
+      art_rgb_bitmap_affine_opaque (dst, x0, y0, x1, y1, dst_rowstride,
+				    src,
+				    src_width, src_height, src_rowstride,
+				    rgba >> 8,
+				    affine,
+				    level,
+				    alphagamma);
+      return;
+    }
+  /* alpha = (65536 * alpha) / 255; */
+  alpha = (alpha << 8) + alpha + (alpha >> 7);
+  r = rgba >> 24;
+  g = (rgba >> 16) & 0xff;
+  b = (rgba >> 8) & 0xff;
+  dst_linestart = dst;
+  art_affine_invert (inv, affine);
+  for (y = y0; y < y1; y++)
+    {
+      pt.y = y + 0.5;
+      run_x0 = x0;
+      run_x1 = x1;
+      art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+			  inv);
+      dst_p = dst_linestart + (run_x0 - x0) * 3;
+      for (x = run_x0; x < run_x1; x++)
+	{
+	  pt.x = x + 0.5;
+	  art_affine_point (&src_pt, &pt, inv);
+	  src_x = floor (src_pt.x);
+	  src_y = floor (src_pt.y);
+	  src_p = src + (src_y * src_rowstride) + (src_x >> 3);
+	  if (*src_p & (128 >> (src_x & 7)))
+	    {
+	      bg_r = dst_p[0];
+	      bg_g = dst_p[1];
+	      bg_b = dst_p[2];
+
+	      fg_r = bg_r + (((r - bg_r) * alpha + 0x8000) >> 16);
+	      fg_g = bg_g + (((g - bg_g) * alpha + 0x8000) >> 16);
+	      fg_b = bg_b + (((b - bg_b) * alpha + 0x8000) >> 16);
+
+	      dst_p[0] = fg_r;
+	      dst_p[1] = fg_g;
+	      dst_p[2] = fg_b;
+	    }
+	  dst_p += 3;
+	}
+      dst_linestart += dst_rowstride;
+    }
+}
diff --git a/src/libart_lgpl/art_rgb_bitmap_affine.h b/src/libart_lgpl/art_rgb_bitmap_affine.h
new file mode 100644
index 0000000..a854fd1
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_bitmap_affine.h
@@ -0,0 +1,51 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_BITMAP_AFFINE_H__
+#define __ART_RGB_BITMAP_AFFINE_H__
+
+/* This module handles compositing of affine-transformed bitmap images
+   over rgb pixel buffers. */
+
+#ifdef LIBART_COMPILATION
+#include "art_filterlevel.h"
+#include "art_alphagamma.h"
+#else
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_bitmap_affine (art_u8 *dst,
+		       int x0, int y0, int x1, int y1, int dst_rowstride,
+		       const art_u8 *src,
+		       int src_width, int src_height, int src_rowstride,
+		       art_u32 rgba,
+		       const double affine[6],
+		       ArtFilterLevel level,
+		       ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rgb_pixbuf_affine.c b/src/libart_lgpl/art_rgb_pixbuf_affine.c
new file mode 100644
index 0000000..f759a4d
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_pixbuf_affine.c
@@ -0,0 +1,103 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_pixbuf_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_pixbuf.h"
+#include "art_rgb_affine.h"
+#include "art_rgb_affine.h"
+#include "art_rgb_rgba_affine.h"
+
+/* This module handles compositing of affine-transformed generic
+   pixbuf images over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+   affine transform. */
+/**
+ * art_rgb_pixbuf_affine: Affine transform source RGB pixbuf and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @pixbuf: source image pixbuf.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges
+ * of this rectangle are included, and the right and bottom edges are
+ * excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be
+ * done in a gamma-corrected color space. In the current
+ * implementation, it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_pixbuf_affine (art_u8 *dst,
+		       int x0, int y0, int x1, int y1, int dst_rowstride,
+		       const ArtPixBuf *pixbuf,
+		       const double affine[6],
+		       ArtFilterLevel level,
+		       ArtAlphaGamma *alphagamma)
+{
+  if (pixbuf->format != ART_PIX_RGB)
+    {
+      art_warn ("art_rgb_pixbuf_affine: need RGB format image\n");
+      return;
+    }
+
+  if (pixbuf->bits_per_sample != 8)
+    {
+      art_warn ("art_rgb_pixbuf_affine: need 8-bit sample data\n");
+      return;
+    }
+
+  if (pixbuf->n_channels != 3 + (pixbuf->has_alpha != 0))
+    {
+      art_warn ("art_rgb_pixbuf_affine: need 8-bit sample data\n");
+      return;
+    }
+
+  if (pixbuf->has_alpha)
+    art_rgb_rgba_affine (dst, x0, y0, x1, y1, dst_rowstride,
+			 pixbuf->pixels,
+			 pixbuf->width, pixbuf->height, pixbuf->rowstride,
+			 affine,
+			 level,
+			 alphagamma);
+  else
+    art_rgb_affine (dst, x0, y0, x1, y1, dst_rowstride,
+		    pixbuf->pixels,
+		    pixbuf->width, pixbuf->height, pixbuf->rowstride,
+		    affine,
+		    level,
+		    alphagamma);
+}
diff --git a/src/libart_lgpl/art_rgb_pixbuf_affine.h b/src/libart_lgpl/art_rgb_pixbuf_affine.h
new file mode 100644
index 0000000..8359938
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_pixbuf_affine.h
@@ -0,0 +1,51 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_PIXBUF_AFFINE_H__
+#define __ART_RGB_PIXBUF_AFFINE_H__
+
+/* This module handles compositing of affine-transformed generic
+   pixbuf images over rgb pixel buffers. */
+
+#ifdef LIBART_COMPILATION
+#include "art_filterlevel.h"
+#include "art_alphagamma.h"
+#include "art_pixbuf.h"
+#else
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_pixbuf.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_pixbuf_affine (art_u8 *dst,
+		       int x0, int y0, int x1, int y1, int dst_rowstride,
+		       const ArtPixBuf *pixbuf,
+		       const double affine[6],
+		       ArtFilterLevel level,
+		       ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rgb_rgba_affine.c b/src/libart_lgpl/art_rgb_rgba_affine.c
new file mode 100644
index 0000000..44f5a50
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_rgba_affine.c
@@ -0,0 +1,141 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgb_rgba_affine.h"
+
+#include <math.h>
+#include "art_misc.h"
+#include "art_point.h"
+#include "art_affine.h"
+#include "art_rgb_affine_private.h"
+
+/* This module handles compositing of affine-transformed rgba images
+   over rgb pixel buffers. */
+
+/* Composite the source image over the destination image, applying the
+   affine transform. */
+
+/**
+ * art_rgb_rgba_affine: Affine transform source RGBA image and composite.
+ * @dst: Destination image RGB buffer.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @dst_rowstride: Rowstride of @dst buffer.
+ * @src: Source image RGBA buffer.
+ * @src_width: Width of source image.
+ * @src_height: Height of source image.
+ * @src_rowstride: Rowstride of @src buffer.
+ * @affine: Affine transform.
+ * @level: Filter level.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Affine transform the source image stored in @src, compositing over
+ * the area of destination image @dst specified by the rectangle
+ * (@x0, @y0) - (@x1, @y1). As usual in libart, the left and top edges
+ * of this rectangle are included, and the right and bottom edges are
+ * excluded.
+ *
+ * The @alphagamma parameter specifies that the alpha compositing be
+ * done in a gamma-corrected color space. In the current
+ * implementation, it is ignored.
+ *
+ * The @level parameter specifies the speed/quality tradeoff of the
+ * image interpolation. Currently, only ART_FILTER_NEAREST is
+ * implemented.
+ **/
+void
+art_rgb_rgba_affine (art_u8 *dst,
+		     int x0, int y0, int x1, int y1, int dst_rowstride,
+		     const art_u8 *src,
+		     int src_width, int src_height, int src_rowstride,
+		     const double affine[6],
+		     ArtFilterLevel level,
+		     ArtAlphaGamma *alphagamma)
+{
+  /* Note: this is a slow implementation, and is missing all filter
+     levels other than NEAREST. It is here for clarity of presentation
+     and to establish the interface. */
+  int x, y;
+  double inv[6];
+  art_u8 *dst_p, *dst_linestart;
+  const art_u8 *src_p;
+  ArtPoint pt, src_pt;
+  int src_x, src_y;
+  int alpha;
+  art_u8 bg_r, bg_g, bg_b;
+  art_u8 fg_r, fg_g, fg_b;
+  int tmp;
+  int run_x0, run_x1;
+
+  dst_linestart = dst;
+  art_affine_invert (inv, affine);
+  for (y = y0; y < y1; y++)
+    {
+      pt.y = y + 0.5;
+      run_x0 = x0;
+      run_x1 = x1;
+      art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height,
+			  inv);
+      dst_p = dst_linestart + (run_x0 - x0) * 3;
+      for (x = run_x0; x < run_x1; x++)
+	{
+	  pt.x = x + 0.5;
+	  art_affine_point (&src_pt, &pt, inv);
+	  src_x = floor (src_pt.x);
+	  src_y = floor (src_pt.y);
+	  src_p = src + (src_y * src_rowstride) + src_x * 4;
+	  if (src_x >= 0 && src_x < src_width &&
+	      src_y >= 0 && src_y < src_height)
+	    {
+
+	  alpha = src_p[3];
+	  if (alpha)
+	    {
+	      if (alpha == 255)
+		{
+		  dst_p[0] = src_p[0];
+		  dst_p[1] = src_p[1];
+		  dst_p[2] = src_p[2];
+		}
+	      else
+		{
+		  bg_r = dst_p[0];
+		  bg_g = dst_p[1];
+		  bg_b = dst_p[2];
+
+		  tmp = (src_p[0] - bg_r) * alpha;
+		  fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+		  tmp = (src_p[1] - bg_g) * alpha;
+		  fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+		  tmp = (src_p[2] - bg_b) * alpha;
+		  fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+		  dst_p[0] = fg_r;
+		  dst_p[1] = fg_g;
+		  dst_p[2] = fg_b;
+		}
+	    }
+	    } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; }
+	  dst_p += 3;
+	}
+      dst_linestart += dst_rowstride;
+    }
+}
diff --git a/src/libart_lgpl/art_rgb_rgba_affine.h b/src/libart_lgpl/art_rgb_rgba_affine.h
new file mode 100644
index 0000000..598c797
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_rgba_affine.h
@@ -0,0 +1,50 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_RGBA_AFFINE_H__
+#define __ART_RGB_RGBA_AFFINE_H__
+
+/* This module handles compositing of affine-transformed rgba images
+   over rgb pixel buffers. */
+
+#ifdef LIBART_COMPILATION
+#include "art_filterlevel.h"
+#include "art_alphagamma.h"
+#else
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_alphagamma.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+art_rgb_rgba_affine (art_u8 *dst,
+		     int x0, int y0, int x1, int y1, int dst_rowstride,
+		     const art_u8 *src,
+		     int src_width, int src_height, int src_rowstride,
+		     const double affine[6],
+		     ArtFilterLevel level,
+		     ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/libart_lgpl/art_rgb_svp.c b/src/libart_lgpl/art_rgb_svp.c
new file mode 100644
index 0000000..0964342
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_svp.c
@@ -0,0 +1,456 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Render a sorted vector path into an RGB buffer. */
+
+#include "config.h"
+#include "art_rgb_svp.h"
+
+#include "art_svp.h"
+#include "art_svp_render_aa.h"
+#include "art_rgb.h"
+
+typedef struct _ArtRgbSVPData ArtRgbSVPData;
+typedef struct _ArtRgbSVPAlphaData ArtRgbSVPAlphaData;
+
+struct _ArtRgbSVPData {
+  art_u32 rgbtab[256];
+  art_u8 *buf;
+  int rowstride;
+  int x0, x1;
+};
+
+struct _ArtRgbSVPAlphaData {
+  int alphatab[256];
+  art_u8 r, g, b, alpha;
+  art_u8 *buf;
+  int rowstride;
+  int x0, x1;
+};
+
+static void
+art_rgb_svp_callback (void *callback_data, int y,
+		      int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtRgbSVPData *data = (ArtRgbSVPData *)callback_data;
+  art_u8 *linebuf;
+  int run_x0, run_x1;
+  art_u32 running_sum = start;
+  art_u32 rgb;
+  int x0, x1;
+  int k;
+
+  linebuf = data->buf;
+  x0 = data->x0;
+  x1 = data->x1;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > x0)
+	{
+	  rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+	  art_rgb_fill_run (linebuf,
+			    rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+			    run_x1 - x0);
+	}
+
+      for (k = 0; k < n_steps - 1; k++)
+	{
+	  running_sum += steps[k].delta;
+	  run_x0 = run_x1;
+	  run_x1 = steps[k + 1].x;
+	  if (run_x1 > run_x0)
+	    {
+	      rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+	      art_rgb_fill_run (linebuf + (run_x0 - x0) * 3,
+				rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+				run_x1 - run_x0);
+	    }
+	}
+      running_sum += steps[k].delta;
+      if (x1 > run_x1)
+	{
+	  rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+	  art_rgb_fill_run (linebuf + (run_x1 - x0) * 3,
+			    rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+			    x1 - run_x1);
+	}
+    }
+  else
+    {
+      rgb = data->rgbtab[(running_sum >> 16) & 0xff];
+      art_rgb_fill_run (linebuf,
+			rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+			x1 - x0);
+    }
+
+  data->buf += data->rowstride;
+}
+
+/* Render the vector path into the RGB buffer. */
+
+/**
+ * art_rgb_svp_aa: Render sorted vector path into RGB buffer.
+ * @svp: The source sorted vector path.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @fg_color: Foreground color in 0xRRGGBB format.
+ * @bg_color: Background color in 0xRRGGBB format.
+ * @buf: Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the rendering.
+ *
+ * Renders the shape specified with @svp into the @buf RGB buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @fg_color and @bg_color arguments specify the opaque colors to
+ * be used for rendering. For pixels of entirely 0 winding-number,
+ * @bg_color is used. For pixels of entirely 1 winding number,
+ * @fg_color is used. In between, the color is interpolated based on
+ * the fraction of the pixel with a winding number of 1. If
+ * @alphagamma is NULL, then linear interpolation (in pixel counts) is
+ * the default. Otherwise, the interpolation is as specified by
+ * @alphagamma.
+ **/
+void
+art_rgb_svp_aa (const ArtSVP *svp,
+		int x0, int y0, int x1, int y1,
+		art_u32 fg_color, art_u32 bg_color,
+		art_u8 *buf, int rowstride,
+		ArtAlphaGamma *alphagamma)
+{
+  ArtRgbSVPData data;
+
+  int r_fg, g_fg, b_fg;
+  int r_bg, g_bg, b_bg;
+  int r, g, b;
+  int dr, dg, db;
+  int i;
+
+  if (alphagamma == NULL)
+    {
+      r_fg = fg_color >> 16;
+      g_fg = (fg_color >> 8) & 0xff;
+      b_fg = fg_color & 0xff;
+
+      r_bg = bg_color >> 16;
+      g_bg = (bg_color >> 8) & 0xff;
+      b_bg = bg_color & 0xff;
+
+      r = (r_bg << 16) + 0x8000;
+      g = (g_bg << 16) + 0x8000;
+      b = (b_bg << 16) + 0x8000;
+      dr = ((r_fg - r_bg) << 16) / 255;
+      dg = ((g_fg - g_bg) << 16) / 255;
+      db = ((b_fg - b_bg) << 16) / 255;
+
+      for (i = 0; i < 256; i++)
+	{
+	  data.rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
+	  r += dr;
+	  g += dg;
+	  b += db;
+	}
+    }
+  else
+    {
+      int *table;
+      art_u8 *invtab;
+
+      table = alphagamma->table;
+
+      r_fg = table[fg_color >> 16];
+      g_fg = table[(fg_color >> 8) & 0xff];
+      b_fg = table[fg_color & 0xff];
+
+      r_bg = table[bg_color >> 16];
+      g_bg = table[(bg_color >> 8) & 0xff];
+      b_bg = table[bg_color & 0xff];
+
+      r = (r_bg << 16) + 0x8000;
+      g = (g_bg << 16) + 0x8000;
+      b = (b_bg << 16) + 0x8000;
+      dr = ((r_fg - r_bg) << 16) / 255;
+      dg = ((g_fg - g_bg) << 16) / 255;
+      db = ((b_fg - b_bg) << 16) / 255;
+
+      invtab = alphagamma->invtable;
+      for (i = 0; i < 256; i++)
+	{
+	  data.rgbtab[i] = (invtab[r >> 16] << 16) |
+	    (invtab[g >> 16] << 8) |
+	    invtab[b >> 16];
+	  r += dr;
+	  g += dg;
+	  b += db;
+	}
+    }
+  data.buf = buf;
+  data.rowstride = rowstride;
+  data.x0 = x0;
+  data.x1 = x1;
+  art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_callback, &data);
+}
+
+static void
+art_rgb_svp_alpha_callback (void *callback_data, int y,
+			    int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+  art_u8 *linebuf;
+  int run_x0, run_x1;
+  art_u32 running_sum = start;
+  int x0, x1;
+  int k;
+  art_u8 r, g, b;
+  int *alphatab;
+  int alpha;
+
+  linebuf = data->buf;
+  x0 = data->x0;
+  x1 = data->x1;
+
+  r = data->r;
+  g = data->g;
+  b = data->b;
+  alphatab = data->alphatab;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > x0)
+	{
+	  alpha = (running_sum >> 16) & 0xff;
+	  if (alpha)
+	    art_rgb_run_alpha (linebuf,
+			       r, g, b, alphatab[alpha],
+			       run_x1 - x0);
+	}
+
+      for (k = 0; k < n_steps - 1; k++)
+	{
+	  running_sum += steps[k].delta;
+	  run_x0 = run_x1;
+	  run_x1 = steps[k + 1].x;
+	  if (run_x1 > run_x0)
+	    {
+	      alpha = (running_sum >> 16) & 0xff;
+	      if (alpha)
+		art_rgb_run_alpha (linebuf + (run_x0 - x0) * 3,
+				   r, g, b, alphatab[alpha],
+				   run_x1 - run_x0);
+	    }
+	}
+      running_sum += steps[k].delta;
+      if (x1 > run_x1)
+	{
+	  alpha = (running_sum >> 16) & 0xff;
+	  if (alpha)
+	    art_rgb_run_alpha (linebuf + (run_x1 - x0) * 3,
+			       r, g, b, alphatab[alpha],
+			       x1 - run_x1);
+	}
+    }
+  else
+    {
+      alpha = (running_sum >> 16) & 0xff;
+      if (alpha)
+	art_rgb_run_alpha (linebuf,
+			   r, g, b, alphatab[alpha],
+			   x1 - x0);
+    }
+
+  data->buf += data->rowstride;
+}
+
+static void
+art_rgb_svp_alpha_opaque_callback (void *callback_data, int y,
+				   int start,
+				   ArtSVPRenderAAStep *steps, int n_steps)
+{
+  ArtRgbSVPAlphaData *data = (ArtRgbSVPAlphaData *)callback_data;
+  art_u8 *linebuf;
+  int run_x0, run_x1;
+  art_u32 running_sum = start;
+  int x0, x1;
+  int k;
+  art_u8 r, g, b;
+  int *alphatab;
+  int alpha;
+
+  linebuf = data->buf;
+  x0 = data->x0;
+  x1 = data->x1;
+
+  r = data->r;
+  g = data->g;
+  b = data->b;
+  alphatab = data->alphatab;
+
+  if (n_steps > 0)
+    {
+      run_x1 = steps[0].x;
+      if (run_x1 > x0)
+	{
+	  alpha = running_sum >> 16;
+	  if (alpha)
+	    {
+	      if (alpha >= 255)
+		art_rgb_fill_run (linebuf,
+				  r, g, b,
+				  run_x1 - x0);
+	      else
+		art_rgb_run_alpha (linebuf,
+				   r, g, b, alphatab[alpha],
+				   run_x1 - x0);
+	    }
+	}
+
+      for (k = 0; k < n_steps - 1; k++)
+	{
+	  running_sum += steps[k].delta;
+	  run_x0 = run_x1;
+	  run_x1 = steps[k + 1].x;
+	  if (run_x1 > run_x0)
+	    {
+	      alpha = running_sum >> 16;
+	      if (alpha)
+		{
+		  if (alpha >= 255)
+		    art_rgb_fill_run (linebuf + (run_x0 - x0) * 3,
+				      r, g, b,
+				      run_x1 - run_x0);
+		  else
+		    art_rgb_run_alpha (linebuf + (run_x0 - x0) * 3,
+				       r, g, b, alphatab[alpha],
+				       run_x1 - run_x0);
+		}
+	    }
+	}
+      running_sum += steps[k].delta;
+      if (x1 > run_x1)
+	{
+	  alpha = running_sum >> 16;
+	  if (alpha)
+	    {
+	      if (alpha >= 255)
+		art_rgb_fill_run (linebuf + (run_x1 - x0) * 3,
+				  r, g, b,
+				  x1 - run_x1);
+	      else
+		art_rgb_run_alpha (linebuf + (run_x1 - x0) * 3,
+				   r, g, b, alphatab[alpha],
+				   x1 - run_x1);
+	    }
+	}
+    }
+  else
+    {
+      alpha = running_sum >> 16;
+      if (alpha)
+	{
+	  if (alpha >= 255)
+	    art_rgb_fill_run (linebuf,
+			      r, g, b,
+			      x1 - x0);
+	  else
+	    art_rgb_run_alpha (linebuf,
+			       r, g, b, alphatab[alpha],
+			       x1 - x0);
+	}
+    }
+
+  data->buf += data->rowstride;
+}
+
+/**
+ * art_rgb_svp_alpha: Alpha-composite sorted vector path over RGB buffer.
+ * @svp: The source sorted vector path.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @rgba: Color in 0xRRGGBBAA format.
+ * @buf: Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Renders the shape specified with @svp over the @buf RGB buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @rgba argument specifies the color for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the color @rgba composited over them (ie,
+ * are replaced by the red, green, blue components of @rgba if the alpha
+ * component is 0xff). Pixels of intermediate coverage are interpolated
+ * according to the rule in @alphagamma, or default to linear if
+ * @alphagamma is NULL.
+ **/
+void
+art_rgb_svp_alpha (const ArtSVP *svp,
+		   int x0, int y0, int x1, int y1,
+		   art_u32 rgba,
+		   art_u8 *buf, int rowstride,
+		   ArtAlphaGamma *alphagamma)
+{
+  ArtRgbSVPAlphaData data;
+  int r, g, b, alpha;
+  int i;
+  int a, da;
+
+  r = rgba >> 24;
+  g = (rgba >> 16) & 0xff;
+  b = (rgba >> 8) & 0xff;
+  alpha = rgba & 0xff;
+
+  data.r = r;
+  data.g = g;
+  data.b = b;
+  data.alpha = alpha;
+
+  a = 0x8000;
+  da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+  for (i = 0; i < 256; i++)
+    {
+      data.alphatab[i] = a >> 16;
+      a += da;
+    }
+
+  data.buf = buf;
+  data.rowstride = rowstride;
+  data.x0 = x0;
+  data.x1 = x1;
+  if (alpha == 255)
+    art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_opaque_callback,
+		       &data);
+  else
+    art_svp_render_aa (svp, x0, y0, x1, y1, art_rgb_svp_alpha_callback, &data);
+}
+
diff --git a/src/libart_lgpl/art_rgb_svp.h b/src/libart_lgpl/art_rgb_svp.h
new file mode 100644
index 0000000..6353840
--- /dev/null
+++ b/src/libart_lgpl/art_rgb_svp.h
@@ -0,0 +1,54 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGB_SVP_H__
+#define __ART_RGB_SVP_H__
+
+/* Render a sorted vector path into an RGB buffer. */
+
+#ifdef LIBART_COMPILATION
+#include "art_alphagamma.h"
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_rgb_svp_aa (const ArtSVP *svp,
+		int x0, int y0, int x1, int y1,
+		art_u32 fg_color, art_u32 bg_color,
+		art_u8 *buf, int rowstride,
+		ArtAlphaGamma *alphagamma);
+
+void
+art_rgb_svp_alpha (const ArtSVP *svp,
+		   int x0, int y0, int x1, int y1,
+		   art_u32 rgba,
+		   art_u8 *buf, int rowstride,
+		   ArtAlphaGamma *alphagamma);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RGB_SVP_H__ */
diff --git a/src/libart_lgpl/art_rgba.c b/src/libart_lgpl/art_rgba.c
new file mode 100644
index 0000000..f5fd234
--- /dev/null
+++ b/src/libart_lgpl/art_rgba.c
@@ -0,0 +1,257 @@
+/*
+ * art_rgba.c: Functions for manipulating RGBA pixel data.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_rgba.h"
+
+#define ART_OPTIMIZE_SPACE
+
+#ifndef ART_OPTIMIZE_SPACE
+#include "art_rgba_table.c"
+#endif
+
+/**
+ * art_rgba_rgba_composite: Composite RGBA image over RGBA buffer.
+ * @dst: Destination RGBA buffer.
+ * @src: Source RGBA buffer.
+ * @n: Number of RGBA pixels to composite.
+ *
+ * Composites the RGBA pixels in @dst over the @src buffer.
+ **/
+void
+art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n)
+{
+  int i;
+#ifdef WORDS_BIGENDIAN
+  art_u32 src_rgba, dst_rgba;
+#else
+  art_u32 src_abgr, dst_abgr;
+#endif
+  art_u8 src_alpha, dst_alpha;
+
+  for (i = 0; i < n; i++)
+    {
+#ifdef WORDS_BIGENDIAN
+      src_rgba = ((art_u32 *)src)[i];
+      src_alpha = src_rgba & 0xff;
+#else
+      src_abgr = ((art_u32 *)src)[i];
+      src_alpha = (src_abgr >> 24) & 0xff;
+#endif
+      if (src_alpha)
+	{
+	  if (src_alpha == 0xff ||
+	      (
+#ifdef WORDS_BIGENDIAN
+	       dst_rgba = ((art_u32 *)dst)[i],
+	       dst_alpha = dst_rgba & 0xff,
+#else
+	       dst_abgr = ((art_u32 *)dst)[i],
+	       dst_alpha = (dst_abgr >> 24),
+#endif
+	       dst_alpha == 0))
+#ifdef WORDS_BIGENDIAN
+	    ((art_u32 *)dst)[i] = src_rgba;
+#else
+	    ((art_u32 *)dst)[i] = src_abgr;
+#endif
+	  else
+	    {
+	      int r, g, b, a;
+	      int src_r, src_g, src_b;
+	      int dst_r, dst_g, dst_b;
+	      int tmp;
+	      int c;
+
+#ifdef ART_OPTIMIZE_SPACE
+	      tmp = (255 - src_alpha) * (255 - dst_alpha) + 0x80;
+	      a = 255 - ((tmp + (tmp >> 8)) >> 8);
+	      c = ((src_alpha << 16) + (a >> 1)) / a;
+#else
+	      tmp = art_rgba_composite_table[(src_alpha << 8) + dst_alpha];
+	      c = tmp & 0x1ffff;
+	      a = tmp >> 24;
+#endif
+#ifdef WORDS_BIGENDIAN
+	      src_r = (src_rgba >> 24) & 0xff;
+	      src_g = (src_rgba >> 16) & 0xff;
+	      src_b = (src_rgba >> 8) & 0xff;
+	      dst_r = (dst_rgba >> 24) & 0xff;
+	      dst_g = (dst_rgba >> 16) & 0xff;
+	      dst_b = (dst_rgba >> 8) & 0xff;
+#else
+	      src_r = src_abgr & 0xff;
+	      src_g = (src_abgr >> 8) & 0xff;
+	      src_b = (src_abgr >> 16) & 0xff;
+	      dst_r = dst_abgr & 0xff;
+	      dst_g = (dst_abgr >> 8) & 0xff;
+	      dst_b = (dst_abgr >> 16) & 0xff;
+#endif
+	      r = dst_r + (((src_r - dst_r) * c + 0x8000) >> 16);
+	      g = dst_g + (((src_g - dst_g) * c + 0x8000) >> 16);
+	      b = dst_b + (((src_b - dst_b) * c + 0x8000) >> 16);
+#ifdef WORDS_BIGENDIAN
+	    ((art_u32 *)dst)[i] = (r << 24) | (g << 16) | (b << 8) | a;
+#else
+	    ((art_u32 *)dst)[i] = (a << 24) | (b << 16) | (g << 8) | r;
+#endif
+	    }
+	}
+#if 0
+      /* it's not clear to me this optimization really wins */
+      else
+	{
+	  /* skip over run of transparent pixels */
+	  for (; i < n - 1; i++)
+	    {
+#ifdef WORDS_BIGENDIAN
+	      src_rgba = ((art_u32 *)src)[i + 1];
+	      if (src_rgba & 0xff)
+		break;
+#else
+	      src_abgr = ((art_u32 *)src)[i + 1];
+	      if (src_abgr & 0xff000000)
+		break;
+#endif
+	    }
+	}
+#endif
+    }
+}
+
+/**
+ * art_rgba_fill_run: fill an RGBA buffer a solid RGB color.
+ * @buf: Buffer to fill.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @n: Number of RGB triples to fill.
+ *
+ * Fills a buffer with @n copies of the (@r, @g, @b) triple, solid
+ * alpha. Thus, locations @buf (inclusive) through @buf + 4 * @n
+ * (exclusive) are written.
+ **/
+void
+art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+  int i;
+#ifdef WORDS_BIGENDIAN
+  art_u32 src_rgba;
+#else
+  art_u32 src_abgr;
+#endif
+
+#ifdef WORDS_BIGENDIAN
+  src_rgba = (r << 24) | (g << 16) | (b << 8) | 255;
+#else
+  src_abgr = (255 << 24) | (b << 16) | (g << 8) | r;
+#endif
+  for (i = 0; i < n; i++)
+    {
+#ifdef WORDS_BIGENDIAN
+      ((art_u32 *)buf)[i] = src_rgba;
+#else
+      ((art_u32 *)buf)[i] = src_abgr;
+#endif
+    }
+}
+
+/**
+ * art_rgba_run_alpha: Render semitransparent color over RGBA buffer.
+ * @buf: Buffer for rendering.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @alpha: Alpha, range 0..255.
+ * @n: Number of RGB triples to render.
+ *
+ * Renders a sequential run of solid (@r, @g, @b) color over @buf with
+ * opacity @alpha. Note that the range of @alpha is 0..255, in contrast
+ * to art_rgb_run_alpha, which has a range of 0..256.
+ **/
+void
+art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+  int i;
+#ifdef WORDS_BIGENDIAN
+  art_u32 src_rgba, dst_rgba;
+#else
+  art_u32 src_abgr, dst_abgr;
+#endif
+  art_u8 dst_alpha;
+  int a;
+  int dst_r, dst_g, dst_b;
+  int tmp;
+  int c;
+
+#ifdef WORDS_BIGENDIAN
+  src_rgba = (r << 24) | (g << 16) | (b << 8) | alpha;
+#else
+  src_abgr = (alpha << 24) | (b << 16) | (g << 8) | r;
+#endif
+  for (i = 0; i < n; i++)
+    {
+#ifdef WORDS_BIGENDIAN
+      dst_rgba = ((art_u32 *)buf)[i];
+      dst_alpha = dst_rgba & 0xff;
+#else
+      dst_abgr = ((art_u32 *)buf)[i];
+      dst_alpha = (dst_abgr >> 24) & 0xff;
+#endif
+      if (dst_alpha)
+	{
+#ifdef ART_OPTIMIZE_SPACE
+	  tmp = (255 - alpha) * (255 - dst_alpha) + 0x80;
+	  a = 255 - ((tmp + (tmp >> 8)) >> 8);
+	  c = ((alpha << 16) + (a >> 1)) / a;
+#else
+	  tmp = art_rgba_composite_table[(alpha << 8) + dst_alpha];
+	  c = tmp & 0x1ffff;
+	  a = tmp >> 24;
+#endif
+#ifdef WORDS_BIGENDIAN
+	  dst_r = (dst_rgba >> 24) & 0xff;
+	  dst_g = (dst_rgba >> 16) & 0xff;
+	  dst_b = (dst_rgba >> 8) & 0xff;
+#else
+	  dst_r = dst_abgr & 0xff;
+	  dst_g = (dst_abgr >> 8) & 0xff;
+	  dst_b = (dst_abgr >> 16) & 0xff;
+#endif
+	  dst_r += (((r - dst_r) * c + 0x8000) >> 16);
+	  dst_g += (((g - dst_g) * c + 0x8000) >> 16);
+	  dst_b += (((b - dst_b) * c + 0x8000) >> 16);
+#ifdef WORDS_BIGENDIAN
+	  ((art_u32 *)buf)[i] = (dst_r << 24) | (dst_g << 16) | (dst_b << 8) | a;
+#else
+	  ((art_u32 *)buf)[i] = (a << 24) | (dst_b << 16) | (dst_g << 8) | dst_r;
+#endif
+	}
+      else
+	{
+#ifdef WORDS_BIGENDIAN
+	  ((art_u32 *)buf)[i] = src_rgba;
+#else
+	  ((art_u32 *)buf)[i] = src_abgr;
+#endif
+	}
+    }
+}
diff --git a/src/libart_lgpl/art_rgba.h b/src/libart_lgpl/art_rgba.h
new file mode 100644
index 0000000..189b991
--- /dev/null
+++ b/src/libart_lgpl/art_rgba.h
@@ -0,0 +1,48 @@
+/*
+ * art_rgba.h: Functions for manipulating RGBA pixel data.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RGBA_H__
+#define __ART_RGBA_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_misc.h"
+#else
+#include <libart_lgpl/art_misc.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n);
+
+void
+art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n);
+
+void
+art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/src/libart_lgpl/art_svp.c b/src/libart_lgpl/art_svp.c
new file mode 100644
index 0000000..9ab3f87
--- /dev/null
+++ b/src/libart_lgpl/art_svp.c
@@ -0,0 +1,151 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for sorted vector paths */
+
+#include "config.h"
+#include "art_svp.h"
+
+#include "art_misc.h"
+
+/* Add a new segment. The arguments can be zero and NULL if the caller
+   would rather fill them in later.
+
+   We also realloc one auxiliary array of ints of size n_segs if
+   desired.
+*/
+/**
+ * art_svp_add_segment: Add a segment to an #ArtSVP structure.
+ * @p_vp: Pointer to where the #ArtSVP structure is stored.
+ * @pn_segs_max: Pointer to the allocated size of * p_vp 
+ * @pn_points_max: Pointer to where auxiliary array is stored.
+ * @n_points: Number of points for new segment.
+ * @dir: Direction for new segment; 0 is up, 1 is down.
+ * @points: Points for new segment.
+ * @bbox: Bounding box for new segment.
+ *
+ * Adds a new segment to an ArtSVP structure. This routine reallocates
+ * the structure if necessary, updating * p_vp and * pn_segs_max as
+ * necessary.
+ *
+ * The new segment is simply added after all other segments. Thus,
+ * this routine should be called in order consistent with the #ArtSVP
+ * sorting rules.
+ *
+ * If the @bbox argument is given, it is simply stored in the new
+ * segment. Otherwise (if it is NULL), the bounding box is computed
+ * from the @points given.
+ **/
+int
+art_svp_add_segment (ArtSVP **p_vp, int *pn_segs_max,
+		     int **pn_points_max,
+		     int n_points, int dir, ArtPoint *points,
+		     ArtDRect *bbox)
+{
+  int seg_num;
+  ArtSVP *svp;
+  ArtSVPSeg *seg;
+
+  svp = *p_vp;
+  seg_num = svp->n_segs++;
+  if (*pn_segs_max == seg_num)
+    {
+      *pn_segs_max <<= 1;
+      svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+				   (*pn_segs_max - 1) * sizeof(ArtSVPSeg));
+      *p_vp = svp;
+      if (pn_points_max != NULL)
+	*pn_points_max = art_renew (*pn_points_max, int, *pn_segs_max);
+    }
+  seg = &svp->segs[seg_num];
+  seg->n_points = n_points;
+  seg->dir = dir;
+  seg->points = points;
+  if (bbox)
+    seg->bbox = *bbox;
+  else if (points)
+    {
+      double x_min, x_max;
+      int i;
+
+      x_min = x_max = points[0].x;
+      for (i = 1; i < n_points; i++)
+	{
+	  if (x_min > points[i].x)
+	    x_min = points[i].x;
+	  if (x_max < points[i].x)
+	    x_max = points[i].x;
+	}
+      seg->bbox.x0 = x_min;
+      seg->bbox.y0 = points[0].y;
+
+      seg->bbox.x1 = x_max;
+      seg->bbox.y1 = points[n_points - 1].y;
+    }
+  return seg_num;
+}
+
+
+/**
+ * art_svp_free: Free an #ArtSVP structure.
+ * @svp: #ArtSVP to free.
+ *
+ * Frees an #ArtSVP structure and all the segments in it.
+ **/
+void
+art_svp_free (ArtSVP *svp)
+{
+  int n_segs = svp->n_segs;
+  int i;
+
+  for (i = 0; i < n_segs; i++)
+    art_free (svp->segs[i].points);
+  art_free (svp);
+}
+
+#ifdef ART_USE_NEW_INTERSECTOR
+#define EPSILON 0
+#else
+#define EPSILON 1e-6
+#endif
+
+/**
+ * art_svp_seg_compare: Compare two segments of an svp.
+ * @seg1: First segment to compare.
+ * @seg2: Second segment to compare.
+ *
+ * Compares two segments of an svp. Return 1 if @seg2 is below or to the
+ * right of @seg1, -1 otherwise.
+ **/
+int
+art_svp_seg_compare (const void *s1, const void *s2)
+{
+  const ArtSVPSeg *seg1 = s1;
+  const ArtSVPSeg *seg2 = s2;
+
+  if (seg1->points[0].y - EPSILON > seg2->points[0].y) return 1;
+  else if (seg1->points[0].y + EPSILON < seg2->points[0].y) return -1;
+  else if (seg1->points[0].x - EPSILON > seg2->points[0].x) return 1;
+  else if (seg1->points[0].x + EPSILON < seg2->points[0].x) return -1;
+  else if ((seg1->points[1].x - seg1->points[0].x) *
+	   (seg2->points[1].y - seg2->points[0].y) -
+	   (seg1->points[1].y - seg1->points[0].y) *
+	   (seg2->points[1].x - seg2->points[0].x) > 0) return 1;
+  else return -1;
+}
+
diff --git a/src/libart_lgpl/art_svp.h b/src/libart_lgpl/art_svp.h
new file mode 100644
index 0000000..6aab7ae
--- /dev/null
+++ b/src/libart_lgpl/art_svp.h
@@ -0,0 +1,67 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_H__
+#define __ART_SVP_H__
+
+/* Basic data structures and constructors for sorted vector paths */
+
+#ifdef LIBART_COMPILATION
+#include "art_rect.h"
+#include "art_point.h"
+#else
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_point.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtSVP ArtSVP;
+typedef struct _ArtSVPSeg ArtSVPSeg;
+
+struct _ArtSVPSeg {
+  int n_points;
+  int dir; /* == 0 for "up", 1 for "down" */
+  ArtDRect bbox;
+  ArtPoint *points;
+};
+
+struct _ArtSVP {
+  int n_segs;
+  ArtSVPSeg segs[1];
+};
+
+int
+art_svp_add_segment (ArtSVP **p_vp, int *pn_segs_max,
+		     int **pn_points_max,
+		     int n_points, int dir, ArtPoint *points,
+		     ArtDRect *bbox);
+
+void
+art_svp_free (ArtSVP *svp);
+
+int
+art_svp_seg_compare (const void *s1, const void *s2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_H__ */
diff --git a/src/libart_lgpl/art_svp_intersect.c b/src/libart_lgpl/art_svp_intersect.c
new file mode 100644
index 0000000..edca44b
--- /dev/null
+++ b/src/libart_lgpl/art_svp_intersect.c
@@ -0,0 +1,1802 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2001 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* This file contains a testbed implementation of the new intersection
+   code.
+*/
+
+#include "config.h"
+#include "art_svp_intersect.h"
+
+#include <math.h> /* for sqrt */
+
+/* Sanitychecking verifies the main invariant on every priority queue
+   point. Do not use in production, as it slows things down way too
+   much. */
+#define noSANITYCHECK
+
+/* This can be used in production, to prevent hangs. Eventually, it
+   should not be necessary. */
+#define CHEAP_SANITYCHECK
+
+#define noVERBOSE
+
+#include "art_misc.h"
+
+/* A priority queue - perhaps move to a separate file if it becomes
+   needed somewhere else */
+
+#define ART_PRIQ_USE_HEAP
+
+typedef struct _ArtPriQ ArtPriQ;
+typedef struct _ArtPriPoint ArtPriPoint;
+
+struct _ArtPriQ {
+  int n_items;
+  int n_items_max;
+  ArtPriPoint **items;
+};
+
+struct _ArtPriPoint {
+  double x;
+  double y;
+  void *user_data;
+};
+
+static ArtPriQ *
+art_pri_new (void)
+{
+  ArtPriQ *result = art_new (ArtPriQ, 1);
+
+  result->n_items = 0;
+  result->n_items_max = 16;
+  result->items = art_new (ArtPriPoint *, result->n_items_max);
+  return result;
+}
+
+static void
+art_pri_free (ArtPriQ *pq)
+{
+  art_free (pq->items);
+  art_free (pq);
+}
+
+static art_boolean
+art_pri_empty (ArtPriQ *pq)
+{
+  return pq->n_items == 0;
+}
+
+#ifdef ART_PRIQ_USE_HEAP
+
+/* This heap implementation is based on Vasek Chvatal's course notes:
+   http://www.cs.rutgers.edu/~chvatal/notes/pq.html#heap */
+
+static void
+art_pri_bubble_up (ArtPriQ *pq, int vacant, ArtPriPoint *missing)
+{
+  ArtPriPoint **items = pq->items;
+  int parent;
+
+  parent = (vacant - 1) >> 1;
+  while (vacant > 0 && (missing->y < items[parent]->y ||
+			(missing->y == items[parent]->y &&
+			 missing->x < items[parent]->x)))
+    {
+      items[vacant] = items[parent];
+      vacant = parent;
+      parent = (vacant - 1) >> 1;
+    }
+
+  items[vacant] = missing;
+}
+
+static void
+art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
+{
+  if (pq->n_items == pq->n_items_max)
+    art_expand (pq->items, ArtPriPoint *, pq->n_items_max);
+
+  art_pri_bubble_up (pq, pq->n_items++, point);
+}
+
+static void
+art_pri_sift_down_from_root (ArtPriQ *pq, ArtPriPoint *missing)
+{
+  ArtPriPoint **items = pq->items;
+  int vacant = 0, child = 2;
+  int n = pq->n_items;
+
+  while (child < n)
+    {
+      if (items[child - 1]->y < items[child]->y ||
+	  (items[child - 1]->y == items[child]->y &&
+	   items[child - 1]->x < items[child]->x))
+	child--;
+      items[vacant] = items[child];
+      vacant = child;
+      child = (vacant + 1) << 1;
+    }
+  if (child == n)
+    {
+      items[vacant] = items[n - 1];
+      vacant = n - 1;
+    }
+
+  art_pri_bubble_up (pq, vacant, missing);
+}
+
+static ArtPriPoint *
+art_pri_choose (ArtPriQ *pq)
+{
+  ArtPriPoint *result = pq->items[0];
+
+  art_pri_sift_down_from_root (pq, pq->items[--pq->n_items]);
+  return result;
+}
+
+#else
+
+/* Choose least point in queue */
+static ArtPriPoint *
+art_pri_choose (ArtPriQ *pq)
+{
+  int i;
+  int best = 0;
+  double best_x, best_y;
+  double y;
+  ArtPriPoint *result;
+
+  if (pq->n_items == 0)
+    return NULL;
+
+  best_x = pq->items[best]->x;
+  best_y = pq->items[best]->y;
+
+  for (i = 1; i < pq->n_items; i++)
+    {
+      y = pq->items[i]->y;
+      if (y < best_y || (y == best_y && pq->items[i]->x < best_x))
+	{
+	  best = i;
+	  best_x = pq->items[best]->x;
+	  best_y = y;
+	}
+    }
+  result = pq->items[best];
+  pq->items[best] = pq->items[--pq->n_items];
+  return result;
+}
+
+static void
+art_pri_insert (ArtPriQ *pq, ArtPriPoint *point)
+{
+  if (pq->n_items == pq->n_items_max)
+    art_expand (pq->items, ArtPriPoint *, pq->n_items_max);
+
+  pq->items[pq->n_items++] = point;
+}
+
+#endif
+
+#ifdef TEST_PRIQ
+
+#include <stdlib.h> /* for rand() */
+#include <stdio.h>
+
+static double
+double_rand (double lo, double hi, int quant)
+{
+  int tmp = rand () / (RAND_MAX * (1.0 / quant)) + 0.5;
+  return lo + tmp * ((hi - lo) / quant);
+}
+
+/*
+ * This custom allocator for priority queue points is here so I can
+ * test speed. It doesn't look like it will be that significant, but
+ * if I want a small improvement later, it's something.
+ */
+
+typedef ArtPriPoint *ArtPriPtPool;
+
+static ArtPriPtPool *
+art_pri_pt_pool_new (void)
+{
+  ArtPriPtPool *result = art_new (ArtPriPtPool, 1);
+  *result = NULL;
+  return result;
+}
+
+static ArtPriPoint *
+art_pri_pt_alloc (ArtPriPtPool *pool)
+{
+  ArtPriPoint *result = *pool;
+  if (result == NULL)
+    return art_new (ArtPriPoint, 1);
+  else
+    {
+      *pool = result->user_data;
+      return result;
+    }
+}
+
+static void
+art_pri_pt_free (ArtPriPtPool *pool, ArtPriPoint *pt)
+{
+  pt->user_data = *pool;
+  *pool = pt;
+}
+
+static void
+art_pri_pt_pool_free (ArtPriPtPool *pool)
+{
+  ArtPriPoint *pt = *pool;
+  while (pt != NULL)
+    {
+      ArtPriPoint *next = pt->user_data;
+      art_free (pt);
+      pt = next;
+    }
+  art_free (pool);
+}
+
+int
+main (int argc, char **argv)
+{
+  ArtPriPtPool *pool = art_pri_pt_pool_new ();
+  ArtPriQ *pq;
+  int i, j;
+  const int n_iter = 1;
+  const int pq_size = 100;
+
+  for (j = 0; j < n_iter; j++)
+    {
+      pq = art_pri_new ();
+
+      for (i = 0; i < pq_size; i++)
+	{
+	  ArtPriPoint *pt = art_pri_pt_alloc (pool);
+	  pt->x = double_rand (0, 1, 100);
+	  pt->y = double_rand (0, 1, 100);
+	  pt->user_data = (void *)i;
+	  art_pri_insert (pq, pt);
+	}
+
+      while (!art_pri_empty (pq))
+	{
+	  ArtPriPoint *pt = art_pri_choose (pq);
+	  if (n_iter == 1)
+	    printf ("(%g, %g), %d\n", pt->x, pt->y, (int)pt->user_data);
+	  art_pri_pt_free (pool, pt);
+	}
+
+      art_pri_free (pq);
+    }
+  art_pri_pt_pool_free (pool);
+  return 0;
+}
+
+#else /* TEST_PRIQ */
+
+/* A virtual class for an "svp writer". A client of this object creates an
+   SVP by repeatedly calling "add segment" and "add point" methods on it.
+*/
+
+typedef struct _ArtSvpWriterRewind ArtSvpWriterRewind;
+
+/* An implementation of the svp writer virtual class that applies the
+   winding rule. */
+
+struct _ArtSvpWriterRewind {
+  ArtSvpWriter super;
+  ArtWindRule rule;
+  ArtSVP *svp;
+  int n_segs_max;
+  int *n_points_max;
+};
+
+static int
+art_svp_writer_rewind_add_segment (ArtSvpWriter *self, int wind_left,
+				   int delta_wind, double x, double y)
+{
+  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+  ArtSVP *svp;
+  ArtSVPSeg *seg;
+  art_boolean left_filled, right_filled;
+  int wind_right = wind_left + delta_wind;
+  int seg_num;
+  const int init_n_points_max = 4;
+
+  switch (swr->rule)
+    {
+    case ART_WIND_RULE_NONZERO:
+      left_filled = (wind_left != 0);
+      right_filled = (wind_right != 0);
+      break;
+    case ART_WIND_RULE_INTERSECT:
+      left_filled = (wind_left > 1);
+      right_filled = (wind_right > 1);
+      break;
+    case ART_WIND_RULE_ODDEVEN:
+      left_filled = (wind_left & 1);
+      right_filled = (wind_right & 1);
+      break;
+    case ART_WIND_RULE_POSITIVE:
+      left_filled = (wind_left > 0);
+      right_filled = (wind_right > 0);
+      break;
+    default:
+      art_die ("Unknown wind rule %d\n", swr->rule);
+    }
+  if (left_filled == right_filled)
+    {
+      /* discard segment now */
+#ifdef VERBOSE
+      art_dprint ("swr add_segment: %d += %d (%g, %g) --> -1\n",
+	      wind_left, delta_wind, x, y);
+#endif
+      return -1;
+   }
+
+  svp = swr->svp;
+  seg_num = svp->n_segs++;
+  if (swr->n_segs_max == seg_num)
+    {
+      swr->n_segs_max <<= 1;
+      svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+				   (swr->n_segs_max - 1) *
+				   sizeof(ArtSVPSeg));
+      swr->svp = svp;
+      swr->n_points_max = art_renew (swr->n_points_max, int,
+				     swr->n_segs_max);
+    }
+  seg = &svp->segs[seg_num];
+  seg->n_points = 1;
+  seg->dir = right_filled;
+  swr->n_points_max[seg_num] = init_n_points_max;
+  seg->bbox.x0 = x;
+  seg->bbox.y0 = y;
+  seg->bbox.x1 = x;
+  seg->bbox.y1 = y;
+  seg->points = art_new (ArtPoint, init_n_points_max);
+  seg->points[0].x = x;
+  seg->points[0].y = y;
+#ifdef VERBOSE
+    art_dprint ("swr add_segment: %d += %d (%g, %g) --> %d(%s)\n",
+	    wind_left, delta_wind, x, y, seg_num,
+	    seg->dir ? "v" : "^");
+#endif
+  return seg_num;
+}
+
+static void
+art_svp_writer_rewind_add_point (ArtSvpWriter *self, int seg_id,
+				 double x, double y)
+{
+  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+  ArtSVPSeg *seg;
+  int n_points;
+
+#ifdef VERBOSE
+  art_dprint ("swr add_point: %d (%g, %g)\n", seg_id, x, y);
+#endif
+  if (seg_id < 0)
+    /* omitted segment */
+    return;
+
+  seg = &swr->svp->segs[seg_id];
+  n_points = seg->n_points++;
+  if (swr->n_points_max[seg_id] == n_points)
+    art_expand (seg->points, ArtPoint, swr->n_points_max[seg_id]);
+  seg->points[n_points].x = x;
+  seg->points[n_points].y = y;
+  if (x < seg->bbox.x0)
+    seg->bbox.x0 = x;
+  if (x > seg->bbox.x1)
+    seg->bbox.x1 = x;
+  seg->bbox.y1 = y;
+}
+
+static void
+art_svp_writer_rewind_close_segment (ArtSvpWriter *self, int seg_id)
+{
+  /* Not needed for this simple implementation. A potential future
+     optimization is to merge segments that can be merged safely. */
+#ifdef SANITYCHECK
+  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+  ArtSVPSeg *seg;
+
+  if (seg_id >= 0)
+    {
+      seg = &swr->svp->segs[seg_id];
+      if (seg->n_points < 2)
+	art_warn ("*** closing segment %d with only %d point%s\n",
+		  seg_id, seg->n_points, seg->n_points == 1 ? "" : "s");
+    }
+#endif
+
+#ifdef VERBOSE
+  art_dprint ("swr close_segment: %d\n", seg_id);
+#endif
+}
+
+ArtSVP *
+art_svp_writer_rewind_reap (ArtSvpWriter *self)
+{
+  ArtSvpWriterRewind *swr = (ArtSvpWriterRewind *)self;
+  ArtSVP *result = swr->svp;
+
+  art_free (swr->n_points_max);
+  art_free (swr);
+  return result;
+}
+
+ArtSvpWriter *
+art_svp_writer_rewind_new (ArtWindRule rule)
+{
+  ArtSvpWriterRewind *result = art_new (ArtSvpWriterRewind, 1);
+
+  result->super.add_segment = art_svp_writer_rewind_add_segment;
+  result->super.add_point = art_svp_writer_rewind_add_point;
+  result->super.close_segment = art_svp_writer_rewind_close_segment;
+
+  result->rule = rule;
+  result->n_segs_max = 16;
+  result->svp = art_alloc (sizeof(ArtSVP) +
+			   (result->n_segs_max - 1) * sizeof(ArtSVPSeg));
+  result->svp->n_segs = 0;
+  result->n_points_max = art_new (int, result->n_segs_max);
+
+  return &result->super;
+}
+
+/* Now, data structures for the active list */
+
+typedef struct _ArtActiveSeg ArtActiveSeg;
+
+/* Note: BNEG is 1 for \ lines, and 0 for /. Thus,
+   x[(flags & BNEG) ^ 1] <= x[flags & BNEG] */
+#define ART_ACTIVE_FLAGS_BNEG 1
+
+/* This flag is set if the segment has been inserted into the active
+   list. */
+#define ART_ACTIVE_FLAGS_IN_ACTIVE 2
+
+/* This flag is set when the segment is to be deleted in the
+   horiz commit process. */
+#define ART_ACTIVE_FLAGS_DEL 4
+
+/* This flag is set if the seg_id is a valid output segment. */
+#define ART_ACTIVE_FLAGS_OUT 8
+
+/* This flag is set if the segment is in the horiz list. */
+#define ART_ACTIVE_FLAGS_IN_HORIZ 16
+
+struct _ArtActiveSeg {
+  int flags;
+  int wind_left, delta_wind;
+  ArtActiveSeg *left, *right; /* doubly linked list structure */
+
+  const ArtSVPSeg *in_seg;
+  int in_curs;
+
+  double x[2];
+  double y0, y1;
+  double a, b, c; /* line equation; ax+by+c = 0 for the line, a^2 + b^2 = 1,
+		     and a>0 */
+
+  /* bottom point and intersection point stack */
+  int n_stack;
+  int n_stack_max;
+  ArtPoint *stack;
+
+  /* horiz commit list */
+  ArtActiveSeg *horiz_left, *horiz_right;
+  double horiz_x;
+  int horiz_delta_wind;
+  int seg_id;
+};
+
+typedef struct _ArtIntersectCtx ArtIntersectCtx;
+
+struct _ArtIntersectCtx {
+  const ArtSVP *in;
+  ArtSvpWriter *out;
+
+  ArtPriQ *pq;
+
+  ArtActiveSeg *active_head;
+
+  double y;
+  ArtActiveSeg *horiz_first;
+  ArtActiveSeg *horiz_last;
+
+  /* segment index of next input segment to be added to pri q */
+  int in_curs;
+};
+
+#define EPSILON_A 1e-5 /* Threshold for breaking lines at point insertions */
+
+/**
+ * art_svp_intersect_setup_seg: Set up an active segment from input segment.
+ * @seg: Active segment.
+ * @pri_pt: Priority queue point to initialize.
+ *
+ * Sets the x[], a, b, c, flags, and stack fields according to the
+ * line from the current cursor value. Sets the priority queue point
+ * to the bottom point of this line. Also advances the input segment
+ * cursor.
+ **/
+static void
+art_svp_intersect_setup_seg (ArtActiveSeg *seg, ArtPriPoint *pri_pt)
+{
+  const ArtSVPSeg *in_seg = seg->in_seg;
+  int in_curs = seg->in_curs++;
+  double x0, y0, x1, y1;
+  double dx, dy, s;
+  double a, b, r2;
+
+  x0 = in_seg->points[in_curs].x;
+  y0 = in_seg->points[in_curs].y;
+  x1 = in_seg->points[in_curs + 1].x;
+  y1 = in_seg->points[in_curs + 1].y;
+  pri_pt->x = x1;
+  pri_pt->y = y1;
+  dx = x1 - x0;
+  dy = y1 - y0;
+  r2 = dx * dx + dy * dy;
+  s = r2 == 0 ? 1 : 1 / sqrt (r2);
+  seg->a = a = dy * s;
+  seg->b = b = -dx * s;
+  seg->c = -(a * x0 + b * y0);
+  seg->flags = (seg->flags & ~ART_ACTIVE_FLAGS_BNEG) | (dx > 0);
+  seg->x[0] = x0;
+  seg->x[1] = x1;
+  seg->y0 = y0;
+  seg->y1 = y1;
+  seg->n_stack = 1;
+  seg->stack[0].x = x1;
+  seg->stack[0].y = y1;
+}
+
+/**
+ * art_svp_intersect_add_horiz: Add point to horizontal list.
+ * @ctx: Intersector context.
+ * @seg: Segment with point to insert into horizontal list.
+ *
+ * Inserts @seg into horizontal list, keeping it in ascending horiz_x
+ * order.
+ *
+ * Note: the horiz_commit routine processes "clusters" of segs in the
+ * horiz list, all sharing the same horiz_x value. The cluster is
+ * processed in active list order, rather than horiz list order. Thus,
+ * the order of segs in the horiz list sharing the same horiz_x
+ * _should_ be irrelevant. Even so, we use b as a secondary sorting key,
+ * as a "belt and suspenders" defensive coding tactic.
+ **/
+static void
+art_svp_intersect_add_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
+{
+  ArtActiveSeg **pp = &ctx->horiz_last;
+  ArtActiveSeg *place;
+  ArtActiveSeg *place_right = NULL;
+
+
+#ifdef CHEAP_SANITYCHECK
+  if (seg->flags & ART_ACTIVE_FLAGS_IN_HORIZ)
+    {
+      art_warn ("*** attempt to put segment in horiz list twice\n");
+      return;
+    }
+  seg->flags |= ART_ACTIVE_FLAGS_IN_HORIZ;
+#endif
+
+#ifdef VERBOSE
+  art_dprint ("add_horiz %lx, x = %g\n", (unsigned long) seg, seg->horiz_x);
+#endif
+  for (place = *pp; place != NULL && (place->horiz_x > seg->horiz_x ||
+				      (place->horiz_x == seg->horiz_x &&
+				       place->b < seg->b));
+       place = *pp)
+    {
+      place_right = place;
+      pp = &place->horiz_left;
+    }
+  *pp = seg;
+  seg->horiz_left = place;
+  seg->horiz_right = place_right;
+  if (place == NULL)
+    ctx->horiz_first = seg;
+  else
+    place->horiz_right = seg;
+}
+
+static void
+art_svp_intersect_push_pt (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+			   double x, double y)
+{
+  ArtPriPoint *pri_pt;
+  int n_stack = seg->n_stack;
+
+  if (n_stack == seg->n_stack_max)
+    art_expand (seg->stack, ArtPoint, seg->n_stack_max);
+  seg->stack[n_stack].x = x;
+  seg->stack[n_stack].y = y;
+  seg->n_stack++;
+
+  seg->x[1] = x;
+  seg->y1 = y;
+
+  pri_pt = art_new (ArtPriPoint, 1);
+  pri_pt->x = x;
+  pri_pt->y = y;
+  pri_pt->user_data = seg;
+  art_pri_insert (ctx->pq, pri_pt);
+}
+
+typedef enum {
+  ART_BREAK_LEFT = 1,
+  ART_BREAK_RIGHT = 2
+} ArtBreakFlags;
+
+/**
+ * art_svp_intersect_break: Break an active segment.
+ *
+ * Note: y must be greater than the top point's y, and less than
+ * the bottom's.
+ *
+ * Return value: x coordinate of break point.
+ */
+static double
+art_svp_intersect_break (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+			 double x_ref, double y, ArtBreakFlags break_flags)
+{
+  double x0, y0, x1, y1;
+  const ArtSVPSeg *in_seg = seg->in_seg;
+  int in_curs = seg->in_curs;
+  double x;
+
+  x0 = in_seg->points[in_curs - 1].x;
+  y0 = in_seg->points[in_curs - 1].y;
+  x1 = in_seg->points[in_curs].x;
+  y1 = in_seg->points[in_curs].y;
+  x = x0 + (x1 - x0) * ((y - y0) / (y1 - y0));
+  if ((break_flags == ART_BREAK_LEFT && x > x_ref) ||
+      (break_flags == ART_BREAK_RIGHT && x < x_ref))
+    {
+#ifdef VERBOSE
+      art_dprint ("art_svp_intersect_break: limiting x to %f, was %f, %s\n",
+		  x_ref, x, break_flags == ART_BREAK_LEFT ? "left" : "right");
+      x = x_ref;
+#endif
+    }
+
+  /* I think we can count on min(x0, x1) <= x <= max(x0, x1) with sane
+     arithmetic, but it might be worthwhile to check just in case. */
+
+  if (y > ctx->y)
+    art_svp_intersect_push_pt (ctx, seg, x, y);
+  else
+    {
+      seg->x[0] = x;
+      seg->y0 = y;
+      seg->horiz_x = x;
+      art_svp_intersect_add_horiz (ctx, seg);
+    }
+
+  return x;
+}
+
+/**
+ * art_svp_intersect_add_point: Add a point, breaking nearby neighbors.
+ * @ctx: Intersector context.
+ * @x: X coordinate of point to add.
+ * @y: Y coordinate of point to add.
+ * @seg: "nearby" segment, or NULL if leftmost.
+ *
+ * Return value: Segment immediately to the left of the new point, or
+ * NULL if the new point is leftmost.
+ **/
+static ArtActiveSeg *
+art_svp_intersect_add_point (ArtIntersectCtx *ctx, double x, double y,
+			     ArtActiveSeg *seg, ArtBreakFlags break_flags)
+{
+  ArtActiveSeg *left, *right;
+  double x_min = x, x_max = x;
+  art_boolean left_live, right_live;
+  double d;
+  double new_x;
+  ArtActiveSeg *test, *result = NULL;
+  double x_test;
+
+  left = seg;
+  if (left == NULL)
+    right = ctx->active_head;
+  else
+    right = left->right;
+  left_live = (break_flags & ART_BREAK_LEFT) && (left != NULL);
+  right_live = (break_flags & ART_BREAK_RIGHT) && (right != NULL);
+  while (left_live || right_live)
+    {
+      if (left_live)
+	{
+	  if (x <= left->x[left->flags & ART_ACTIVE_FLAGS_BNEG] &&
+	      /* It may be that one of these conjuncts turns out to be always
+		 true. We test both anyway, to be defensive. */
+	      y != left->y0 && y < left->y1)
+	    {
+	      d = x_min * left->a + y * left->b + left->c;
+	      if (d < EPSILON_A)
+		{
+		  new_x = art_svp_intersect_break (ctx, left, x_min, y,
+						   ART_BREAK_LEFT);
+		  if (new_x > x_max)
+		    {
+		      x_max = new_x;
+		      right_live = (right != NULL);
+		    }
+		  else if (new_x < x_min)
+		    x_min = new_x;
+		  left = left->left;
+		  left_live = (left != NULL);
+		}
+	      else
+		left_live = ART_FALSE;
+	    }
+	  else
+	    left_live = ART_FALSE;
+	}
+      else if (right_live)
+	{
+	  if (x >= right->x[(right->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] &&
+	      /* It may be that one of these conjuncts turns out to be always
+		 true. We test both anyway, to be defensive. */
+	      y != right->y0 && y < right->y1)
+	    {
+	      d = x_max * right->a + y * right->b + right->c;
+	      if (d > -EPSILON_A)
+		{
+		  new_x = art_svp_intersect_break (ctx, right, x_max, y,
+						   ART_BREAK_RIGHT);
+		  if (new_x < x_min)
+		    {
+		      x_min = new_x;
+		      left_live = (left != NULL);
+		    }
+		  else if (new_x >= x_max)
+		    x_max = new_x;
+		  right = right->right;
+		  right_live = (right != NULL);
+		}
+	      else
+		right_live = ART_FALSE;
+	    }
+	  else
+	    right_live = ART_FALSE;
+	}
+    }
+
+  /* Ascending order is guaranteed by break_flags. Thus, we don't need
+     to actually fix up non-ascending pairs. */
+
+  /* Now, (left, right) defines an interval of segments broken. Sort
+     into ascending x order. */
+  test = left == NULL ? ctx->active_head : left->right;
+  result = left;
+  if (test != NULL && test != right)
+    {
+      if (y == test->y0)
+	x_test = test->x[0];
+      else /* assert y == test->y1, I think */
+	x_test = test->x[1];
+      for (;;)
+	{
+	  if (x_test <= x)
+	    result = test;
+	  test = test->right;
+	  if (test == right)
+	    break;
+	  new_x = x_test;
+	  if (new_x < x_test)
+	    {
+	      art_warn ("art_svp_intersect_add_point: non-ascending x\n");
+	    }
+	  x_test = new_x;
+	}
+    }
+  return result;
+}
+
+static void
+art_svp_intersect_swap_active (ArtIntersectCtx *ctx,
+			       ArtActiveSeg *left_seg, ArtActiveSeg *right_seg)
+{
+  right_seg->left = left_seg->left;
+  if (right_seg->left != NULL)
+    right_seg->left->right = right_seg;
+  else
+    ctx->active_head = right_seg;
+  left_seg->right = right_seg->right;
+  if (left_seg->right != NULL)
+    left_seg->right->left = left_seg;
+  left_seg->left = right_seg;
+  right_seg->right = left_seg;
+}
+
+/**
+ * art_svp_intersect_test_cross: Test crossing of a pair of active segments.
+ * @ctx: Intersector context.
+ * @left_seg: Left segment of the pair.
+ * @right_seg: Right segment of the pair.
+ * @break_flags: Flags indicating whether to break neighbors.
+ *
+ * Tests crossing of @left_seg and @right_seg. If there is a crossing,
+ * inserts the intersection point into both segments.
+ *
+ * Return value: True if the intersection took place at the current
+ * scan line, indicating further iteration is needed.
+ **/
+static art_boolean
+art_svp_intersect_test_cross (ArtIntersectCtx *ctx,
+			      ArtActiveSeg *left_seg, ArtActiveSeg *right_seg,
+			      ArtBreakFlags break_flags)
+{
+  double left_x0, left_y0, left_x1;
+  double left_y1 = left_seg->y1;
+  double right_y1 = right_seg->y1;
+  double d;
+
+  const ArtSVPSeg *in_seg;
+  int in_curs;
+  double d0, d1, t;
+  double x, y; /* intersection point */
+
+#ifdef VERBOSE
+  static int count = 0;
+
+  art_dprint ("art_svp_intersect_test_cross %lx <-> %lx: count=%d\n",
+	  (unsigned long)left_seg, (unsigned long)right_seg, count++);
+#endif
+
+  if (left_seg->y0 == right_seg->y0 && left_seg->x[0] == right_seg->x[0])
+    {
+      /* Top points of left and right segments coincide. This case
+	 feels like a bit of duplication - we may want to merge it
+	 with the cases below. However, this way, we're sure that this
+	 logic makes only localized changes. */
+
+      if (left_y1 < right_y1)
+	{
+	  /* Test left (x1, y1) against right segment */
+	  double left_x1 = left_seg->x[1];
+
+	  if (left_x1 <
+	      right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+	      left_y1 == right_seg->y0)
+	    return ART_FALSE;
+	  d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+	  if (d < -EPSILON_A)
+	    return ART_FALSE;
+	  else if (d < EPSILON_A)
+	    {
+	      /* I'm unsure about the break flags here. */
+	      double right_x1 = art_svp_intersect_break (ctx, right_seg,
+							 left_x1, left_y1,
+							 ART_BREAK_RIGHT);
+	      if (left_x1 <= right_x1)
+		return ART_FALSE;
+	    }
+	}
+      else if (left_y1 > right_y1)
+	{
+	  /* Test right (x1, y1) against left segment */
+	  double right_x1 = right_seg->x[1];
+
+	  if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+	      right_y1 == left_seg->y0)
+	    return ART_FALSE;
+	  d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+	  if (d > EPSILON_A)
+	    return ART_FALSE;
+	  else if (d > -EPSILON_A)
+	    {
+	      /* See above regarding break flags. */
+	      double left_x1 = art_svp_intersect_break (ctx, left_seg,
+							right_x1, right_y1,
+							ART_BREAK_LEFT);
+	      if (left_x1 <= right_x1)
+		return ART_FALSE;
+	    }
+	}
+      else /* left_y1 == right_y1 */
+	{
+	  double left_x1 = left_seg->x[1];
+	  double right_x1 = right_seg->x[1];
+
+	  if (left_x1 <= right_x1)
+	    return ART_FALSE;
+	}
+      art_svp_intersect_swap_active (ctx, left_seg, right_seg);
+      return ART_TRUE;
+    }
+
+  if (left_y1 < right_y1)
+    {
+      /* Test left (x1, y1) against right segment */
+      double left_x1 = left_seg->x[1];
+
+      if (left_x1 <
+	  right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1] ||
+	  left_y1 == right_seg->y0)
+	return ART_FALSE;
+      d = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+      if (d < -EPSILON_A)
+	return ART_FALSE;
+      else if (d < EPSILON_A)
+	{
+	  double right_x1 = art_svp_intersect_break (ctx, right_seg,
+						     left_x1, left_y1,
+						     ART_BREAK_RIGHT);
+	  if (left_x1 <= right_x1)
+	    return ART_FALSE;
+	}
+    }
+  else if (left_y1 > right_y1)
+    {
+      /* Test right (x1, y1) against left segment */
+      double right_x1 = right_seg->x[1];
+
+      if (right_x1 > left_seg->x[left_seg->flags & ART_ACTIVE_FLAGS_BNEG] ||
+	  right_y1 == left_seg->y0)
+	return ART_FALSE;
+      d = right_x1 * left_seg->a + right_y1 * left_seg->b + left_seg->c;
+      if (d > EPSILON_A)
+	return ART_FALSE;
+      else if (d > -EPSILON_A)
+	{
+	  double left_x1 = art_svp_intersect_break (ctx, left_seg,
+						    right_x1, right_y1,
+						    ART_BREAK_LEFT);
+	  if (left_x1 <= right_x1)
+	    return ART_FALSE;
+	}
+    }
+  else /* left_y1 == right_y1 */
+    {
+      double left_x1 = left_seg->x[1];
+      double right_x1 = right_seg->x[1];
+
+      if (left_x1 <= right_x1)
+	return ART_FALSE;
+    }
+
+  /* The segments cross. Find the intersection point. */
+
+  in_seg = left_seg->in_seg;
+  in_curs = left_seg->in_curs;
+  left_x0 = in_seg->points[in_curs - 1].x;
+  left_y0 = in_seg->points[in_curs - 1].y;
+  left_x1 = in_seg->points[in_curs].x;
+  left_y1 = in_seg->points[in_curs].y;
+  d0 = left_x0 * right_seg->a + left_y0 * right_seg->b + right_seg->c;
+  d1 = left_x1 * right_seg->a + left_y1 * right_seg->b + right_seg->c;
+  if (d0 == d1)
+    {
+      x = left_x0;
+      y = left_y0;
+    }
+  else
+    {
+      /* Is this division always safe? It could possibly overflow. */
+      t = d0 / (d0 - d1);
+      if (t <= 0)
+	{
+	  x = left_x0;
+	  y = left_y0;
+	}
+      else if (t >= 1)
+	{
+	  x = left_x1;
+	  y = left_y1;
+	}
+      else
+	{
+	  x = left_x0 + t * (left_x1 - left_x0);
+	  y = left_y0 + t * (left_y1 - left_y0);
+	}
+    }
+
+  /* Make sure intersection point is within bounds of right seg. */
+  if (y < right_seg->y0)
+    {
+      x = right_seg->x[0];
+      y = right_seg->y0;
+    }
+  else if (y > right_seg->y1)
+    {
+      x = right_seg->x[1];
+      y = right_seg->y1;
+    }
+  else if (x < right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1])
+    x = right_seg->x[(right_seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1];
+  else if (x > right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG])
+    x = right_seg->x[right_seg->flags & ART_ACTIVE_FLAGS_BNEG];
+
+  if (y == left_seg->y0)
+    {
+      if (y != right_seg->y0)
+	{
+#ifdef VERBOSE
+	  art_dprint ("art_svp_intersect_test_cross: intersection (%g, %g) matches former y0 of %lx, %lx\n",
+		    x, y, (unsigned long)left_seg, (unsigned long)right_seg);
+#endif
+	  art_svp_intersect_push_pt (ctx, right_seg, x, y);
+	  if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+	    art_svp_intersect_add_point (ctx, x, y, right_seg->right,
+					 break_flags);
+	}
+      else
+	{
+	  /* Intersection takes place at current scan line; process
+	     immediately rather than queueing intersection point into
+	     priq. */
+	  ArtActiveSeg *winner, *loser;
+
+	  /* Choose "most vertical" segement */
+	  if (left_seg->a > right_seg->a)
+	    {
+	      winner = left_seg;
+	      loser = right_seg;
+	    }
+	  else
+	    {
+	      winner = right_seg;
+	      loser = left_seg;
+	    }
+
+	  loser->x[0] = winner->x[0];
+	  loser->horiz_x = loser->x[0];
+	  loser->horiz_delta_wind += loser->delta_wind;
+	  winner->horiz_delta_wind -= loser->delta_wind;
+
+	  art_svp_intersect_swap_active (ctx, left_seg, right_seg);
+	  return ART_TRUE;
+	}
+    }
+  else if (y == right_seg->y0)
+    {
+#ifdef VERBOSE
+      art_dprint ("*** art_svp_intersect_test_cross: intersection (%g, %g) matches latter y0 of %lx, %lx\n",
+	      x, y, (unsigned long)left_seg, (unsigned long)right_seg);
+#endif
+      art_svp_intersect_push_pt (ctx, left_seg, x, y);
+      if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+	art_svp_intersect_add_point (ctx, x, y, left_seg->left,
+				     break_flags);
+    }
+  else
+    {
+#ifdef VERBOSE
+      art_dprint ("Inserting (%g, %g) into %lx, %lx\n",
+	      x, y, (unsigned long)left_seg, (unsigned long)right_seg);
+#endif
+      /* Insert the intersection point into both segments. */
+      art_svp_intersect_push_pt (ctx, left_seg, x, y);
+      art_svp_intersect_push_pt (ctx, right_seg, x, y);
+      if ((break_flags & ART_BREAK_LEFT) && left_seg->left != NULL)
+	art_svp_intersect_add_point (ctx, x, y, left_seg->left, break_flags);
+      if ((break_flags & ART_BREAK_RIGHT) && right_seg->right != NULL)
+	art_svp_intersect_add_point (ctx, x, y, right_seg->right, break_flags);
+    }
+  return ART_FALSE;
+}
+
+/**
+ * art_svp_intersect_active_delete: Delete segment from active list.
+ * @ctx: Intersection context.
+ * @seg: Segment to delete.
+ *
+ * Deletes @seg from the active list.
+ **/
+static /* todo inline */ void
+art_svp_intersect_active_delete (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
+{
+  ArtActiveSeg *left = seg->left, *right = seg->right;
+
+  if (left != NULL)
+    left->right = right;
+  else
+    ctx->active_head = right;
+  if (right != NULL)
+    right->left = left;
+}
+
+/**
+ * art_svp_intersect_active_free: Free an active segment.
+ * @seg: Segment to delete.
+ *
+ * Frees @seg.
+ **/
+static /* todo inline */ void
+art_svp_intersect_active_free (ArtActiveSeg *seg)
+{
+  art_free (seg->stack);
+#ifdef VERBOSE
+  art_dprint ("Freeing %lx\n", (unsigned long) seg);
+#endif
+  art_free (seg);
+}
+
+/**
+ * art_svp_intersect_insert_cross: Test crossings of newly inserted line.
+ *
+ * Tests @seg against its left and right neighbors for intersections.
+ * Precondition: the line in @seg is not purely horizontal.
+ **/
+static void
+art_svp_intersect_insert_cross (ArtIntersectCtx *ctx,
+				ArtActiveSeg *seg)
+{
+  ArtActiveSeg *left = seg, *right = seg;
+
+  for (;;)
+    {
+      if (left != NULL)
+	{
+	  ArtActiveSeg *leftc;
+
+	  for (leftc = left->left; leftc != NULL; leftc = leftc->left)
+	    if (!(leftc->flags & ART_ACTIVE_FLAGS_DEL))
+	      break;
+	  if (leftc != NULL &&
+	      art_svp_intersect_test_cross (ctx, leftc, left,
+					    ART_BREAK_LEFT))
+	    {
+	      if (left == right || right == NULL)
+		right = left->right;
+	    }
+	  else
+	    {
+	      left = NULL;
+	    }
+	}
+      else if (right != NULL && right->right != NULL)
+	{
+	  ArtActiveSeg *rightc;
+
+	  for (rightc = right->right; rightc != NULL; rightc = rightc->right)
+	    if (!(rightc->flags & ART_ACTIVE_FLAGS_DEL))
+	      break;
+	  if (rightc != NULL &&
+	      art_svp_intersect_test_cross (ctx, right, rightc,
+					    ART_BREAK_RIGHT))
+	    {
+	      if (left == right || left == NULL)
+		left = right->left;
+	    }
+	  else
+	    {
+	      right = NULL;
+	    }
+	}
+      else
+	break;
+    }
+}
+
+/**
+ * art_svp_intersect_horiz: Add horizontal line segment.
+ * @ctx: Intersector context.
+ * @seg: Segment on which to add horizontal line.
+ * @x0: Old x position.
+ * @x1: New x position.
+ *
+ * Adds a horizontal line from @x0 to @x1, and updates the current
+ * location of @seg to @x1.
+ **/
+static void
+art_svp_intersect_horiz (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+			 double x0, double x1)
+{
+  ArtActiveSeg *hs;
+
+  if (x0 == x1)
+    return;
+
+  hs = art_new (ArtActiveSeg, 1);
+
+  hs->flags = ART_ACTIVE_FLAGS_DEL | (seg->flags & ART_ACTIVE_FLAGS_OUT);
+  if (seg->flags & ART_ACTIVE_FLAGS_OUT)
+    {
+      ArtSvpWriter *swr = ctx->out;
+
+      swr->add_point (swr, seg->seg_id, x0, ctx->y);
+    }
+  hs->seg_id = seg->seg_id;
+  hs->horiz_x = x0;
+  hs->horiz_delta_wind = seg->delta_wind;
+  hs->stack = NULL;
+
+  /* Ideally, the (a, b, c) values will never be read. However, there
+     are probably some tests remaining that don't check for _DEL
+     before evaluating the line equation. For those, these
+     initializations will at least prevent a UMR of the values, which
+     can crash on some platforms. */
+  hs->a = 0.0;
+  hs->b = 0.0;
+  hs->c = 0.0;
+
+  seg->horiz_delta_wind -= seg->delta_wind;
+
+  art_svp_intersect_add_horiz (ctx, hs);
+
+  if (x0 > x1)
+    {
+      ArtActiveSeg *left;
+      art_boolean first = ART_TRUE;
+
+      for (left = seg->left; left != NULL; left = seg->left)
+	{
+	  int left_bneg = left->flags & ART_ACTIVE_FLAGS_BNEG;
+
+	  if (left->x[left_bneg] <= x1)
+	    break;
+	  if (left->x[left_bneg ^ 1] <= x1 &&
+	      x1 * left->a + ctx->y * left->b + left->c >= 0)
+	    break;
+	  if (left->y0 != ctx->y && left->y1 != ctx->y)
+	    {
+	      art_svp_intersect_break (ctx, left, x1, ctx->y, ART_BREAK_LEFT);
+	    }
+#ifdef VERBOSE
+	  art_dprint ("x0=%g > x1=%g, swapping %lx, %lx\n",
+		  x0, x1, (unsigned long)left, (unsigned long)seg);
+#endif
+	  art_svp_intersect_swap_active (ctx, left, seg);
+	  if (first && left->right != NULL)
+	    {
+	      art_svp_intersect_test_cross (ctx, left, left->right,
+					    ART_BREAK_RIGHT);
+	      first = ART_FALSE;
+	    }
+	}
+    }
+  else
+    {
+      ArtActiveSeg *right;
+      art_boolean first = ART_TRUE;
+
+      for (right = seg->right; right != NULL; right = seg->right)
+	{
+	  int right_bneg = right->flags & ART_ACTIVE_FLAGS_BNEG;
+
+	  if (right->x[right_bneg ^ 1] >= x1)
+	    break;
+	  if (right->x[right_bneg] >= x1 &&
+	      x1 * right->a + ctx->y * right->b + right->c <= 0)
+	    break;
+	  if (right->y0 != ctx->y && right->y1 != ctx->y)
+	    {
+	      art_svp_intersect_break (ctx, right, x1, ctx->y,
+				       ART_BREAK_LEFT);
+	    }
+#ifdef VERBOSE
+	  art_dprint ("[right]x0=%g < x1=%g, swapping %lx, %lx\n",
+		  x0, x1, (unsigned long)seg, (unsigned long)right);
+#endif
+	  art_svp_intersect_swap_active (ctx, seg, right);
+	  if (first && right->left != NULL)
+	    {
+	      art_svp_intersect_test_cross (ctx, right->left, right,
+					    ART_BREAK_RIGHT);
+	      first = ART_FALSE;
+	    }
+	}
+    }
+
+  seg->x[0] = x1;
+  seg->x[1] = x1;
+  seg->horiz_x = x1;
+  seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+}
+
+/**
+ * art_svp_intersect_insert_line: Insert a line into the active list.
+ * @ctx: Intersector context.
+ * @seg: Segment containing line to insert.
+ *
+ * Inserts the line into the intersector context, taking care of any
+ * intersections, and adding the appropriate horizontal points to the
+ * active list.
+ **/
+static void
+art_svp_intersect_insert_line (ArtIntersectCtx *ctx, ArtActiveSeg *seg)
+{
+  if (seg->y1 == seg->y0)
+    {
+#ifdef VERBOSE
+      art_dprint ("art_svp_intersect_insert_line: %lx is horizontal\n",
+	      (unsigned long)seg);
+#endif
+      art_svp_intersect_horiz (ctx, seg, seg->x[0], seg->x[1]);
+    }
+  else
+    {
+      art_svp_intersect_insert_cross (ctx, seg);
+      art_svp_intersect_add_horiz (ctx, seg);
+    }
+}
+
+static void
+art_svp_intersect_process_intersection (ArtIntersectCtx *ctx,
+					ArtActiveSeg *seg)
+{
+  int n_stack = --seg->n_stack;
+  seg->x[1] = seg->stack[n_stack - 1].x;
+  seg->y1 = seg->stack[n_stack - 1].y;
+  seg->x[0] = seg->stack[n_stack].x;
+  seg->y0 = seg->stack[n_stack].y;
+  seg->horiz_x = seg->x[0];
+  art_svp_intersect_insert_line (ctx, seg);
+}
+
+static void
+art_svp_intersect_advance_cursor (ArtIntersectCtx *ctx, ArtActiveSeg *seg,
+				  ArtPriPoint *pri_pt)
+{
+  const ArtSVPSeg *in_seg = seg->in_seg;
+  int in_curs = seg->in_curs;
+  ArtSvpWriter *swr = seg->flags & ART_ACTIVE_FLAGS_OUT ? ctx->out : NULL;
+
+  if (swr != NULL)
+    swr->add_point (swr, seg->seg_id, seg->x[1], seg->y1);
+  if (in_curs + 1 == in_seg->n_points)
+    {
+      ArtActiveSeg *left = seg->left, *right = seg->right;
+
+#if 0
+      if (swr != NULL)
+	swr->close_segment (swr, seg->seg_id);
+      seg->flags &= ~ART_ACTIVE_FLAGS_OUT;
+#endif
+      seg->flags |= ART_ACTIVE_FLAGS_DEL;
+      art_svp_intersect_add_horiz (ctx, seg);
+      art_svp_intersect_active_delete (ctx, seg);
+      if (left != NULL && right != NULL)
+	art_svp_intersect_test_cross (ctx, left, right,
+				      ART_BREAK_LEFT | ART_BREAK_RIGHT);
+      art_free (pri_pt);
+    }
+  else
+    {
+      seg->horiz_x = seg->x[1];
+
+      art_svp_intersect_setup_seg (seg, pri_pt);
+      art_pri_insert (ctx->pq, pri_pt);
+      art_svp_intersect_insert_line (ctx, seg);
+    }
+}
+
+static void
+art_svp_intersect_add_seg (ArtIntersectCtx *ctx, const ArtSVPSeg *in_seg)
+{
+  ArtActiveSeg *seg = art_new (ArtActiveSeg, 1);
+  ArtActiveSeg *test;
+  double x0, y0;
+  ArtActiveSeg *beg_range;
+  ArtActiveSeg *last = NULL;
+  ArtActiveSeg *left, *right;
+  ArtPriPoint *pri_pt = art_new (ArtPriPoint, 1);
+
+  seg->flags = 0;
+  seg->in_seg = in_seg;
+  seg->in_curs = 0;
+
+  seg->n_stack_max = 4;
+  seg->stack = art_new (ArtPoint, seg->n_stack_max);
+
+  seg->horiz_delta_wind = 0;
+
+  seg->wind_left = 0;
+
+  pri_pt->user_data = seg;
+  art_svp_intersect_setup_seg (seg, pri_pt);
+  art_pri_insert (ctx->pq, pri_pt);
+
+  /* Find insertion place for new segment */
+  /* This is currently a left-to-right scan, but should be replaced
+     with a binary search as soon as it's validated. */
+
+  x0 = in_seg->points[0].x;
+  y0 = in_seg->points[0].y;
+  beg_range = NULL;
+  for (test = ctx->active_head; test != NULL; test = test->right)
+    {
+      double d;
+      int test_bneg = test->flags & ART_ACTIVE_FLAGS_BNEG;
+
+      if (x0 < test->x[test_bneg])
+	{
+	  if (x0 < test->x[test_bneg ^ 1])
+	    break;
+	  d = x0 * test->a + y0 * test->b + test->c;
+	  if (d < 0)
+	    break;
+	}
+      last = test;
+    }
+
+  left = art_svp_intersect_add_point (ctx, x0, y0, last, ART_BREAK_LEFT | ART_BREAK_RIGHT);
+  seg->left = left;
+  if (left == NULL)
+    {
+      right = ctx->active_head;
+      ctx->active_head = seg;
+    }
+  else
+    {
+      right = left->right;
+      left->right = seg;
+    }
+  seg->right = right;
+  if (right != NULL)
+    right->left = seg;
+
+  seg->delta_wind = in_seg->dir ? 1 : -1;
+  seg->horiz_x = x0;
+
+  art_svp_intersect_insert_line (ctx, seg);
+}
+
+#ifdef SANITYCHECK
+static void
+art_svp_intersect_sanitycheck_winding (ArtIntersectCtx *ctx)
+{
+#if 0
+  /* At this point, we seem to be getting false positives, so it's
+     turned off for now. */
+
+  ArtActiveSeg *seg;
+  int winding_number = 0;
+
+  for (seg = ctx->active_head; seg != NULL; seg = seg->right)
+    {
+      /* Check winding number consistency. */
+      if (seg->flags & ART_ACTIVE_FLAGS_OUT)
+	{
+	  if (winding_number != seg->wind_left)
+	    art_warn ("*** art_svp_intersect_sanitycheck_winding: seg %lx has wind_left of %d, expected %d\n",
+		  (unsigned long) seg, seg->wind_left, winding_number);
+	  winding_number = seg->wind_left + seg->delta_wind;
+	}
+    }
+  if (winding_number != 0)
+    art_warn ("*** art_svp_intersect_sanitycheck_winding: non-balanced winding number %d\n",
+	      winding_number);
+#endif
+}
+#endif
+
+/**
+ * art_svp_intersect_horiz_commit: Commit points in horiz list to output.
+ * @ctx: Intersection context.
+ *
+ * The main function of the horizontal commit is to output new
+ * points to the output writer.
+ *
+ * This "commit" pass is also where winding numbers are assigned,
+ * because doing it here provides much greater tolerance for inputs
+ * which are not in strict SVP order.
+ *
+ * Each cluster in the horiz_list contains both segments that are in
+ * the active list (ART_ACTIVE_FLAGS_DEL is false) and that are not,
+ * and are scheduled to be deleted (ART_ACTIVE_FLAGS_DEL is true). We
+ * need to deal with both.
+ **/
+static void
+art_svp_intersect_horiz_commit (ArtIntersectCtx *ctx)
+{
+  ArtActiveSeg *seg;
+  int winding_number = 0; /* initialization just to avoid warning */
+  int horiz_wind = 0;
+  double last_x = 0; /* initialization just to avoid warning */
+
+#ifdef VERBOSE
+  art_dprint ("art_svp_intersect_horiz_commit: y=%g\n", ctx->y);
+  for (seg = ctx->horiz_first; seg != NULL; seg = seg->horiz_right)
+    art_dprint (" %lx: %g %+d\n",
+	    (unsigned long)seg, seg->horiz_x, seg->horiz_delta_wind);
+#endif
+
+  /* Output points to svp writer. */
+  for (seg = ctx->horiz_first; seg != NULL;)
+    {
+      /* Find a cluster with common horiz_x, */
+      ArtActiveSeg *curs;
+      double x = seg->horiz_x;
+
+      /* Generate any horizontal segments. */
+      if (horiz_wind != 0)
+	{
+	  ArtSvpWriter *swr = ctx->out;
+	  int seg_id;
+
+	  seg_id = swr->add_segment (swr, winding_number, horiz_wind,
+				     last_x, ctx->y);
+	  swr->add_point (swr, seg_id, x, ctx->y);
+	  swr->close_segment (swr, seg_id);
+	}
+
+      /* Find first active segment in cluster. */
+
+      for (curs = seg; curs != NULL && curs->horiz_x == x;
+	   curs = curs->horiz_right)
+	if (!(curs->flags & ART_ACTIVE_FLAGS_DEL))
+	  break;
+
+      if (curs != NULL && curs->horiz_x == x)
+	{
+	  /* There exists at least one active segment in this cluster. */
+
+	  /* Find beginning of cluster. */
+	  for (; curs->left != NULL; curs = curs->left)
+	    if (curs->left->horiz_x != x)
+	      break;
+
+	  if (curs->left != NULL)
+	    winding_number = curs->left->wind_left + curs->left->delta_wind;
+	  else
+	    winding_number = 0;
+
+	  do
+	    {
+#ifdef VERBOSE
+	      art_dprint (" curs %lx: winding_number = %d += %d\n",
+		      (unsigned long)curs, winding_number, curs->delta_wind);
+#endif
+	      if (!(curs->flags & ART_ACTIVE_FLAGS_OUT) ||
+		  curs->wind_left != winding_number)
+		{
+		  ArtSvpWriter *swr = ctx->out;
+
+		  if (curs->flags & ART_ACTIVE_FLAGS_OUT)
+		    {
+		      swr->add_point (swr, curs->seg_id,
+				      curs->horiz_x, ctx->y);
+		      swr->close_segment (swr, curs->seg_id);
+		    }
+
+		  curs->seg_id = swr->add_segment (swr, winding_number,
+						   curs->delta_wind,
+						   x, ctx->y);
+		  curs->flags |= ART_ACTIVE_FLAGS_OUT;
+		}
+	      curs->wind_left = winding_number;
+	      winding_number += curs->delta_wind;
+	      curs = curs->right;
+	    }
+	  while (curs != NULL && curs->horiz_x == x);
+	}
+
+      /* Skip past cluster. */
+      do
+	{
+	  ArtActiveSeg *next = seg->horiz_right;
+
+	  seg->flags &= ~ART_ACTIVE_FLAGS_IN_HORIZ;
+	  horiz_wind += seg->horiz_delta_wind;
+	  seg->horiz_delta_wind = 0;
+	  if (seg->flags & ART_ACTIVE_FLAGS_DEL)
+	    {
+	      if (seg->flags & ART_ACTIVE_FLAGS_OUT)
+		{
+		  ArtSvpWriter *swr = ctx->out;
+		  swr->close_segment (swr, seg->seg_id);
+		}
+	      art_svp_intersect_active_free (seg);
+	    }
+	  seg = next;
+	}
+      while (seg != NULL && seg->horiz_x == x);
+
+      last_x = x;
+    }
+  ctx->horiz_first = NULL;
+  ctx->horiz_last = NULL;
+#ifdef SANITYCHECK
+  art_svp_intersect_sanitycheck_winding (ctx);
+#endif
+}
+
+#ifdef VERBOSE
+static void
+art_svp_intersect_print_active (ArtIntersectCtx *ctx)
+{
+  ArtActiveSeg *seg;
+
+  art_dprint ("Active list (y = %g):\n", ctx->y);
+  for (seg = ctx->active_head; seg != NULL; seg = seg->right)
+    {
+      art_dprint (" %lx: (%g, %g)-(%g, %g), (a, b, c) = (%g, %g, %g)\n",
+	      (unsigned long)seg,
+	      seg->x[0], seg->y0, seg->x[1], seg->y1,
+	      seg->a, seg->b, seg->c);
+    }
+}
+#endif
+
+#ifdef SANITYCHECK
+static void
+art_svp_intersect_sanitycheck (ArtIntersectCtx *ctx)
+{
+  ArtActiveSeg *seg;
+  ArtActiveSeg *last = NULL;
+  double d;
+
+  for (seg = ctx->active_head; seg != NULL; seg = seg->right)
+    {
+      if (seg->left != last)
+	{
+	  art_warn ("*** art_svp_intersect_sanitycheck: last=%lx, seg->left=%lx\n",
+		    (unsigned long)last, (unsigned long)seg->left);
+	}
+      if (last != NULL)
+	{
+	  /* pairwise compare with previous seg */
+
+	  /* First the top. */
+	  if (last->y0 < seg->y0)
+	    {
+	    }
+	  else
+	    {
+	    }
+
+	  /* Then the bottom. */
+	  if (last->y1 < seg->y1)
+	    {
+	      if (!((last->x[1] <
+		     seg->x[(seg->flags & ART_ACTIVE_FLAGS_BNEG) ^ 1]) ||
+		    last->y1 == seg->y0))
+		{
+		  d = last->x[1] * seg->a + last->y1 * seg->b + seg->c;
+		  if (d >= -EPSILON_A)
+		    art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to right (d = %g)\n",
+			      last->x[1], last->y1, (unsigned long) last,
+			      (unsigned long) seg, d);
+		}
+	    }
+	  else if (last->y1 > seg->y1)
+
+	    {
+	      if (!((seg->x[1] >
+		     last->x[last->flags & ART_ACTIVE_FLAGS_BNEG]) ||
+		    seg->y1 == last->y0))
+	      {
+		d = seg->x[1] * last->a + seg->y1 * last->b + last->c;
+		if (d <= EPSILON_A)
+		  art_warn ("*** bottom (%g, %g) of %lx is not clear of %lx to left (d = %g)\n",
+			      seg->x[1], seg->y1, (unsigned long) seg,
+			      (unsigned long) last, d);
+	      }
+	    }
+	  else
+	    {
+	      if (last->x[1] > seg->x[1])
+		art_warn ("*** bottoms (%g, %g) of %lx and (%g, %g) of %lx out of order\n",
+			  last->x[1], last->y1, (unsigned long)last,
+			  seg->x[1], seg->y1, (unsigned long)seg);
+	    }
+	}
+      last = seg;
+    }
+}
+#endif
+
+void
+art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out)
+{
+  ArtIntersectCtx *ctx;
+  ArtPriQ *pq;
+  ArtPriPoint *first_point;
+#ifdef VERBOSE
+  int count = 0;
+#endif
+
+  if (in->n_segs == 0)
+    return;
+
+  ctx = art_new (ArtIntersectCtx, 1);
+  ctx->in = in;
+  ctx->out = out;
+  pq = art_pri_new ();
+  ctx->pq = pq;
+
+  ctx->active_head = NULL;
+
+  ctx->horiz_first = NULL;
+  ctx->horiz_last = NULL;
+
+  ctx->in_curs = 0;
+  first_point = art_new (ArtPriPoint, 1);
+  first_point->x = in->segs[0].points[0].x;
+  first_point->y = in->segs[0].points[0].y;
+  first_point->user_data = NULL;
+  ctx->y = first_point->y;
+  art_pri_insert (pq, first_point);
+
+  while (!art_pri_empty (pq))
+    {
+      ArtPriPoint *pri_point = art_pri_choose (pq);
+      ArtActiveSeg *seg = (ArtActiveSeg *)pri_point->user_data;
+
+#ifdef VERBOSE
+      art_dprint ("\nIntersector step %d\n", count++);
+      art_svp_intersect_print_active (ctx);
+      art_dprint ("priq choose (%g, %g) %lx\n", pri_point->x, pri_point->y,
+	      (unsigned long)pri_point->user_data);
+#endif
+#ifdef SANITYCHECK
+      art_svp_intersect_sanitycheck(ctx);
+#endif
+
+      if (ctx->y != pri_point->y)
+	{
+	  art_svp_intersect_horiz_commit (ctx);
+	  ctx->y = pri_point->y;
+	}
+
+      if (seg == NULL)
+	{
+	  /* Insert new segment from input */
+	  const ArtSVPSeg *in_seg = &in->segs[ctx->in_curs++];
+	  art_svp_intersect_add_seg (ctx, in_seg);
+	  if (ctx->in_curs < in->n_segs)
+	    {
+	      const ArtSVPSeg *next_seg = &in->segs[ctx->in_curs];
+	      pri_point->x = next_seg->points[0].x;
+	      pri_point->y = next_seg->points[0].y;
+	      /* user_data is already NULL */
+	      art_pri_insert (pq, pri_point);
+	    }
+	  else
+	    art_free (pri_point);
+	}
+      else
+	{
+	  int n_stack = seg->n_stack;
+
+	  if (n_stack > 1)
+	    {
+	      art_svp_intersect_process_intersection (ctx, seg);
+	      art_free (pri_point);
+	    }
+	  else
+	    {
+	      art_svp_intersect_advance_cursor (ctx, seg, pri_point);
+	    }
+	}
+    }
+
+  art_svp_intersect_horiz_commit (ctx);
+
+  art_pri_free (pq);
+  art_free (ctx);
+}
+
+#endif /* not TEST_PRIQ */
diff --git a/src/libart_lgpl/art_svp_intersect.h b/src/libart_lgpl/art_svp_intersect.h
new file mode 100644
index 0000000..adeef20
--- /dev/null
+++ b/src/libart_lgpl/art_svp_intersect.h
@@ -0,0 +1,69 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2001 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_INTERSECT_H__
+#define __ART_SVP_INTERSECT_H__
+
+/* The funky new SVP intersector. */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef ART_WIND_RULE_DEFINED
+#define ART_WIND_RULE_DEFINED
+typedef enum {
+  ART_WIND_RULE_NONZERO,
+  ART_WIND_RULE_INTERSECT,
+  ART_WIND_RULE_ODDEVEN,
+  ART_WIND_RULE_POSITIVE
+} ArtWindRule;
+#endif
+
+typedef struct _ArtSvpWriter ArtSvpWriter;
+
+struct _ArtSvpWriter {
+  int (*add_segment) (ArtSvpWriter *self, int wind_left, int delta_wind,
+		      double x, double y);
+  void (*add_point) (ArtSvpWriter *self, int seg_id, double x, double y);
+  void (*close_segment) (ArtSvpWriter *self, int seg_id);
+};
+
+ArtSvpWriter *
+art_svp_writer_rewind_new (ArtWindRule rule);
+
+ArtSVP *
+art_svp_writer_rewind_reap (ArtSvpWriter *self);
+
+int
+art_svp_seg_compare (const void *s1, const void *s2);
+
+void
+art_svp_intersector (const ArtSVP *in, ArtSvpWriter *out);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_INTERSECT_H__ */
diff --git a/src/libart_lgpl/art_svp_ops.c b/src/libart_lgpl/art_svp_ops.c
new file mode 100644
index 0000000..57d4b93
--- /dev/null
+++ b/src/libart_lgpl/art_svp_ops.c
@@ -0,0 +1,400 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#define noVERBOSE
+
+/* Vector path set operations, over sorted vpaths. */
+
+#include "config.h"
+#include "art_svp_ops.h"
+
+#include "art_misc.h"
+
+#include "art_svp.h"
+#include "art_vpath.h"
+#include "art_svp_vpath.h"
+#include "art_svp.h"
+#ifdef ART_USE_NEW_INTERSECTOR
+#include "art_svp_intersect.h"
+#else
+#include "art_svp_wind.h"
+#endif
+#include "art_vpath_svp.h"
+
+/* Merge the segments of the two svp's. The resulting svp will share
+   segments with args passed in, so be super-careful with the
+   allocation.  */
+/**
+ * art_svp_merge: Merge the segments of two svp's.
+ * @svp1: One svp to merge.
+ * @svp2: The other svp to merge.
+ *
+ * Merges the segments of two SVP's into a new one. The resulting
+ * #ArtSVP data structure will share the segments of the argument
+ * svp's, so it is probably a good idea to free it shallowly,
+ * especially if the arguments will be freed with art_svp_free().
+ *
+ * Return value: The merged #ArtSVP.
+ **/
+static ArtSVP *
+art_svp_merge (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+  ArtSVP *svp_new;
+  int ix;
+  int ix1, ix2;
+
+  svp_new = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+				 (svp1->n_segs + svp2->n_segs - 1) *
+				 sizeof(ArtSVPSeg));
+  ix1 = 0;
+  ix2 = 0;
+  for (ix = 0; ix < svp1->n_segs + svp2->n_segs; ix++)
+    {
+      if (ix1 < svp1->n_segs &&
+	  (ix2 == svp2->n_segs ||
+	   art_svp_seg_compare (&svp1->segs[ix1], &svp2->segs[ix2]) < 1))
+	svp_new->segs[ix] = svp1->segs[ix1++];
+      else
+	svp_new->segs[ix] = svp2->segs[ix2++];
+    }
+
+  svp_new->n_segs = ix;
+  return svp_new;
+}
+
+#ifdef VERBOSE
+
+#define XOFF 50
+#define YOFF 700
+
+static void
+print_ps_vpath (ArtVpath *vpath)
+{
+  int i;
+
+  printf ("gsave %d %d translate 1 -1 scale\n", XOFF, YOFF);
+  for (i = 0; vpath[i].code != ART_END; i++)
+    {
+      switch (vpath[i].code)
+	{
+	case ART_MOVETO:
+	  printf ("%g %g moveto\n", vpath[i].x, vpath[i].y);
+	  break;
+	case ART_LINETO:
+	  printf ("%g %g lineto\n", vpath[i].x, vpath[i].y);
+	  break;
+	default:
+	  break;
+	}
+    }
+  printf ("stroke grestore showpage\n");
+}
+
+#define DELT 4
+
+static void
+print_ps_svp (ArtSVP *vpath)
+{
+  int i, j;
+
+  printf ("%% begin\n");
+  for (i = 0; i < vpath->n_segs; i++)
+    {
+      printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
+      for (j = 0; j < vpath->segs[i].n_points; j++)
+	{
+	  printf ("%g %g %s\n",
+		  XOFF + vpath->segs[i].points[j].x,
+		  YOFF - vpath->segs[i].points[j].y,
+		  j ? "lineto" : "moveto");
+	}
+      printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
+	      XOFF + vpath->segs[i].points[0].x - DELT,
+	      YOFF - DELT - vpath->segs[i].points[0].y,
+	      XOFF + vpath->segs[i].points[0].x - DELT,
+	      YOFF - vpath->segs[i].points[0].y,
+	      XOFF + vpath->segs[i].points[0].x + DELT,
+	      YOFF - vpath->segs[i].points[0].y,
+	      XOFF + vpath->segs[i].points[0].x + DELT,
+	      YOFF - DELT - vpath->segs[i].points[0].y);
+      printf ("%g %g moveto %g %g lineto %g %g lineto %g %g lineto stroke\n",
+	      XOFF + vpath->segs[i].points[j - 1].x - DELT,
+	      YOFF + DELT - vpath->segs[i].points[j - 1].y,
+	      XOFF + vpath->segs[i].points[j - 1].x - DELT,
+	      YOFF - vpath->segs[i].points[j - 1].y,
+	      XOFF + vpath->segs[i].points[j - 1].x + DELT,
+	      YOFF - vpath->segs[i].points[j - 1].y,
+	      XOFF + vpath->segs[i].points[j - 1].x + DELT,
+	      YOFF + DELT - vpath->segs[i].points[j - 1].y);
+      printf ("stroke\n");
+    }
+
+  printf ("showpage\n");
+}
+#endif
+
+#ifndef ART_USE_NEW_INTERSECTOR
+static ArtSVP *
+art_svp_merge_perturbed (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+  ArtVpath *vpath1, *vpath2;
+  ArtVpath *vpath1_p, *vpath2_p;
+  ArtSVP *svp1_p, *svp2_p;
+  ArtSVP *svp_new;
+
+  vpath1 = art_vpath_from_svp (svp1);
+  vpath1_p = art_vpath_perturb (vpath1);
+  art_free (vpath1);
+  svp1_p = art_svp_from_vpath (vpath1_p);
+  art_free (vpath1_p);
+
+  vpath2 = art_vpath_from_svp (svp2);
+  vpath2_p = art_vpath_perturb (vpath2);
+  art_free (vpath2);
+  svp2_p = art_svp_from_vpath (vpath2_p);
+  art_free (vpath2_p);
+
+  svp_new = art_svp_merge (svp1_p, svp2_p);
+#ifdef VERBOSE
+  print_ps_svp (svp1_p);
+  print_ps_svp (svp2_p);
+  print_ps_svp (svp_new);
+#endif
+  art_free (svp1_p);
+  art_free (svp2_p);
+
+  return svp_new;
+}
+#endif
+
+/* Compute the union of two vector paths.
+
+   Status of this routine:
+
+   Basic correctness: Seems to work.
+
+   Numerical stability: We cheat (adding random perturbation). Thus,
+   it seems very likely that no numerical stability problems will be
+   seen in practice.
+
+   Speed: Would be better if we didn't go to unsorted vector path
+   and back to add the perturbation.
+
+   Precision: The perturbation fuzzes the coordinates slightly. In
+   cases of butting segments, razor thin long holes may appear.
+
+*/
+/**
+ * art_svp_union: Compute the union of two sorted vector paths.
+ * @svp1: One sorted vector path.
+ * @svp2: The other sorted vector path.
+ *
+ * Computes the union of the two argument svp's. Given two svp's with
+ * winding numbers of 0 and 1 everywhere, the resulting winding number
+ * will be 1 where either (or both) of the argument svp's has a
+ * winding number 1, 0 otherwise. The result is newly allocated.
+ *
+ * Currently, this routine has accuracy problems pending the
+ * implementation of the new intersector.
+ *
+ * Return value: The union of @svp1 and @svp2.
+ **/
+ArtSVP *
+art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+  ArtSVP *svp3, *svp_new;
+  ArtSvpWriter *swr;
+
+  svp3 = art_svp_merge (svp1, svp2);
+  swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
+  art_svp_intersector (svp3, swr);
+  svp_new = art_svp_writer_rewind_reap (swr);
+  art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+  return svp_new;
+#else
+  ArtSVP *svp3, *svp4, *svp_new;
+
+  svp3 = art_svp_merge_perturbed (svp1, svp2);
+  svp4 = art_svp_uncross (svp3);
+  art_svp_free (svp3);
+
+  svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_POSITIVE);
+#ifdef VERBOSE
+  print_ps_svp (svp4);
+  print_ps_svp (svp_new);
+#endif
+  art_svp_free (svp4);
+  return svp_new;
+#endif
+}
+
+/* Compute the intersection of two vector paths.
+
+   Status of this routine:
+
+   Basic correctness: Seems to work.
+
+   Numerical stability: We cheat (adding random perturbation). Thus,
+   it seems very likely that no numerical stability problems will be
+   seen in practice.
+
+   Speed: Would be better if we didn't go to unsorted vector path
+   and back to add the perturbation.
+
+   Precision: The perturbation fuzzes the coordinates slightly. In
+   cases of butting segments, razor thin long isolated segments may
+   appear.
+
+*/
+
+/**
+ * art_svp_intersect: Compute the intersection of two sorted vector paths.
+ * @svp1: One sorted vector path.
+ * @svp2: The other sorted vector path.
+ *
+ * Computes the intersection of the two argument svp's. Given two
+ * svp's with winding numbers of 0 and 1 everywhere, the resulting
+ * winding number will be 1 where both of the argument svp's has a
+ * winding number 1, 0 otherwise. The result is newly allocated.
+ *
+ * Currently, this routine has accuracy problems pending the
+ * implementation of the new intersector.
+ *
+ * Return value: The intersection of @svp1 and @svp2.
+ **/
+ArtSVP *
+art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+  ArtSVP *svp3, *svp_new;
+  ArtSvpWriter *swr;
+
+  svp3 = art_svp_merge (svp1, svp2);
+  swr = art_svp_writer_rewind_new (ART_WIND_RULE_INTERSECT);
+  art_svp_intersector (svp3, swr);
+  svp_new = art_svp_writer_rewind_reap (swr);
+  art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+  return svp_new;
+#else
+  ArtSVP *svp3, *svp4, *svp_new;
+
+  svp3 = art_svp_merge_perturbed (svp1, svp2);
+  svp4 = art_svp_uncross (svp3);
+  art_svp_free (svp3);
+
+  svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_INTERSECT);
+  art_svp_free (svp4);
+  return svp_new;
+#endif
+}
+
+/* Compute the symmetric difference of two vector paths.
+
+   Status of this routine:
+
+   Basic correctness: Seems to work.
+
+   Numerical stability: We cheat (adding random perturbation). Thus,
+   it seems very likely that no numerical stability problems will be
+   seen in practice.
+
+   Speed: We could do a lot better by scanning through the svp
+   representations and culling out any segments that are exactly
+   identical. It would also be better if we didn't go to unsorted
+   vector path and back to add the perturbation.
+
+   Precision: Awful. In the case of inputs which are similar (the
+   common case for canvas display), the entire outline is "hairy." In
+   addition, the perturbation fuzzes the coordinates slightly. It can
+   be used as a conservative approximation.
+
+*/
+
+/**
+ * art_svp_diff: Compute the symmetric difference of two sorted vector paths.
+ * @svp1: One sorted vector path.
+ * @svp2: The other sorted vector path.
+ *
+ * Computes the symmetric of the two argument svp's. Given two svp's
+ * with winding numbers of 0 and 1 everywhere, the resulting winding
+ * number will be 1 where either, but not both, of the argument svp's
+ * has a winding number 1, 0 otherwise. The result is newly allocated.
+ *
+ * Currently, this routine has accuracy problems pending the
+ * implementation of the new intersector.
+ *
+ * Return value: The symmetric difference of @svp1 and @svp2.
+ **/
+ArtSVP *
+art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+  ArtSVP *svp3, *svp_new;
+  ArtSvpWriter *swr;
+
+  svp3 = art_svp_merge (svp1, svp2);
+  swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
+  art_svp_intersector (svp3, swr);
+  svp_new = art_svp_writer_rewind_reap (swr);
+  art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+  return svp_new;
+#else
+  ArtSVP *svp3, *svp4, *svp_new;
+
+  svp3 = art_svp_merge_perturbed (svp1, svp2);
+  svp4 = art_svp_uncross (svp3);
+  art_svp_free (svp3);
+
+  svp_new = art_svp_rewind_uncrossed (svp4, ART_WIND_RULE_ODDEVEN);
+  art_svp_free (svp4);
+  return svp_new;
+#endif
+}
+
+#ifdef ART_USE_NEW_INTERSECTOR
+ArtSVP *
+art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2)
+{
+  ArtSVP *svp2_mod;
+  ArtSVP *svp3, *svp_new;
+  ArtSvpWriter *swr;
+  int i;
+
+  svp2_mod = (ArtSVP *) svp2; /* get rid of the const for a while */
+
+  /* First invert svp2 to "turn it inside out" */
+  for (i = 0; i < svp2_mod->n_segs; i++)
+    svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
+
+  svp3 = art_svp_merge (svp1, svp2_mod);
+  swr = art_svp_writer_rewind_new (ART_WIND_RULE_POSITIVE);
+  art_svp_intersector (svp3, swr);
+  svp_new = art_svp_writer_rewind_reap (swr);
+  art_free (svp3); /* shallow free because svp3 contains shared segments */
+
+  /* Flip svp2 back to its original state */
+  for (i = 0; i < svp2_mod->n_segs; i++)
+    svp2_mod->segs[i].dir = !svp2_mod->segs[i].dir;
+
+  return svp_new;
+}
+#endif /* ART_USE_NEW_INTERSECTOR */
diff --git a/src/libart_lgpl/art_svp_ops.h b/src/libart_lgpl/art_svp_ops.h
new file mode 100644
index 0000000..c894583
--- /dev/null
+++ b/src/libart_lgpl/art_svp_ops.h
@@ -0,0 +1,43 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_OPS_H__
+#define __ART_SVP_OPS_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Vector path set operations, over sorted vpaths. */
+
+ArtSVP *art_svp_union (const ArtSVP *svp1, const ArtSVP *svp2);
+ArtSVP *art_svp_intersect (const ArtSVP *svp1, const ArtSVP *svp2);
+ArtSVP *art_svp_diff (const ArtSVP *svp1, const ArtSVP *svp2);
+ArtSVP *art_svp_minus (const ArtSVP *svp1, const ArtSVP *svp2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_OPS_H__ */
diff --git a/src/libart_lgpl/art_svp_point.c b/src/libart_lgpl/art_svp_point.c
new file mode 100644
index 0000000..0a92219
--- /dev/null
+++ b/src/libart_lgpl/art_svp_point.c
@@ -0,0 +1,143 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_svp_point.h"
+
+#include <math.h>
+#include "art_misc.h"
+
+#include "art_svp.h"
+
+/* Determine whether a point is inside, or near, an svp. */
+
+/* return winding number of point wrt svp */
+/**
+ * art_svp_point_wind: Determine winding number of a point with respect to svp.
+ * @svp: The svp.
+ * @x: The X coordinate of the point.
+ * @y: The Y coordinate of the point.
+ *
+ * Determine the winding number of the point @x, @y with respect to @svp.
+ *
+ * Return value: the winding number.
+ **/
+int
+art_svp_point_wind (ArtSVP *svp, double x, double y)
+{
+  int i, j;
+  int wind = 0;
+
+  for (i = 0; i < svp->n_segs; i++)
+    {
+      ArtSVPSeg *seg = &svp->segs[i];
+
+      if (seg->bbox.y0 > y)
+	break;
+
+      if (seg->bbox.y1 > y)
+	{
+	  if (seg->bbox.x1 < x)
+	    wind += seg->dir ? 1 : -1;
+	  else if (seg->bbox.x0 <= x)
+	    {
+	      double x0, y0, x1, y1, dx, dy;
+
+	      for (j = 0; j < seg->n_points - 1; j++)
+		{
+		  if (seg->points[j + 1].y > y)
+		    break;
+		}
+	      x0 = seg->points[j].x;
+	      y0 = seg->points[j].y;
+	      x1 = seg->points[j + 1].x;
+	      y1 = seg->points[j + 1].y;
+
+	      dx = x1 - x0;
+	      dy = y1 - y0;
+	      if ((x - x0) * dy > (y - y0) * dx)
+		wind += seg->dir ? 1 : -1;
+	    }
+	}
+    }
+
+  return wind;
+}
+
+/**
+ * art_svp_point_dist: Determine distance between point and svp.
+ * @svp: The svp.
+ * @x: The X coordinate of the point.
+ * @y: The Y coordinate of the point.
+ *
+ * Determines the distance of the point @x, @y to the closest edge in
+ * @svp. A large number is returned if @svp is empty.
+ *
+ * Return value: the distance.
+ **/
+double
+art_svp_point_dist (ArtSVP *svp, double x, double y)
+{
+  int i, j;
+  double dist_sq;
+  double best_sq = -1;
+
+  for (i = 0; i < svp->n_segs; i++)
+    {
+      ArtSVPSeg *seg = &svp->segs[i];
+      for (j = 0; j < seg->n_points - 1; j++)
+	{
+	  double x0 = seg->points[j].x;
+	  double y0 = seg->points[j].y;
+	  double x1 = seg->points[j + 1].x;
+	  double y1 = seg->points[j + 1].y;
+
+	  double dx = x1 - x0;
+	  double dy = y1 - y0;
+
+	  double dxx0 = x - x0;
+	  double dyy0 = y - y0;
+
+	  double dot = dxx0 * dx + dyy0 * dy;
+
+	  if (dot < 0)
+	    dist_sq = dxx0 * dxx0 + dyy0 * dyy0;
+	  else
+	    {
+	      double rr = dx * dx + dy * dy;
+
+	      if (dot > rr)
+		dist_sq = (x - x1) * (x - x1) + (y - y1) * (y - y1);
+	      else
+		{
+		  double perp = (y - y0) * dx - (x - x0) * dy;
+
+		  dist_sq = perp * perp / rr;
+		}
+	    }
+	  if (best_sq < 0 || dist_sq < best_sq)
+	    best_sq = dist_sq;
+	}
+    }
+
+  if (best_sq >= 0)
+    return sqrt (best_sq);
+  else
+    return 1e12;
+}
+
diff --git a/src/libart_lgpl/art_svp_point.h b/src/libart_lgpl/art_svp_point.h
new file mode 100644
index 0000000..8da28c4
--- /dev/null
+++ b/src/libart_lgpl/art_svp_point.h
@@ -0,0 +1,48 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_POINT_H__
+#define __ART_SVP_POINT_H__
+
+/* Determine whether a point is inside, or near, an svp. */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int
+art_svp_point_wind (ArtSVP *svp, double x, double y);
+
+double
+art_svp_point_dist (ArtSVP *svp, double x, double y);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_H__ */
+
+
+
+
diff --git a/src/libart_lgpl/art_svp_render_aa.c b/src/libart_lgpl/art_svp_render_aa.c
new file mode 100644
index 0000000..fdce8ca
--- /dev/null
+++ b/src/libart_lgpl/art_svp_render_aa.c
@@ -0,0 +1,462 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+#include "config.h"
+#include "art_svp_render_aa.h"
+
+#include <math.h>
+#include <string.h> /* for memmove */
+#include "art_misc.h"
+
+#include "art_rect.h"
+#include "art_svp.h"
+
+#include <stdio.h>
+
+typedef double artfloat;
+
+struct _ArtSVPRenderAAIter {
+  const ArtSVP *svp;
+  int x0, x1;
+  int y;
+  int seg_ix;
+
+  int *active_segs;
+  int n_active_segs;
+  int *cursor;
+  artfloat *seg_x;
+  artfloat *seg_dx;
+
+  ArtSVPRenderAAStep *steps;
+};
+
+static void
+art_svp_render_insert_active (int i, int *active_segs, int n_active_segs,
+			      artfloat *seg_x, artfloat *seg_dx)
+{
+  int j;
+  artfloat x;
+  int tmp1, tmp2;
+
+  /* this is a cheap hack to get ^'s sorted correctly */
+  x = seg_x[i] + 0.001 * seg_dx[i];
+  for (j = 0; j < n_active_segs && seg_x[active_segs[j]] < x; j++);
+
+  tmp1 = i;
+  while (j < n_active_segs)
+    {
+      tmp2 = active_segs[j];
+      active_segs[j] = tmp1;
+      tmp1 = tmp2;
+      j++;
+    }
+  active_segs[j] = tmp1;
+}
+
+static void
+art_svp_render_delete_active (int *active_segs, int j, int n_active_segs)
+{
+  int k;
+
+  for (k = j; k < n_active_segs; k++)
+    active_segs[k] = active_segs[k + 1];
+}
+
+#define EPSILON 1e-6
+
+/* Render the sorted vector path in the given rectangle, antialiased.
+
+   This interface uses a callback for the actual pixel rendering. The
+   callback is called y1 - y0 times (once for each scan line). The y
+   coordinate is given as an argument for convenience (it could be
+   stored in the callback's private data and incremented on each
+   call).
+
+   The rendered polygon is represented in a semi-runlength format: a
+   start value and a sequence of "steps". Each step has an x
+   coordinate and a value delta. The resulting value at position x is
+   equal to the sum of the start value and all step delta values for
+   which the step x coordinate is less than or equal to x. An
+   efficient algorithm will traverse the steps left to right, keeping
+   a running sum.
+
+   All x coordinates in the steps are guaranteed to be x0 <= x < x1.
+   (This guarantee is a change from the gfonted vpaar renderer, and is
+   designed to simplify the callback).
+
+   There is now a further guarantee that no two steps will have the
+   same x value. This may allow for further speedup and simplification
+   of renderers.
+
+   The value 0x8000 represents 0% coverage by the polygon, while
+   0xff8000 represents 100% coverage. This format is designed so that
+   >> 16 results in a standard 0x00..0xff value range, with nice
+   rounding.
+
+   Status of this routine:
+
+   Basic correctness: OK
+
+   Numerical stability: pretty good, although probably not
+   bulletproof.
+
+   Speed: Needs more aggressive culling of bounding boxes.  Can
+   probably speed up the [x0,x1) clipping of step values.  Can do more
+   of the step calculation in fixed point.
+
+   Precision: No known problems, although it should be tested
+   thoroughly, especially for symmetry.
+
+*/
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter (const ArtSVP *svp,
+			int x0, int y0, int x1, int y1)
+{
+  ArtSVPRenderAAIter *iter = art_new (ArtSVPRenderAAIter, 1);
+
+  iter->svp = svp;
+  iter->y = y0;
+  iter->x0 = x0;
+  iter->x1 = x1;
+  iter->seg_ix = 0;
+
+  iter->active_segs = art_new (int, svp->n_segs);
+  iter->cursor = art_new (int, svp->n_segs);
+  iter->seg_x = art_new (artfloat, svp->n_segs);
+  iter->seg_dx = art_new (artfloat, svp->n_segs);
+  iter->steps = art_new (ArtSVPRenderAAStep, x1 - x0);
+  iter->n_active_segs = 0;
+
+  return iter;
+}
+
+#define ADD_STEP(xpos, xdelta)                          \
+  /* stereotype code fragment for adding a step */      \
+  if (n_steps == 0 || steps[n_steps - 1].x < xpos)      \
+    {                                                   \
+      sx = n_steps;                                     \
+      steps[sx].x = xpos;                               \
+      steps[sx].delta = xdelta;                         \
+      n_steps++;                                        \
+    }                                                   \
+  else                                                  \
+    {                                                   \
+      for (sx = n_steps; sx > 0; sx--)                  \
+	{                                               \
+	  if (steps[sx - 1].x == xpos)                  \
+	    {                                           \
+	      steps[sx - 1].delta += xdelta;            \
+	      sx = n_steps;                             \
+	      break;                                    \
+	    }                                           \
+	  else if (steps[sx - 1].x < xpos)              \
+	    {                                           \
+	      break;                                    \
+	    }                                           \
+	}                                               \
+      if (sx < n_steps)                                 \
+	{                                               \
+	  memmove (&steps[sx + 1], &steps[sx],          \
+		   (n_steps - sx) * sizeof(steps[0]));  \
+	  steps[sx].x = xpos;                           \
+	  steps[sx].delta = xdelta;                     \
+	  n_steps++;                                    \
+	}                                               \
+    }
+
+void
+art_svp_render_aa_iter_step (ArtSVPRenderAAIter *iter, int *p_start,
+			     ArtSVPRenderAAStep **p_steps, int *p_n_steps)
+{
+  const ArtSVP *svp = iter->svp;
+  int *active_segs = iter->active_segs;
+  int n_active_segs = iter->n_active_segs;
+  int *cursor = iter->cursor;
+  artfloat *seg_x = iter->seg_x;
+  artfloat *seg_dx = iter->seg_dx;
+  int i = iter->seg_ix;
+  int j;
+  int x0 = iter->x0;
+  int x1 = iter->x1;
+  int y = iter->y;
+  int seg_index;
+
+  int x;
+  ArtSVPRenderAAStep *steps = iter->steps;
+  int n_steps;
+  artfloat y_top, y_bot;
+  artfloat x_top, x_bot;
+  artfloat x_min, x_max;
+  int ix_min, ix_max;
+  artfloat delta; /* delta should be int too? */
+  int last, this;
+  int xdelta;
+  artfloat rslope, drslope;
+  int start;
+  const ArtSVPSeg *seg;
+  int curs;
+  artfloat dy;
+
+  int sx;
+
+  /* insert new active segments */
+  for (; i < svp->n_segs && svp->segs[i].bbox.y0 < y + 1; i++)
+    {
+      if (svp->segs[i].bbox.y1 > y &&
+	  svp->segs[i].bbox.x0 < x1)
+	{
+	  seg = &svp->segs[i];
+	  /* move cursor to topmost vector which overlaps [y,y+1) */
+	  for (curs = 0; seg->points[curs + 1].y < y; curs++);
+	  cursor[i] = curs;
+	  dy = seg->points[curs + 1].y - seg->points[curs].y;
+	  if (fabs (dy) >= EPSILON)
+	    seg_dx[i] = (seg->points[curs + 1].x - seg->points[curs].x) /
+	      dy;
+	  else
+	    seg_dx[i] = 1e12;
+	  seg_x[i] = seg->points[curs].x +
+	    (y - seg->points[curs].y) * seg_dx[i];
+	  art_svp_render_insert_active (i, active_segs, n_active_segs++,
+					seg_x, seg_dx);
+	}
+    }
+
+  n_steps = 0;
+
+  /* render the runlengths, advancing and deleting as we go */
+  start = 0x8000;
+
+  for (j = 0; j < n_active_segs; j++)
+    {
+      seg_index = active_segs[j];
+      seg = &svp->segs[seg_index];
+      curs = cursor[seg_index];
+      while (curs != seg->n_points - 1 &&
+	     seg->points[curs].y < y + 1)
+	{
+	  y_top = y;
+	  if (y_top < seg->points[curs].y)
+	    y_top = seg->points[curs].y;
+	  y_bot = y + 1;
+	  if (y_bot > seg->points[curs + 1].y)
+	    y_bot = seg->points[curs + 1].y;
+	  if (y_top != y_bot) {
+	    delta = (seg->dir ? 16711680.0 : -16711680.0) *
+	      (y_bot - y_top);
+	    x_top = seg_x[seg_index] + (y_top - y) * seg_dx[seg_index];
+	    x_bot = seg_x[seg_index] + (y_bot - y) * seg_dx[seg_index];
+	    if (x_top < x_bot)
+	      {
+		x_min = x_top;
+		x_max = x_bot;
+	      }
+	    else
+	      {
+		x_min = x_bot;
+		x_max = x_top;
+	      }
+	    ix_min = floor (x_min);
+	    ix_max = floor (x_max);
+	    if (ix_min >= x1)
+	      {
+		/* skip; it starts to the right of the render region */
+	      }
+	    else if (ix_max < x0)
+	      /* it ends to the left of the render region */
+	      start += delta;
+	    else if (ix_min == ix_max)
+	      {
+		/* case 1, antialias a single pixel */
+		xdelta = (ix_min + 1 - (x_min + x_max) * 0.5) * delta;
+
+		ADD_STEP(ix_min, xdelta)
+
+		if (ix_min + 1 < x1)
+		  {
+		    xdelta = delta - xdelta;
+
+		    ADD_STEP(ix_min + 1, xdelta)
+		  }
+	      }
+	    else
+	      {
+		/* case 2, antialias a run */
+		rslope = 1.0 / fabs (seg_dx[seg_index]);
+		drslope = delta * rslope;
+		last =
+		  drslope * 0.5 *
+		  (ix_min + 1 - x_min) * (ix_min + 1 - x_min);
+		xdelta = last;
+		if (ix_min >= x0)
+		  {
+		    ADD_STEP(ix_min, xdelta)
+
+		    x = ix_min + 1;
+		  }
+		else
+		  {
+		    start += last;
+		    x = x0;
+		  }
+		if (ix_max > x1)
+		  ix_max = x1;
+		for (; x < ix_max; x++)
+		  {
+		    this = (seg->dir ? 16711680.0 : -16711680.0) * rslope *
+		      (x + 0.5 - x_min);
+		    xdelta = this - last;
+		    last = this;
+
+		    ADD_STEP(x, xdelta)
+		  }
+		if (x < x1)
+		  {
+		    this =
+		      delta * (1 - 0.5 *
+			       (x_max - ix_max) * (x_max - ix_max) *
+			       rslope);
+		    xdelta = this - last;
+		    last = this;
+
+		    ADD_STEP(x, xdelta)
+
+		    if (x + 1 < x1)
+		      {
+			xdelta = delta - last;
+
+			ADD_STEP(x + 1, xdelta)
+		      }
+		  }
+	      }
+	  }
+	  curs++;
+	  if (curs != seg->n_points - 1 &&
+	      seg->points[curs].y < y + 1)
+	    {
+	      dy = seg->points[curs + 1].y - seg->points[curs].y;
+	      if (fabs (dy) >= EPSILON)
+		seg_dx[seg_index] = (seg->points[curs + 1].x -
+				     seg->points[curs].x) / dy;
+	      else
+		seg_dx[seg_index] = 1e12;
+	      seg_x[seg_index] = seg->points[curs].x +
+		(y - seg->points[curs].y) * seg_dx[seg_index];
+	    }
+	  /* break here, instead of duplicating predicate in while? */
+	}
+      if (seg->points[curs].y >= y + 1)
+	{
+	  curs--;
+	  cursor[seg_index] = curs;
+	  seg_x[seg_index] += seg_dx[seg_index];
+	}
+      else
+	{
+	  art_svp_render_delete_active (active_segs, j--,
+					--n_active_segs);
+	}
+    }
+
+  *p_start = start;
+  *p_steps = steps;
+  *p_n_steps = n_steps;
+
+  iter->seg_ix = i;
+  iter->n_active_segs = n_active_segs;
+  iter->y++;
+}
+
+void
+art_svp_render_aa_iter_done (ArtSVPRenderAAIter *iter)
+{
+  art_free (iter->steps);
+
+  art_free (iter->seg_dx);
+  art_free (iter->seg_x);
+  art_free (iter->cursor);
+  art_free (iter->active_segs);
+  art_free (iter);
+}
+
+/**
+ * art_svp_render_aa: Render SVP antialiased.
+ * @svp: The #ArtSVP to render.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @callback: The callback which actually paints the pixels.
+ * @callback_data: Private data for @callback.
+ *
+ * Renders the sorted vector path in the given rectangle, antialiased.
+ *
+ * This interface uses a callback for the actual pixel rendering. The
+ * callback is called @y1 - @y0 times (once for each scan line). The y
+ * coordinate is given as an argument for convenience (it could be
+ * stored in the callback's private data and incremented on each
+ * call).
+ *
+ * The rendered polygon is represented in a semi-runlength format: a
+ * start value and a sequence of "steps". Each step has an x
+ * coordinate and a value delta. The resulting value at position x is
+ * equal to the sum of the start value and all step delta values for
+ * which the step x coordinate is less than or equal to x. An
+ * efficient algorithm will traverse the steps left to right, keeping
+ * a running sum.
+ *
+ * All x coordinates in the steps are guaranteed to be @x0 <= x < @x1.
+ * (This guarantee is a change from the gfonted vpaar renderer from
+ * which this routine is derived, and is designed to simplify the
+ * callback).
+ *
+ * The value 0x8000 represents 0% coverage by the polygon, while
+ * 0xff8000 represents 100% coverage. This format is designed so that
+ * >> 16 results in a standard 0x00..0xff value range, with nice
+ * rounding.
+ *
+ **/
+void
+art_svp_render_aa (const ArtSVP *svp,
+		   int x0, int y0, int x1, int y1,
+		   void (*callback) (void *callback_data,
+				     int y,
+				     int start,
+				     ArtSVPRenderAAStep *steps, int n_steps),
+		   void *callback_data)
+{
+  ArtSVPRenderAAIter *iter;
+  int y;
+  int start;
+  ArtSVPRenderAAStep *steps;
+  int n_steps;
+
+  iter = art_svp_render_aa_iter (svp, x0, y0, x1, y1);
+
+
+  for (y = y0; y < y1; y++)
+    {
+      art_svp_render_aa_iter_step (iter, &start, &steps, &n_steps);
+      (*callback) (callback_data, y, start, steps, n_steps);
+    }
+
+  art_svp_render_aa_iter_done (iter);
+}
diff --git a/src/libart_lgpl/art_svp_render_aa.h b/src/libart_lgpl/art_svp_render_aa.h
new file mode 100644
index 0000000..52571f0
--- /dev/null
+++ b/src/libart_lgpl/art_svp_render_aa.h
@@ -0,0 +1,66 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_RENDER_AA_H__
+#define __ART_SVP_RENDER_AA_H__
+
+/* The spiffy antialiased renderer for sorted vector paths. */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtSVPRenderAAStep ArtSVPRenderAAStep;
+typedef struct _ArtSVPRenderAAIter ArtSVPRenderAAIter;
+
+struct _ArtSVPRenderAAStep {
+  int x;
+  int delta; /* stored with 16 fractional bits */
+};
+
+ArtSVPRenderAAIter *
+art_svp_render_aa_iter (const ArtSVP *svp,
+			int x0, int y0, int x1, int y1);
+
+void
+art_svp_render_aa_iter_step (ArtSVPRenderAAIter *iter, int *p_start,
+			     ArtSVPRenderAAStep **p_steps, int *p_n_steps);
+
+void
+art_svp_render_aa_iter_done (ArtSVPRenderAAIter *iter);
+
+void
+art_svp_render_aa (const ArtSVP *svp,
+		   int x0, int y0, int x1, int y1,
+		   void (*callback) (void *callback_data,
+				     int y,
+				     int start,
+				     ArtSVPRenderAAStep *steps, int n_steps),
+		   void *callback_data);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_RENDER_AA_H__ */
diff --git a/src/libart_lgpl/art_svp_vpath.c b/src/libart_lgpl/art_svp_vpath.c
new file mode 100644
index 0000000..0c55511
--- /dev/null
+++ b/src/libart_lgpl/art_svp_vpath.c
@@ -0,0 +1,214 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Sort vector paths into sorted vector paths */
+
+#include "config.h"
+#include "art_svp_vpath.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "art_misc.h"
+
+#include "art_vpath.h"
+#include "art_svp.h"
+
+
+/* reverse a list of points in place */
+static void
+reverse_points (ArtPoint *points, int n_points)
+{
+  int i;
+  ArtPoint tmp_p;
+
+  for (i = 0; i < (n_points >> 1); i++)
+    {
+      tmp_p = points[i];
+      points[i] = points[n_points - (i + 1)];
+      points[n_points - (i + 1)] = tmp_p;
+    }
+}
+
+/**
+ * art_svp_from_vpath: Convert a vpath to a sorted vector path.
+ * @vpath: #ArtVPath to convert.
+ *
+ * Converts a vector path into sorted vector path form. The svp form is
+ * more efficient for rendering and other vector operations.
+ *
+ * Basically, the implementation is to traverse the vector path,
+ * generating a new segment for each "run" of points in the vector
+ * path with monotonically increasing Y values. All the resulting
+ * values are then sorted.
+ *
+ * Note: I'm not sure that the sorting rule is correct with respect
+ * to numerical stability issues.
+ *
+ * Return value: Resulting sorted vector path.
+ **/
+ArtSVP *
+art_svp_from_vpath (ArtVpath *vpath)
+{
+  int n_segs, n_segs_max;
+  ArtSVP *svp;
+  int dir;
+  int new_dir;
+  int i;
+  ArtPoint *points;
+  int n_points, n_points_max;
+  double x, y;
+  double x_min, x_max;
+
+  n_segs = 0;
+  n_segs_max = 16;
+  svp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+			     (n_segs_max - 1) * sizeof(ArtSVPSeg));
+
+  dir = 0;
+  n_points = 0;
+  n_points_max = 0;
+  points = NULL;
+  i = 0;
+
+  x = y = 0; /* unnecessary, given "first code must not be LINETO" invariant,
+		but it makes gcc -Wall -ansi -pedantic happier */
+  x_min = x_max = 0; /* same */
+
+  while (vpath[i].code != ART_END) {
+    if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN)
+      {
+	if (points != NULL && n_points >= 2)
+	  {
+	    if (n_segs == n_segs_max)
+	      {
+		n_segs_max <<= 1;
+		svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+					     (n_segs_max - 1) *
+					     sizeof(ArtSVPSeg));
+	      }
+	    svp->segs[n_segs].n_points = n_points;
+	    svp->segs[n_segs].dir = (dir > 0);
+	    if (dir < 0)
+	      reverse_points (points, n_points);
+	    svp->segs[n_segs].points = points;
+	    svp->segs[n_segs].bbox.x0 = x_min;
+	    svp->segs[n_segs].bbox.x1 = x_max;
+	    svp->segs[n_segs].bbox.y0 = points[0].y;
+	    svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+	    n_segs++;
+	    points = NULL;
+	  }
+
+	if (points == NULL)
+	  {
+	    n_points_max = 4;
+	    points = art_new (ArtPoint, n_points_max);
+	  }
+
+	n_points = 1;
+	points[0].x = x = vpath[i].x;
+	points[0].y = y = vpath[i].y;
+	x_min = x;
+	x_max = x;
+	dir = 0;
+      }
+    else /* must be LINETO */
+      {
+	new_dir = (vpath[i].y > y ||
+		   (vpath[i].y == y && vpath[i].x > x)) ? 1 : -1;
+	if (dir && dir != new_dir)
+	  {
+	    /* new segment */
+	    x = points[n_points - 1].x;
+	    y = points[n_points - 1].y;
+	    if (n_segs == n_segs_max)
+	      {
+		n_segs_max <<= 1;
+		svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+					     (n_segs_max - 1) *
+					     sizeof(ArtSVPSeg));
+	      }
+	    svp->segs[n_segs].n_points = n_points;
+	    svp->segs[n_segs].dir = (dir > 0);
+	    if (dir < 0)
+	      reverse_points (points, n_points);
+	    svp->segs[n_segs].points = points;
+	    svp->segs[n_segs].bbox.x0 = x_min;
+	    svp->segs[n_segs].bbox.x1 = x_max;
+	    svp->segs[n_segs].bbox.y0 = points[0].y;
+	    svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+	    n_segs++;
+
+	    n_points = 1;
+	    n_points_max = 4;
+	    points = art_new (ArtPoint, n_points_max);
+	    points[0].x = x;
+	    points[0].y = y;
+	    x_min = x;
+	    x_max = x;
+	  }
+
+	if (points != NULL)
+	  {
+	    if (n_points == n_points_max)
+	      art_expand (points, ArtPoint, n_points_max);
+	    points[n_points].x = x = vpath[i].x;
+	    points[n_points].y = y = vpath[i].y;
+	    if (x < x_min) x_min = x;
+	    else if (x > x_max) x_max = x;
+	    n_points++;
+	  }
+	dir = new_dir;
+      }
+    i++;
+  }
+
+  if (points != NULL)
+    {
+      if (n_points >= 2)
+	{
+	  if (n_segs == n_segs_max)
+	    {
+	      n_segs_max <<= 1;
+	      svp = (ArtSVP *)art_realloc (svp, sizeof(ArtSVP) +
+					   (n_segs_max - 1) *
+					   sizeof(ArtSVPSeg));
+	    }
+	  svp->segs[n_segs].n_points = n_points;
+	  svp->segs[n_segs].dir = (dir > 0);
+	  if (dir < 0)
+	    reverse_points (points, n_points);
+	  svp->segs[n_segs].points = points;
+	  svp->segs[n_segs].bbox.x0 = x_min;
+	  svp->segs[n_segs].bbox.x1 = x_max;
+	  svp->segs[n_segs].bbox.y0 = points[0].y;
+	  svp->segs[n_segs].bbox.y1 = points[n_points - 1].y;
+	  n_segs++;
+	}
+      else
+	art_free (points);
+    }
+
+  svp->n_segs = n_segs;
+
+  qsort (&svp->segs, n_segs, sizeof (ArtSVPSeg), art_svp_seg_compare);
+
+  return svp;
+}
+
diff --git a/src/libart_lgpl/art_svp_vpath.h b/src/libart_lgpl/art_svp_vpath.h
new file mode 100644
index 0000000..19e0c45
--- /dev/null
+++ b/src/libart_lgpl/art_svp_vpath.h
@@ -0,0 +1,43 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_VPATH_H__
+#define __ART_SVP_VPATH_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#include "art_vpath.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+#endif
+
+/* Sort vector paths into sorted vector paths. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtSVP *
+art_svp_from_vpath (ArtVpath *vpath);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_VPATH_H__ */
diff --git a/src/libart_lgpl/art_svp_vpath_stroke.c b/src/libart_lgpl/art_svp_vpath_stroke.c
new file mode 100644
index 0000000..892c426
--- /dev/null
+++ b/src/libart_lgpl/art_svp_vpath_stroke.c
@@ -0,0 +1,738 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "config.h"
+#include "art_svp_vpath_stroke.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "art_misc.h"
+
+#include "art_vpath.h"
+#include "art_svp.h"
+#ifdef ART_USE_NEW_INTERSECTOR
+#include "art_svp_intersect.h"
+#else
+#include "art_svp_wind.h"
+#endif
+#include "art_svp_vpath.h"
+
+#define EPSILON 1e-6
+#define EPSILON_2 1e-12
+
+#define yes_OPTIMIZE_INNER
+
+/* Render an arc segment starting at (xc + x0, yc + y0) to (xc + x1,
+   yc + y1), centered at (xc, yc), and with given radius. Both x0^2 +
+   y0^2 and x1^2 + y1^2 should be equal to radius^2.
+
+   A positive value of radius means curve to the left, negative means
+   curve to the right.
+*/
+static void
+art_svp_vpath_stroke_arc (ArtVpath **p_vpath, int *pn, int *pn_max,
+			  double xc, double yc,
+			  double x0, double y0,
+			  double x1, double y1,
+			  double radius,
+			  double flatness)
+{
+  double theta;
+  double th_0, th_1;
+  int n_pts;
+  int i;
+  double aradius;
+
+  aradius = fabs (radius);
+  theta = 2 * M_SQRT2 * sqrt (flatness / aradius);
+  th_0 = atan2 (y0, x0);
+  th_1 = atan2 (y1, x1);
+  if (radius > 0)
+    {
+      /* curve to the left */
+      if (th_0 < th_1) th_0 += M_PI * 2;
+      n_pts = ceil ((th_0 - th_1) / theta);
+    }
+  else
+    {
+      /* curve to the right */
+      if (th_1 < th_0) th_1 += M_PI * 2;
+      n_pts = ceil ((th_1 - th_0) / theta);
+    }
+#ifdef VERBOSE
+  printf ("start %f %f; th_0 = %f, th_1 = %f, r = %f, theta = %f\n", x0, y0, th_0, th_1, radius, theta);
+#endif
+  art_vpath_add_point (p_vpath, pn, pn_max,
+		       ART_LINETO, xc + x0, yc + y0);
+  for (i = 1; i < n_pts; i++)
+    {
+      theta = th_0 + (th_1 - th_0) * i / n_pts;
+      art_vpath_add_point (p_vpath, pn, pn_max,
+			   ART_LINETO, xc + cos (theta) * aradius,
+			   yc + sin (theta) * aradius);
+#ifdef VERBOSE
+      printf ("mid %f %f\n", cos (theta) * radius, sin (theta) * radius);
+#endif
+    }
+  art_vpath_add_point (p_vpath, pn, pn_max,
+		       ART_LINETO, xc + x1, yc + y1);
+#ifdef VERBOSE
+  printf ("end %f %f\n", x1, y1);
+#endif
+}
+
+/* Assume that forw and rev are at point i0. Bring them to i1,
+   joining with the vector i1 - i2.
+
+   This used to be true, but isn't now that the stroke_raw code is
+   filtering out (near)zero length vectors: {It so happens that all
+   invocations of this function maintain the precondition i1 = i0 + 1,
+   so we could decrease the number of arguments by one. We haven't
+   done that here, though.}
+
+   forw is to the line's right and rev is to its left.
+
+   Precondition: no zero-length vectors, otherwise a divide by
+   zero will happen.  */
+static void
+render_seg (ArtVpath **p_forw, int *pn_forw, int *pn_forw_max,
+	    ArtVpath **p_rev, int *pn_rev, int *pn_rev_max,
+	    ArtVpath *vpath, int i0, int i1, int i2,
+	    ArtPathStrokeJoinType join,
+	    double line_width, double miter_limit, double flatness)
+{
+  double dx0, dy0;
+  double dx1, dy1;
+  double dlx0, dly0;
+  double dlx1, dly1;
+  double dmx, dmy;
+  double dmr2;
+  double scale;
+  double cross;
+
+#ifdef VERBOSE
+  printf ("join style = %d\n", join);
+#endif
+
+  /* The vectors of the lines from i0 to i1 and i1 to i2. */
+  dx0 = vpath[i1].x - vpath[i0].x;
+  dy0 = vpath[i1].y - vpath[i0].y;
+
+  dx1 = vpath[i2].x - vpath[i1].x;
+  dy1 = vpath[i2].y - vpath[i1].y;
+
+  /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+     90 degrees, and scaled to the length of line_width. */
+  scale = line_width / sqrt (dx0 * dx0 + dy0 * dy0);
+  dlx0 = dy0 * scale;
+  dly0 = -dx0 * scale;
+
+  /* Set dl[xy]1 to the vector from i1 to i2, rotated counterclockwise
+     90 degrees, and scaled to the length of line_width. */
+  scale = line_width / sqrt (dx1 * dx1 + dy1 * dy1);
+  dlx1 = dy1 * scale;
+  dly1 = -dx1 * scale;
+
+#ifdef VERBOSE
+  printf ("%% render_seg: (%g, %g) - (%g, %g) - (%g, %g)\n",
+	  vpath[i0].x, vpath[i0].y,
+	  vpath[i1].x, vpath[i1].y,
+	  vpath[i2].x, vpath[i2].y);
+
+  printf ("%% render_seg: d[xy]0 = (%g, %g), dl[xy]0 = (%g, %g)\n",
+	  dx0, dy0, dlx0, dly0);
+
+  printf ("%% render_seg: d[xy]1 = (%g, %g), dl[xy]1 = (%g, %g)\n",
+	  dx1, dy1, dlx1, dly1);
+#endif
+
+  /* now, forw's last point is expected to be colinear along d[xy]0
+     to point i0 - dl[xy]0, and rev with i0 + dl[xy]0. */
+
+  /* positive for positive area (i.e. left turn) */
+  cross = dx1 * dy0 - dx0 * dy1;
+
+  dmx = (dlx0 + dlx1) * 0.5;
+  dmy = (dly0 + dly1) * 0.5;
+  dmr2 = dmx * dmx + dmy * dmy;
+
+  if (join == ART_PATH_STROKE_JOIN_MITER &&
+      dmr2 * miter_limit * miter_limit < line_width * line_width)
+    join = ART_PATH_STROKE_JOIN_BEVEL;
+
+  /* the case when dmr2 is zero or very small bothers me
+     (i.e. near a 180 degree angle)
+     ALEX: So, we avoid the optimization when dmr2 is very small. This should
+     be safe since dmx/y is only used in optimization and in MITER case, and MITER
+     should be converted to BEVEL when dmr2 is very small. */
+  if (dmr2 > EPSILON_2)
+    {
+      scale = line_width * line_width / dmr2;
+      dmx *= scale;
+      dmy *= scale;
+    }
+
+  if (cross * cross < EPSILON_2 && dx0 * dx1 + dy0 * dy1 >= 0)
+    {
+      /* going straight */
+#ifdef VERBOSE
+      printf ("%% render_seg: straight\n");
+#endif
+      art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+		       ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+      art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+		       ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+    }
+  else if (cross > 0)
+    {
+      /* left turn, forw is outside and rev is inside */
+
+#ifdef VERBOSE
+      printf ("%% render_seg: left\n");
+#endif
+      if (
+#ifdef NO_OPTIMIZE_INNER
+	  0 &&
+#endif
+	  (dmr2 > EPSILON_2) &&
+	  /* check that i1 + dm[xy] is inside i0-i1 rectangle */
+	  (dx0 + dmx) * dx0 + (dy0 + dmy) * dy0 > 0 &&
+	  /* and that i1 + dm[xy] is inside i1-i2 rectangle */
+	  ((dx1 - dmx) * dx1 + (dy1 - dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+	  &&
+	  /* check that i1 + dl[xy]1 is inside i0-i1 rectangle */
+	  (dx0 + dlx1) * dx0 + (dy0 + dly1) * dy0 > 0 &&
+	  /* and that i1 + dl[xy]0 is inside i1-i2 rectangle */
+	  ((dx1 - dlx0) * dx1 + (dy1 - dly0) * dy1 > 0)
+#endif
+	  )
+	{
+	  /* can safely add single intersection point */
+	  art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+			   ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+	}
+      else
+	{
+	  /* need to loop-de-loop the inside */
+	  art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+			   ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+	  art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+			   ART_LINETO, vpath[i1].x, vpath[i1].y);
+	  art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+			   ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+	}
+
+      if (join == ART_PATH_STROKE_JOIN_BEVEL)
+	{
+	  /* bevel */
+	  art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+			   ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+	  art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+			   ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+	}
+      else if (join == ART_PATH_STROKE_JOIN_MITER)
+	{
+	  art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+			   ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+	}
+      else if (join == ART_PATH_STROKE_JOIN_ROUND)
+	art_svp_vpath_stroke_arc (p_forw, pn_forw, pn_forw_max,
+				  vpath[i1].x, vpath[i1].y,
+				  -dlx0, -dly0,
+				  -dlx1, -dly1,
+				  line_width,
+				  flatness);
+    }
+  else
+    {
+      /* right turn, rev is outside and forw is inside */
+#ifdef VERBOSE
+      printf ("%% render_seg: right\n");
+#endif
+
+      if (
+#ifdef NO_OPTIMIZE_INNER
+	  0 &&
+#endif
+	  (dmr2 > EPSILON_2) &&
+	  /* check that i1 - dm[xy] is inside i0-i1 rectangle */
+	  (dx0 - dmx) * dx0 + (dy0 - dmy) * dy0 > 0 &&
+	  /* and that i1 - dm[xy] is inside i1-i2 rectangle */
+	  ((dx1 + dmx) * dx1 + (dy1 + dmy) * dy1 > 0)
+#ifdef PEDANTIC_INNER
+	  &&
+	  /* check that i1 - dl[xy]1 is inside i0-i1 rectangle */
+	  (dx0 - dlx1) * dx0 + (dy0 - dly1) * dy0 > 0 &&
+	  /* and that i1 - dl[xy]0 is inside i1-i2 rectangle */
+	  ((dx1 + dlx0) * dx1 + (dy1 + dly0) * dy1 > 0)
+#endif
+	  )
+	{
+	  /* can safely add single intersection point */
+	  art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+			   ART_LINETO, vpath[i1].x - dmx, vpath[i1].y - dmy);
+	}
+      else
+	{
+	  /* need to loop-de-loop the inside */
+	  art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+			   ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+	  art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+			   ART_LINETO, vpath[i1].x, vpath[i1].y);
+	  art_vpath_add_point (p_forw, pn_forw, pn_forw_max,
+			   ART_LINETO, vpath[i1].x - dlx1, vpath[i1].y - dly1);
+	}
+
+      if (join == ART_PATH_STROKE_JOIN_BEVEL)
+	{
+	  /* bevel */
+	  art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+			   ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+	  art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+			   ART_LINETO, vpath[i1].x + dlx1, vpath[i1].y + dly1);
+	}
+      else if (join == ART_PATH_STROKE_JOIN_MITER)
+	{
+	  art_vpath_add_point (p_rev, pn_rev, pn_rev_max,
+			   ART_LINETO, vpath[i1].x + dmx, vpath[i1].y + dmy);
+	}
+      else if (join == ART_PATH_STROKE_JOIN_ROUND)
+	art_svp_vpath_stroke_arc (p_rev, pn_rev, pn_rev_max,
+				  vpath[i1].x, vpath[i1].y,
+				  dlx0, dly0,
+				  dlx1, dly1,
+				  -line_width,
+				  flatness);
+
+    }
+}
+
+/* caps i1, under the assumption of a vector from i0 */
+static void
+render_cap (ArtVpath **p_result, int *pn_result, int *pn_result_max,
+	    ArtVpath *vpath, int i0, int i1,
+	    ArtPathStrokeCapType cap, double line_width, double flatness)
+{
+  double dx0, dy0;
+  double dlx0, dly0;
+  double scale;
+  int n_pts;
+  int i;
+
+  dx0 = vpath[i1].x - vpath[i0].x;
+  dy0 = vpath[i1].y - vpath[i0].y;
+
+  /* Set dl[xy]0 to the vector from i0 to i1, rotated counterclockwise
+     90 degrees, and scaled to the length of line_width. */
+  scale = line_width / sqrt (dx0 * dx0 + dy0 * dy0);
+  dlx0 = dy0 * scale;
+  dly0 = -dx0 * scale;
+
+#ifdef VERBOSE
+  printf ("cap style = %d\n", cap);
+#endif
+
+  switch (cap)
+    {
+    case ART_PATH_STROKE_CAP_BUTT:
+      art_vpath_add_point (p_result, pn_result, pn_result_max,
+			   ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+      art_vpath_add_point (p_result, pn_result, pn_result_max,
+			   ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+      break;
+    case ART_PATH_STROKE_CAP_ROUND:
+      n_pts = ceil (M_PI / (2.0 * M_SQRT2 * sqrt (flatness / line_width)));
+      art_vpath_add_point (p_result, pn_result, pn_result_max,
+			   ART_LINETO, vpath[i1].x - dlx0, vpath[i1].y - dly0);
+      for (i = 1; i < n_pts; i++)
+	{
+	  double theta, c_th, s_th;
+
+	  theta = M_PI * i / n_pts;
+	  c_th = cos (theta);
+	  s_th = sin (theta);
+	  art_vpath_add_point (p_result, pn_result, pn_result_max,
+			       ART_LINETO,
+			       vpath[i1].x - dlx0 * c_th - dly0 * s_th,
+			       vpath[i1].y - dly0 * c_th + dlx0 * s_th);
+	}
+      art_vpath_add_point (p_result, pn_result, pn_result_max,
+			   ART_LINETO, vpath[i1].x + dlx0, vpath[i1].y + dly0);
+      break;
+    case ART_PATH_STROKE_CAP_SQUARE:
+      art_vpath_add_point (p_result, pn_result, pn_result_max,
+			   ART_LINETO,
+			   vpath[i1].x - dlx0 - dly0,
+			   vpath[i1].y - dly0 + dlx0);
+      art_vpath_add_point (p_result, pn_result, pn_result_max,
+			   ART_LINETO,
+			   vpath[i1].x + dlx0 - dly0,
+			   vpath[i1].y + dly0 + dlx0);
+      break;
+    }
+}
+
+/**
+ * art_svp_from_vpath_raw: Stroke a vector path, raw version
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Exactly the same as art_svp_vpath_stroke(), except that the resulting
+ * stroke outline may self-intersect and have regions of winding number
+ * greater than 1.
+ *
+ * Return value: Resulting raw stroked outline in svp format.
+ **/
+ArtVpath *
+art_svp_vpath_stroke_raw (ArtVpath *vpath,
+			  ArtPathStrokeJoinType join,
+			  ArtPathStrokeCapType cap,
+			  double line_width,
+			  double miter_limit,
+			  double flatness)
+{
+  int begin_idx, end_idx;
+  int i;
+  ArtVpath *forw, *rev;
+  int n_forw, n_rev;
+  int n_forw_max, n_rev_max;
+  ArtVpath *result;
+  int n_result, n_result_max;
+  double half_lw = 0.5 * line_width;
+  int closed;
+  int last, this, next, second;
+  double dx, dy;
+
+  n_forw_max = 16;
+  forw = art_new (ArtVpath, n_forw_max);
+
+  n_rev_max = 16;
+  rev = art_new (ArtVpath, n_rev_max);
+
+  n_result = 0;
+  n_result_max = 16;
+  result = art_new (ArtVpath, n_result_max);
+
+  for (begin_idx = 0; vpath[begin_idx].code != ART_END; begin_idx = end_idx)
+    {
+      n_forw = 0;
+      n_rev = 0;
+
+      closed = (vpath[begin_idx].code == ART_MOVETO);
+
+      /* we don't know what the first point joins with until we get to the
+	 last point and see if it's closed. So we start with the second
+	 line in the path.
+
+	 Note: this is not strictly true (we now know it's closed from
+	 the opening pathcode), but why fix code that isn't broken?
+      */
+
+      this = begin_idx;
+      /* skip over identical points at the beginning of the subpath */
+      for (i = this + 1; vpath[i].code == ART_LINETO; i++)
+	{
+	  dx = vpath[i].x - vpath[this].x;
+	  dy = vpath[i].y - vpath[this].y;
+	  if (dx * dx + dy * dy > EPSILON_2)
+	    break;
+	}
+      next = i;
+      second = next;
+
+      /* invariant: this doesn't coincide with next */
+      while (vpath[next].code == ART_LINETO)
+	{
+	  last = this;
+	  this = next;
+	  /* skip over identical points after the beginning of the subpath */
+	  for (i = this + 1; vpath[i].code == ART_LINETO; i++)
+	    {
+	      dx = vpath[i].x - vpath[this].x;
+	      dy = vpath[i].y - vpath[this].y;
+	      if (dx * dx + dy * dy > EPSILON_2)
+		break;
+	    }
+	  next = i;
+	  if (vpath[next].code != ART_LINETO)
+	    {
+	      /* reached end of path */
+	      /* make "closed" detection conform to PostScript
+		 semantics (i.e. explicit closepath code rather than
+		 just the fact that end of the path is the beginning) */
+	      if (closed &&
+		  vpath[this].x == vpath[begin_idx].x &&
+		  vpath[this].y == vpath[begin_idx].y)
+		{
+		  int j;
+
+		  /* path is closed, render join to beginning */
+		  render_seg (&forw, &n_forw, &n_forw_max,
+			      &rev, &n_rev, &n_rev_max,
+			      vpath, last, this, second,
+			      join, half_lw, miter_limit, flatness);
+
+#ifdef VERBOSE
+		  printf ("%% forw %d, rev %d\n", n_forw, n_rev);
+#endif
+		  /* do forward path */
+		  art_vpath_add_point (&result, &n_result, &n_result_max,
+				   ART_MOVETO, forw[n_forw - 1].x,
+				   forw[n_forw - 1].y);
+		  for (j = 0; j < n_forw; j++)
+		    art_vpath_add_point (&result, &n_result, &n_result_max,
+				     ART_LINETO, forw[j].x,
+				     forw[j].y);
+
+		  /* do reverse path, reversed */
+		  art_vpath_add_point (&result, &n_result, &n_result_max,
+				   ART_MOVETO, rev[0].x,
+				   rev[0].y);
+		  for (j = n_rev - 1; j >= 0; j--)
+		    art_vpath_add_point (&result, &n_result, &n_result_max,
+				     ART_LINETO, rev[j].x,
+				     rev[j].y);
+		}
+	      else
+		{
+		  /* path is open */
+		  int j;
+
+		  /* add to forw rather than result to ensure that
+		     forw has at least one point. */
+		  render_cap (&forw, &n_forw, &n_forw_max,
+			      vpath, last, this,
+			      cap, half_lw, flatness);
+		  art_vpath_add_point (&result, &n_result, &n_result_max,
+				   ART_MOVETO, forw[0].x,
+				   forw[0].y);
+		  for (j = 1; j < n_forw; j++)
+		    art_vpath_add_point (&result, &n_result, &n_result_max,
+				     ART_LINETO, forw[j].x,
+				     forw[j].y);
+		  for (j = n_rev - 1; j >= 0; j--)
+		    art_vpath_add_point (&result, &n_result, &n_result_max,
+				     ART_LINETO, rev[j].x,
+				     rev[j].y);
+		  render_cap (&result, &n_result, &n_result_max,
+			      vpath, second, begin_idx,
+			      cap, half_lw, flatness);
+		  art_vpath_add_point (&result, &n_result, &n_result_max,
+				   ART_LINETO, forw[0].x,
+				   forw[0].y);
+		}
+	    }
+	  else
+	    render_seg (&forw, &n_forw, &n_forw_max,
+			&rev, &n_rev, &n_rev_max,
+			vpath, last, this, next,
+			join, half_lw, miter_limit, flatness);
+	}
+      end_idx = next;
+    }
+
+  art_free (forw);
+  art_free (rev);
+#ifdef VERBOSE
+  printf ("%% n_result = %d\n", n_result);
+#endif
+  art_vpath_add_point (&result, &n_result, &n_result_max, ART_END, 0, 0);
+  return result;
+}
+
+#define noVERBOSE
+
+#ifdef VERBOSE
+
+#define XOFF 50
+#define YOFF 700
+
+static void
+print_ps_vpath (ArtVpath *vpath)
+{
+  int i;
+
+  for (i = 0; vpath[i].code != ART_END; i++)
+    {
+      switch (vpath[i].code)
+	{
+	case ART_MOVETO:
+	  printf ("%g %g moveto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+	  break;
+	case ART_LINETO:
+	  printf ("%g %g lineto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+	  break;
+	default:
+	  break;
+	}
+    }
+  printf ("stroke showpage\n");
+}
+
+static void
+print_ps_svp (ArtSVP *vpath)
+{
+  int i, j;
+
+  printf ("%% begin\n");
+  for (i = 0; i < vpath->n_segs; i++)
+    {
+      printf ("%g setgray\n", vpath->segs[i].dir ? 0.7 : 0);
+      for (j = 0; j < vpath->segs[i].n_points; j++)
+	{
+	  printf ("%g %g %s\n",
+		  XOFF + vpath->segs[i].points[j].x,
+		  YOFF - vpath->segs[i].points[j].y,
+		  j ? "lineto" : "moveto");
+	}
+      printf ("stroke\n");
+    }
+
+  printf ("showpage\n");
+}
+#endif
+
+/* Render a vector path into a stroked outline.
+
+   Status of this routine:
+
+   Basic correctness: Only miter and bevel line joins are implemented,
+   and only butt line caps. Otherwise, seems to be fine.
+
+   Numerical stability: We cheat (adding random perturbation). Thus,
+   it seems very likely that no numerical stability problems will be
+   seen in practice.
+
+   Speed: Should be pretty good.
+
+   Precision: The perturbation fuzzes the coordinates slightly,
+   but not enough to be visible.  */
+/**
+ * art_svp_vpath_stroke: Stroke a vector path.
+ * @vpath: #ArtVPath to stroke.
+ * @join: Join style.
+ * @cap: Cap style.
+ * @line_width: Width of stroke.
+ * @miter_limit: Miter limit.
+ * @flatness: Flatness.
+ *
+ * Computes an svp representing the stroked outline of @vpath. The
+ * width of the stroked line is @line_width.
+ *
+ * Lines are joined according to the @join rule. Possible values are
+ * ART_PATH_STROKE_JOIN_MITER (for mitered joins),
+ * ART_PATH_STROKE_JOIN_ROUND (for round joins), and
+ * ART_PATH_STROKE_JOIN_BEVEL (for bevelled joins). The mitered join
+ * is converted to a bevelled join if the miter would extend to a
+ * distance of more than @miter_limit * @line_width from the actual
+ * join point.
+ *
+ * If there are open subpaths, the ends of these subpaths are capped
+ * according to the @cap rule. Possible values are
+ * ART_PATH_STROKE_CAP_BUTT (squared cap, extends exactly to end
+ * point), ART_PATH_STROKE_CAP_ROUND (rounded half-circle centered at
+ * the end point), and ART_PATH_STROKE_CAP_SQUARE (squared cap,
+ * extending half @line_width past the end point).
+ *
+ * The @flatness parameter controls the accuracy of the rendering. It
+ * is most important for determining the number of points to use to
+ * approximate circular arcs for round lines and joins. In general, the
+ * resulting vector path will be within @flatness pixels of the "ideal"
+ * path containing actual circular arcs. I reserve the right to use
+ * the @flatness parameter to convert bevelled joins to miters for very
+ * small turn angles, as this would reduce the number of points in the
+ * resulting outline path.
+ *
+ * The resulting path is "clean" with respect to self-intersections, i.e.
+ * the winding number is 0 or 1 at each point.
+ *
+ * Return value: Resulting stroked outline in svp format.
+ **/
+ArtSVP *
+art_svp_vpath_stroke (ArtVpath *vpath,
+		      ArtPathStrokeJoinType join,
+		      ArtPathStrokeCapType cap,
+		      double line_width,
+		      double miter_limit,
+		      double flatness)
+{
+#ifdef ART_USE_NEW_INTERSECTOR
+  ArtVpath *vpath_stroke;
+  ArtSVP *svp, *svp2;
+  ArtSvpWriter *swr;
+
+  vpath_stroke = art_svp_vpath_stroke_raw (vpath, join, cap,
+					   line_width, miter_limit, flatness);
+#ifdef VERBOSE
+  print_ps_vpath (vpath_stroke);
+#endif
+  svp = art_svp_from_vpath (vpath_stroke);
+#ifdef VERBOSE
+  print_ps_svp (svp);
+#endif
+  art_free (vpath_stroke);
+
+  swr = art_svp_writer_rewind_new (ART_WIND_RULE_NONZERO);
+  art_svp_intersector (svp, swr);
+
+  svp2 = art_svp_writer_rewind_reap (swr);
+#ifdef VERBOSE
+  print_ps_svp (svp2);
+#endif
+  art_svp_free (svp);
+  return svp2;
+#else
+  ArtVpath *vpath_stroke, *vpath2;
+  ArtSVP *svp, *svp2, *svp3;
+
+  vpath_stroke = art_svp_vpath_stroke_raw (vpath, join, cap,
+					   line_width, miter_limit, flatness);
+#ifdef VERBOSE
+  print_ps_vpath (vpath_stroke);
+#endif
+  vpath2 = art_vpath_perturb (vpath_stroke);
+#ifdef VERBOSE
+  print_ps_vpath (vpath2);
+#endif
+  art_free (vpath_stroke);
+  svp = art_svp_from_vpath (vpath2);
+#ifdef VERBOSE
+  print_ps_svp (svp);
+#endif
+  art_free (vpath2);
+  svp2 = art_svp_uncross (svp);
+#ifdef VERBOSE
+  print_ps_svp (svp2);
+#endif
+  art_svp_free (svp);
+  svp3 = art_svp_rewind_uncrossed (svp2, ART_WIND_RULE_NONZERO);
+#ifdef VERBOSE
+  print_ps_svp (svp3);
+#endif
+  art_svp_free (svp2);
+
+  return svp3;
+#endif
+}
diff --git a/src/libart_lgpl/art_svp_vpath_stroke.h b/src/libart_lgpl/art_svp_vpath_stroke.h
new file mode 100644
index 0000000..2c7d7e7
--- /dev/null
+++ b/src/libart_lgpl/art_svp_vpath_stroke.h
@@ -0,0 +1,69 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_VPATH_STROKE_H__
+#define __ART_SVP_VPATH_STROKE_H__
+
+/* Sort vector paths into sorted vector paths. */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#include "art_vpath.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+  ART_PATH_STROKE_JOIN_MITER,
+  ART_PATH_STROKE_JOIN_ROUND,
+  ART_PATH_STROKE_JOIN_BEVEL
+} ArtPathStrokeJoinType;
+
+typedef enum {
+  ART_PATH_STROKE_CAP_BUTT,
+  ART_PATH_STROKE_CAP_ROUND,
+  ART_PATH_STROKE_CAP_SQUARE
+} ArtPathStrokeCapType;
+
+ArtSVP *
+art_svp_vpath_stroke (ArtVpath *vpath,
+		      ArtPathStrokeJoinType join,
+		      ArtPathStrokeCapType cap,
+		      double line_width,
+		      double miter_limit,
+		      double flatness);
+
+/* This version may have winding numbers exceeding 1. */
+ArtVpath *
+art_svp_vpath_stroke_raw (ArtVpath *vpath,
+			  ArtPathStrokeJoinType join,
+			  ArtPathStrokeCapType cap,
+			  double line_width,
+			  double miter_limit,
+			  double flatness);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_VPATH_STROKE_H__ */
diff --git a/src/libart_lgpl/art_svp_wind.c b/src/libart_lgpl/art_svp_wind.c
new file mode 100644
index 0000000..4ca70b9
--- /dev/null
+++ b/src/libart_lgpl/art_svp_wind.c
@@ -0,0 +1,1544 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Primitive intersection and winding number operations on sorted
+   vector paths.
+
+   These routines are internal to libart, used to construct operations
+   like intersection, union, and difference. */
+
+#include "config.h"
+#include "art_svp_wind.h"
+
+#include <stdio.h> /* for printf of debugging info */
+#include <string.h> /* for memcpy */
+#include <math.h>
+#include "art_misc.h"
+
+#include "art_rect.h"
+#include "art_svp.h"
+
+#define noVERBOSE
+
+#define PT_EQ(p1,p2) ((p1).x == (p2).x && (p1).y == (p2).y)
+
+#define PT_CLOSE(p1,p2) (fabs ((p1).x - (p2).x) < 1e-6 && fabs ((p1).y - (p2).y) < 1e-6)
+
+/* return nonzero and set *p to the intersection point if the lines
+   z0-z1 and z2-z3 intersect each other. */
+static int
+intersect_lines (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3,
+		 ArtPoint *p)
+{
+  double a01, b01, c01;
+  double a23, b23, c23;
+  double d0, d1, d2, d3;
+  double det;
+
+  /* if the vectors share an endpoint, they don't intersect */
+  if (PT_EQ (z0, z2) || PT_EQ (z0, z3) || PT_EQ (z1, z2) || PT_EQ (z1, z3))
+    return 0;
+
+#if 0
+  if (PT_CLOSE (z0, z2) || PT_CLOSE (z0, z3) || PT_CLOSE (z1, z2) || PT_CLOSE (z1, z3))
+    return 0;
+#endif
+
+  /* find line equations ax + by + c = 0 */
+  a01 = z0.y - z1.y;
+  b01 = z1.x - z0.x;
+  c01 = -(z0.x * a01 + z0.y * b01);
+  /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
+     = (z1.x * z0.y - z1.y * z0.x) */
+
+  d2 = a01 * z2.x + b01 * z2.y + c01;
+  d3 = a01 * z3.x + b01 * z3.y + c01;
+  if ((d2 > 0) == (d3 > 0))
+    return 0;
+
+  a23 = z2.y - z3.y;
+  b23 = z3.x - z2.x;
+  c23 = -(z2.x * a23 + z2.y * b23);
+
+  d0 = a23 * z0.x + b23 * z0.y + c23;
+  d1 = a23 * z1.x + b23 * z1.y + c23;
+  if ((d0 > 0) == (d1 > 0))
+    return 0;
+
+  /* now we definitely know that the lines intersect */
+  /* solve the two linear equations ax + by + c = 0 */
+  det = 1.0 / (a01 * b23 - a23 * b01);
+  p->x = det * (c23 * b01 - c01 * b23);
+  p->y = det * (c01 * a23 - c23 * a01);
+
+  return 1;
+}
+
+#define EPSILON 1e-6
+
+static double
+trap_epsilon (double v)
+{
+  const double epsilon = EPSILON;
+
+  if (v < epsilon && v > -epsilon) return 0;
+  else return v;
+}
+
+/* Determine the order of line segments z0-z1 and z2-z3.
+   Return +1 if z2-z3 lies entirely to the right of z0-z1,
+   -1 if entirely to the left,
+   or 0 if overlap.
+
+   The case analysis in this function is quite ugly. The fact that it's
+   almost 200 lines long is ridiculous.
+
+   Ok, so here's the plan to cut it down:
+
+   First, do a bounding line comparison on the x coordinates. This is pretty
+   much the common case, and should go quickly. It also takes care of the
+   case where both lines are horizontal.
+
+   Then, do d0 and d1 computation, but only if a23 is nonzero.
+
+   Finally, do d2 and d3 computation, but only if a01 is nonzero.
+
+   Fall through to returning 0 (this will happen when both lines are
+   horizontal and they overlap).
+   */
+static int
+x_order (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3)
+{
+  double a01, b01, c01;
+  double a23, b23, c23;
+  double d0, d1, d2, d3;
+
+  if (z0.y == z1.y)
+    {
+      if (z2.y == z3.y)
+	{
+	  double x01min, x01max;
+	  double x23min, x23max;
+
+	  if (z0.x > z1.x)
+	    {
+	      x01min = z1.x;
+	      x01max = z0.x;
+	    }
+	  else
+	    {
+	      x01min = z0.x;
+	      x01max = z1.x;
+	    }
+
+	  if (z2.x > z3.x)
+	    {
+	      x23min = z3.x;
+	      x23max = z2.x;
+	    }
+	  else
+	    {
+	      x23min = z2.x;
+	      x23max = z3.x;
+	    }
+
+	  if (x23min >= x01max) return 1;
+	  else if (x01min >= x23max) return -1;
+	  else return 0;
+	}
+      else
+	{
+	  /* z0-z1 is horizontal, z2-z3 isn't */
+	  a23 = z2.y - z3.y;
+	  b23 = z3.x - z2.x;
+	  c23 = -(z2.x * a23 + z2.y * b23);
+
+	  if (z3.y < z2.y)
+	    {
+	      a23 = -a23;
+	      b23 = -b23;
+	      c23 = -c23;
+	    }
+
+	  d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23);
+	  d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23);
+
+	  if (d0 > 0)
+	    {
+	      if (d1 >= 0) return 1;
+	      else return 0;
+	    }
+	  else if (d0 == 0)
+	    {
+	      if (d1 > 0) return 1;
+	      else if (d1 < 0) return -1;
+	      else printf ("case 1 degenerate\n");
+	      return 0;
+	    }
+	  else /* d0 < 0 */
+	    {
+	      if (d1 <= 0) return -1;
+	      else return 0;
+	    }
+	}
+    }
+  else if (z2.y == z3.y)
+    {
+      /* z2-z3 is horizontal, z0-z1 isn't */
+      a01 = z0.y - z1.y;
+      b01 = z1.x - z0.x;
+      c01 = -(z0.x * a01 + z0.y * b01);
+      /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
+	 = (z1.x * z0.y - z1.y * z0.x) */
+
+      if (z1.y < z0.y)
+	{
+	  a01 = -a01;
+	  b01 = -b01;
+	  c01 = -c01;
+	}
+
+      d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01);
+      d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01);
+
+      if (d2 > 0)
+	{
+	  if (d3 >= 0) return -1;
+	  else return 0;
+	}
+      else if (d2 == 0)
+	{
+	  if (d3 > 0) return -1;
+	  else if (d3 < 0) return 1;
+	  else printf ("case 2 degenerate\n");
+	  return 0;
+	}
+      else /* d2 < 0 */
+	{
+	  if (d3 <= 0) return 1;
+	  else return 0;
+	}
+    }
+
+  /* find line equations ax + by + c = 0 */
+  a01 = z0.y - z1.y;
+  b01 = z1.x - z0.x;
+  c01 = -(z0.x * a01 + z0.y * b01);
+  /* = -((z0.y - z1.y) * z0.x + (z1.x - z0.x) * z0.y)
+     = -(z1.x * z0.y - z1.y * z0.x) */
+
+  if (a01 > 0)
+    {
+      a01 = -a01;
+      b01 = -b01;
+      c01 = -c01;
+    }
+  /* so now, (a01, b01) points to the left, thus a01 * x + b01 * y + c01
+     is negative if the point lies to the right of the line */
+
+  d2 = trap_epsilon (a01 * z2.x + b01 * z2.y + c01);
+  d3 = trap_epsilon (a01 * z3.x + b01 * z3.y + c01);
+  if (d2 > 0)
+    {
+      if (d3 >= 0) return -1;
+    }
+  else if (d2 == 0)
+    {
+      if (d3 > 0) return -1;
+      else if (d3 < 0) return 1;
+      else
+	fprintf (stderr, "colinear!\n");
+    }
+  else /* d2 < 0 */
+    {
+      if (d3 <= 0) return 1;
+    }
+
+  a23 = z2.y - z3.y;
+  b23 = z3.x - z2.x;
+  c23 = -(z2.x * a23 + z2.y * b23);
+
+  if (a23 > 0)
+    {
+      a23 = -a23;
+      b23 = -b23;
+      c23 = -c23;
+    }
+  d0 = trap_epsilon (a23 * z0.x + b23 * z0.y + c23);
+  d1 = trap_epsilon (a23 * z1.x + b23 * z1.y + c23);
+  if (d0 > 0)
+    {
+      if (d1 >= 0) return 1;
+    }
+  else if (d0 == 0)
+    {
+      if (d1 > 0) return 1;
+      else if (d1 < 0) return -1;
+      else
+	fprintf (stderr, "colinear!\n");
+    }
+  else /* d0 < 0 */
+    {
+      if (d1 <= 0) return -1;
+    }
+
+  return 0;
+}
+
+/* similar to x_order, but to determine whether point z0 + epsilon lies to
+   the left of the line z2-z3 or to the right */
+static int
+x_order_2 (ArtPoint z0, ArtPoint z1, ArtPoint z2, ArtPoint z3)
+{
+  double a23, b23, c23;
+  double d0, d1;
+
+  a23 = z2.y - z3.y;
+  b23 = z3.x - z2.x;
+  c23 = -(z2.x * a23 + z2.y * b23);
+
+  if (a23 > 0)
+    {
+      a23 = -a23;
+      b23 = -b23;
+      c23 = -c23;
+    }
+
+  d0 = a23 * z0.x + b23 * z0.y + c23;
+
+  if (d0 > EPSILON)
+    return -1;
+  else if (d0 < -EPSILON)
+    return 1;
+
+  d1 = a23 * z1.x + b23 * z1.y + c23;
+  if (d1 > EPSILON)
+    return -1;
+  else if (d1 < -EPSILON)
+    return 1;
+
+  if (z0.x == z1.x && z1.x == z2.x && z2.x == z3.x)
+    {
+      art_dprint ("x_order_2: colinear and horizontally aligned!\n");
+      return 0;
+    }
+
+  if (z0.x <= z2.x && z1.x <= z2.x && z0.x <= z3.x && z1.x <= z3.x)
+    return -1;
+  if (z0.x >= z2.x && z1.x >= z2.x && z0.x >= z3.x && z1.x >= z3.x)
+    return 1;
+
+  fprintf (stderr, "x_order_2: colinear!\n");
+  return 0;
+}
+
+#ifdef DEAD_CODE
+/* Traverse the vector path, keeping it in x-sorted order.
+
+   This routine doesn't actually do anything - it's just here for
+   explanatory purposes. */
+void
+traverse (ArtSVP *vp)
+{
+  int *active_segs;
+  int n_active_segs;
+  int *cursor;
+  int seg_idx;
+  double y;
+  int tmp1, tmp2;
+  int asi;
+  int i, j;
+
+  active_segs = art_new (int, vp->n_segs);
+  cursor = art_new (int, vp->n_segs);
+
+  n_active_segs = 0;
+  seg_idx = 0;
+  y = vp->segs[0].points[0].y;
+  while (seg_idx < vp->n_segs || n_active_segs > 0)
+    {
+      printf ("y = %g\n", y);
+      /* delete segments ending at y from active list */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  if (vp->segs[asi].n_points - 1 == cursor[asi] &&
+	      vp->segs[asi].points[cursor[asi]].y == y)
+	    {
+	      printf ("deleting %d\n", asi);
+	      n_active_segs--;
+	      for (j = i; j < n_active_segs; j++)
+		active_segs[j] = active_segs[j + 1];
+	      i--;
+	    }
+	}
+
+      /* insert new segments into the active list */
+      while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
+	{
+	  cursor[seg_idx] = 0;
+	  printf ("inserting %d\n", seg_idx);
+	  for (i = 0; i < n_active_segs; i++)
+	    {
+	      asi = active_segs[i];
+	      if (x_order (vp->segs[asi].points[cursor[asi]],
+			   vp->segs[asi].points[cursor[asi] + 1],
+			   vp->segs[seg_idx].points[0],
+			   vp->segs[seg_idx].points[1]) == -1)
+	      break;
+	    }
+	  tmp1 = seg_idx;
+	  for (j = i; j < n_active_segs; j++)
+	    {
+	      tmp2 = active_segs[j];
+	      active_segs[j] = tmp1;
+	      tmp1 = tmp2;
+	    }
+	  active_segs[n_active_segs] = tmp1;
+	  n_active_segs++;
+	  seg_idx++;
+	}
+
+      /* all active segs cross the y scanline (considering segs to be
+       closed on top and open on bottom) */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  printf ("%d (%g, %g) - (%g, %g) %s\n", asi,
+		  vp->segs[asi].points[cursor[asi]].x,
+		  vp->segs[asi].points[cursor[asi]].y,
+		  vp->segs[asi].points[cursor[asi] + 1].x,
+		  vp->segs[asi].points[cursor[asi] + 1].y,
+		  vp->segs[asi].dir ? "v" : "^");
+	}
+
+      /* advance y to the next event */
+      if (n_active_segs == 0)
+	{
+	  if (seg_idx < vp->n_segs)
+	    y = vp->segs[seg_idx].points[0].y;
+	  /* else we're done */
+	}
+      else
+	{
+	  asi = active_segs[0];
+	  y = vp->segs[asi].points[cursor[asi] + 1].y;
+	  for (i = 1; i < n_active_segs; i++)
+	    {
+	      asi = active_segs[i];
+	      if (y > vp->segs[asi].points[cursor[asi] + 1].y)
+		y = vp->segs[asi].points[cursor[asi] + 1].y;
+	    }
+	  if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
+	    y = vp->segs[seg_idx].points[0].y;
+	}
+
+      /* advance cursors to reach new y */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  while (cursor[asi] < vp->segs[asi].n_points - 1 &&
+		 y >= vp->segs[asi].points[cursor[asi] + 1].y)
+	    cursor[asi]++;
+	}
+      printf ("\n");
+    }
+  art_free (cursor);
+  art_free (active_segs);
+}
+#endif
+
+/* I believe that the loop will always break with i=1.
+
+   I think I'll want to change this from a simple sorted list to a
+   modified stack. ips[*][0] will get its own data structure, and
+   ips[*] will in general only be allocated if there is an intersection.
+   Finally, the segment can be traced through the initial point
+   (formerly ips[*][0]), backwards through the stack, and finally
+   to cursor + 1.
+
+   This change should cut down on allocation bandwidth, and also
+   eliminate the iteration through n_ipl below.
+
+*/
+static void
+insert_ip (int seg_i, int *n_ips, int *n_ips_max, ArtPoint **ips, ArtPoint ip)
+{
+  int i;
+  ArtPoint tmp1, tmp2;
+  int n_ipl;
+  ArtPoint *ipl;
+
+  n_ipl = n_ips[seg_i]++;
+  if (n_ipl == n_ips_max[seg_i])
+      art_expand (ips[seg_i], ArtPoint, n_ips_max[seg_i]);
+  ipl = ips[seg_i];
+  for (i = 1; i < n_ipl; i++)
+    if (ipl[i].y > ip.y)
+      break;
+  tmp1 = ip;
+  for (; i <= n_ipl; i++)
+    {
+      tmp2 = ipl[i];
+      ipl[i] = tmp1;
+      tmp1 = tmp2;
+    }
+}
+
+/* test active segment (i - 1) against i for intersection, if
+   so, add intersection point to both ips lists. */
+static void
+intersect_neighbors (int i, int *active_segs,
+		     int *n_ips, int *n_ips_max, ArtPoint **ips,
+		     int *cursor, ArtSVP *vp)
+{
+  ArtPoint z0, z1, z2, z3;
+  int asi01, asi23;
+  ArtPoint ip;
+
+  asi01 = active_segs[i - 1];
+
+  z0 = ips[asi01][0];
+  if (n_ips[asi01] == 1)
+    z1 = vp->segs[asi01].points[cursor[asi01] + 1];
+  else
+    z1 = ips[asi01][1];
+
+  asi23 = active_segs[i];
+
+  z2 = ips[asi23][0];
+  if (n_ips[asi23] == 1)
+    z3 = vp->segs[asi23].points[cursor[asi23] + 1];
+  else
+    z3 = ips[asi23][1];
+
+  if (intersect_lines (z0, z1, z2, z3, &ip))
+    {
+#ifdef VERBOSE
+      printf ("new intersection point: (%g, %g)\n", ip.x, ip.y);
+#endif
+      insert_ip (asi01, n_ips, n_ips_max, ips, ip);
+      insert_ip (asi23, n_ips, n_ips_max, ips, ip);
+    }
+}
+
+/* Add a new point to a segment in the svp.
+
+   Here, we also check to make sure that the segments satisfy nocross.
+   However, this is only valuable for debugging, and could possibly be
+   removed.
+*/
+static void
+svp_add_point (ArtSVP *svp, int *n_points_max,
+	       ArtPoint p, int *seg_map, int *active_segs, int n_active_segs,
+	       int i)
+{
+  int asi, asi_left, asi_right;
+  int n_points, n_points_left, n_points_right;
+  ArtSVPSeg *seg;
+
+  asi = seg_map[active_segs[i]];
+  seg = &svp->segs[asi];
+  n_points = seg->n_points;
+  /* find out whether neighboring segments share a point */
+  if (i > 0)
+    {
+      asi_left = seg_map[active_segs[i - 1]];
+      n_points_left = svp->segs[asi_left].n_points;
+      if (n_points_left > 1 &&
+	  PT_EQ (svp->segs[asi_left].points[n_points_left - 2],
+		 svp->segs[asi].points[n_points - 1]))
+	{
+	  /* ok, new vector shares a top point with segment to the left -
+	     now, check that it satisfies ordering invariant */
+	  if (x_order (svp->segs[asi_left].points[n_points_left - 2],
+		       svp->segs[asi_left].points[n_points_left - 1],
+		       svp->segs[asi].points[n_points - 1],
+		       p) < 1)
+
+	    {
+#ifdef VERBOSE
+	      printf ("svp_add_point: cross on left!\n");
+#endif
+	    }
+	}
+    }
+
+  if (i + 1 < n_active_segs)
+    {
+      asi_right = seg_map[active_segs[i + 1]];
+      n_points_right = svp->segs[asi_right].n_points;
+      if (n_points_right > 1 &&
+	  PT_EQ (svp->segs[asi_right].points[n_points_right - 2],
+		 svp->segs[asi].points[n_points - 1]))
+	{
+	  /* ok, new vector shares a top point with segment to the right -
+	     now, check that it satisfies ordering invariant */
+	  if (x_order (svp->segs[asi_right].points[n_points_right - 2],
+		       svp->segs[asi_right].points[n_points_right - 1],
+		       svp->segs[asi].points[n_points - 1],
+		       p) > -1)
+	    {
+#ifdef VERBOSE
+	      printf ("svp_add_point: cross on right!\n");
+#endif
+	    }
+	}
+    }
+  if (n_points_max[asi] == n_points)
+    art_expand (seg->points, ArtPoint, n_points_max[asi]);
+  seg->points[n_points] = p;
+  if (p.x < seg->bbox.x0)
+    seg->bbox.x0 = p.x;
+  else if (p.x > seg->bbox.x1)
+    seg->bbox.x1 = p.x;
+  seg->bbox.y1 = p.y;
+  seg->n_points++;
+}
+
+#if 0
+/* find where the segment (currently at i) is supposed to go, and return
+   the target index - if equal to i, then there is no crossing problem.
+
+   "Where it is supposed to go" is defined as following:
+
+   Delete element i, re-insert at position target (bumping everything
+   target and greater to the right).
+   */
+static int
+find_crossing (int i, int *active_segs, int n_active_segs,
+	       int *cursor, ArtPoint **ips, int *n_ips, ArtSVP *vp)
+{
+  int asi, asi_left, asi_right;
+  ArtPoint p0, p1;
+  ArtPoint p0l, p1l;
+  ArtPoint p0r, p1r;
+  int target;
+
+  asi = active_segs[i];
+  p0 = ips[asi][0];
+  if (n_ips[asi] == 1)
+    p1 = vp->segs[asi].points[cursor[asi] + 1];
+  else
+    p1 = ips[asi][1];
+
+  for (target = i; target > 0; target--)
+    {
+      asi_left = active_segs[target - 1];
+      p0l = ips[asi_left][0];
+      if (n_ips[asi_left] == 1)
+	p1l = vp->segs[asi_left].points[cursor[asi_left] + 1];
+      else
+	p1l = ips[asi_left][1];
+      if (!PT_EQ (p0, p0l))
+	break;
+
+#ifdef VERBOSE
+      printf ("point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n",
+	      p0l.x, p0l.y, p1l.x, p1l.y, p0.x, p0.y, p1.x, p1.y);
+#endif
+      if (x_order (p0l, p1l, p0, p1) == 1)
+	break;
+
+#ifdef VERBOSE
+      printf ("scanning to the left (i=%d, target=%d)\n", i, target);
+#endif
+    }
+
+  if (target < i) return target;
+
+  for (; target < n_active_segs - 1; target++)
+    {
+      asi_right = active_segs[target + 1];
+      p0r = ips[asi_right][0];
+      if (n_ips[asi_right] == 1)
+	p1r = vp->segs[asi_right].points[cursor[asi_right] + 1];
+      else
+	p1r = ips[asi_right][1];
+      if (!PT_EQ (p0, p0r))
+	break;
+
+#ifdef VERBOSE
+      printf ("point matches on left (%g, %g) - (%g, %g) x (%g, %g) - (%g, %g)!\n",
+	      p0.x, p0.y, p1.x, p1.y, p0r.x, p0r.y, p1r.x, p1r.y);
+#endif
+      if (x_order (p0r, p1r, p0, p1) == 1)
+	break;
+
+#ifdef VERBOSE
+      printf ("scanning to the right (i=%d, target=%d)\n", i, target);
+#endif
+    }
+
+  return target;
+}
+#endif
+
+/* This routine handles the case where the segment changes its position
+   in the active segment list. Generally, this will happen when the
+   segment (defined by i and cursor) shares a top point with a neighbor,
+   but breaks the ordering invariant.
+
+   Essentially, this routine sorts the lines [start..end), all of which
+   share a top point. This is implemented as your basic insertion sort.
+
+   This routine takes care of intersecting the appropriate neighbors,
+   as well.
+
+   A first argument of -1 immediately returns, which helps reduce special
+   casing in the main unwind routine.
+*/
+static void
+fix_crossing (int start, int end, int *active_segs, int n_active_segs,
+	      int *cursor, ArtPoint **ips, int *n_ips, int *n_ips_max,
+	      ArtSVP *vp, int *seg_map,
+	      ArtSVP **p_new_vp, int *pn_segs_max,
+	      int **pn_points_max)
+{
+  int i, j;
+  int target;
+  int asi, asj;
+  ArtPoint p0i, p1i;
+  ArtPoint p0j, p1j;
+  int swap = 0;
+#ifdef VERBOSE
+  int k;
+#endif
+  ArtPoint *pts;
+
+#ifdef VERBOSE
+  printf ("fix_crossing: [%d..%d)", start, end);
+  for (k = 0; k < n_active_segs; k++)
+    printf (" %d", active_segs[k]);
+  printf ("\n");
+#endif
+
+  if (start == -1)
+    return;
+
+  for (i = start + 1; i < end; i++)
+    {
+
+      asi = active_segs[i];
+      if (cursor[asi] < vp->segs[asi].n_points - 1) {
+	p0i = ips[asi][0];
+	if (n_ips[asi] == 1)
+	  p1i = vp->segs[asi].points[cursor[asi] + 1];
+	else
+	  p1i = ips[asi][1];
+
+	for (j = i - 1; j >= start; j--)
+	  {
+	    asj = active_segs[j];
+	    if (cursor[asj] < vp->segs[asj].n_points - 1)
+	      {
+		p0j = ips[asj][0];
+		if (n_ips[asj] == 1)
+		  p1j = vp->segs[asj].points[cursor[asj] + 1];
+		else
+		  p1j = ips[asj][1];
+
+		/* we _hope_ p0i = p0j */
+		if (x_order_2 (p0j, p1j, p0i, p1i) == -1)
+		  break;
+	      }
+	  }
+
+	target = j + 1;
+	/* target is where active_seg[i] _should_ be in active_segs */
+
+	if (target != i)
+	  {
+	    swap = 1;
+
+#ifdef VERBOSE
+	    printf ("fix_crossing: at %i should be %i\n", i, target);
+#endif
+
+	    /* let's close off all relevant segments */
+	    for (j = i; j >= target; j--)
+	      {
+		asi = active_segs[j];
+		/* First conjunct: this isn't the last point in the original
+		   segment.
+
+		   Second conjunct: this isn't the first point in the new
+		   segment (i.e. already broken).
+		*/
+		if (cursor[asi] < vp->segs[asi].n_points - 1 &&
+		    (*p_new_vp)->segs[seg_map[asi]].n_points != 1)
+		  {
+		    int seg_num;
+		    /* so break here */
+#ifdef VERBOSE
+		    printf ("closing off %d\n", j);
+#endif
+
+		    pts = art_new (ArtPoint, 16);
+		    pts[0] = ips[asi][0];
+		    seg_num = art_svp_add_segment (p_new_vp, pn_segs_max,
+						   pn_points_max,
+						   1, vp->segs[asi].dir,
+						   pts,
+						   NULL);
+		    (*pn_points_max)[seg_num] = 16;
+		    seg_map[asi] = seg_num;
+		  }
+	      }
+
+	    /* now fix the ordering in active_segs */
+	    asi = active_segs[i];
+	    for (j = i; j > target; j--)
+	      active_segs[j] = active_segs[j - 1];
+	    active_segs[j] = asi;
+	  }
+      }
+    }
+  if (swap && start > 0)
+    {
+      int as_start;
+
+      as_start = active_segs[start];
+      if (cursor[as_start] < vp->segs[as_start].n_points)
+	{
+#ifdef VERBOSE
+	  printf ("checking intersection of %d, %d\n", start - 1, start);
+#endif
+	  intersect_neighbors (start, active_segs,
+			       n_ips, n_ips_max, ips,
+			       cursor, vp);
+	}
+    }
+
+  if (swap && end < n_active_segs)
+    {
+      int as_end;
+
+      as_end = active_segs[end - 1];
+      if (cursor[as_end] < vp->segs[as_end].n_points)
+	{
+#ifdef VERBOSE
+	  printf ("checking intersection of %d, %d\n", end - 1, end);
+#endif
+	  intersect_neighbors (end, active_segs,
+			       n_ips, n_ips_max, ips,
+			       cursor, vp);
+	}
+    }
+  if (swap)
+    {
+#ifdef VERBOSE
+      printf ("fix_crossing return: [%d..%d)", start, end);
+      for (k = 0; k < n_active_segs; k++)
+	printf (" %d", active_segs[k]);
+      printf ("\n");
+#endif
+    }
+}
+
+/* Return a new sorted vector that covers the same area as the
+   argument, but which satisfies the nocross invariant.
+
+   Basically, this routine works by finding the intersection points,
+   and cutting the segments at those points.
+
+   Status of this routine:
+
+   Basic correctness: Seems ok.
+
+   Numerical stability: known problems in the case of points falling
+   on lines, and colinear lines. For actual use, randomly perturbing
+   the vertices is currently recommended.
+
+   Speed: pretty good, although a more efficient priority queue, as
+   well as bbox culling of potential intersections, are two
+   optimizations that could help.
+
+   Precision: pretty good, although the numerical stability problems
+   make this routine unsuitable for precise calculations of
+   differences.
+
+*/
+
+/* Here is a more detailed description of the algorithm. It follows
+   roughly the structure of traverse (above), but is obviously quite
+   a bit more complex.
+
+   Here are a few important data structures:
+
+   A new sorted vector path (new_svp).
+
+   For each (active) segment in the original, a list of intersection
+   points.
+
+   Of course, the original being traversed.
+
+   The following invariants hold (in addition to the invariants
+   of the traverse procedure).
+
+   The new sorted vector path lies entirely above the y scan line.
+
+   The new sorted vector path keeps the nocross invariant.
+
+   For each active segment, the y scan line crosses the line from the
+   first to the second of the intersection points (where the second
+   point is cursor + 1 if there is only one intersection point).
+
+   The list of intersection points + the (cursor + 1) point is kept
+   in nondecreasing y order.
+
+   Of the active segments, none of the lines from first to second
+   intersection point cross the 1st ip..2nd ip line of the left or
+   right neighbor. (However, such a line may cross further
+   intersection points of the neighbors, or segments past the
+   immediate neighbors).
+
+   Of the active segments, all lines from 1st ip..2nd ip are in
+   strictly increasing x_order (this is very similar to the invariant
+   of the traverse procedure, but is explicitly stated here in terms
+   of ips). (this basically says that nocross holds on the active
+   segments)
+
+   The combination of the new sorted vector path, the path through all
+   the intersection points to cursor + 1, and [cursor + 1, n_points)
+   covers the same area as the argument.
+
+   Another important data structure is mapping from original segment
+   number to new segment number.
+
+   The algorithm is perhaps best understood as advancing the cursors
+   while maintaining these invariants. Here's roughly how it's done.
+
+   When deleting segments from the active list, those segments are added
+   to the new sorted vector path. In addition, the neighbors may intersect
+   each other, so they are intersection tested (see below).
+
+   When inserting new segments, they are intersection tested against
+   their neighbors. The top point of the segment becomes the first
+   intersection point.
+
+   Advancing the cursor is just a bit different from the traverse
+   routine, as the cursor may advance through the intersection points
+   as well. Only when there is a single intersection point in the list
+   does the cursor advance in the original segment. In either case,
+   the new vector is intersection tested against both neighbors. It
+   also causes the vector over which the cursor is advancing to be
+   added to the new svp.
+
+   Two steps need further clarification:
+
+   Intersection testing: the 1st ip..2nd ip lines of the neighbors
+   are tested to see if they cross (using intersect_lines). If so,
+   then the intersection point is added to the ip list of both
+   segments, maintaining the invariant that the list of intersection
+   points is nondecreasing in y).
+
+   Adding vector to new svp: if the new vector shares a top x
+   coordinate with another vector, then it is checked to see whether
+   it is in order. If not, then both segments are "broken," and then
+   restarted. Note: in the case when both segments are in the same
+   order, they may simply be swapped without breaking.
+
+   For the time being, I'm going to put some of these operations into
+   subroutines. If it turns out to be a performance problem, I could
+   try to reorganize the traverse procedure so that each is only
+   called once, and inline them. But if it's not a performance
+   problem, I'll just keep it this way, because it will probably help
+   to make the code clearer, and I believe this code could use all the
+   clarity it can get. */
+/**
+ * art_svp_uncross: Resolve self-intersections of an svp.
+ * @vp: The original svp.
+ *
+ * Finds all the intersections within @vp, and constructs a new svp
+ * with new points added at these intersections.
+ *
+ * This routine needs to be redone from scratch with numerical robustness
+ * in mind. I'm working on it.
+ *
+ * Return value: The new svp.
+ **/
+ArtSVP *
+art_svp_uncross (ArtSVP *vp)
+{
+  int *active_segs;
+  int n_active_segs;
+  int *cursor;
+  int seg_idx;
+  double y;
+  int tmp1, tmp2;
+  int asi;
+  int i, j;
+  /* new data structures */
+  /* intersection points; invariant: *ips[i] is only allocated if
+     i is active */
+  int *n_ips, *n_ips_max;
+  ArtPoint **ips;
+  /* new sorted vector path */
+  int n_segs_max, seg_num;
+  ArtSVP *new_vp;
+  int *n_points_max;
+  /* mapping from argument to new segment numbers - again, only valid
+   if active */
+  int *seg_map;
+  double y_curs;
+  ArtPoint p_curs;
+  int first_share;
+  double share_x;
+  ArtPoint *pts;
+
+  n_segs_max = 16;
+  new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+				(n_segs_max - 1) * sizeof(ArtSVPSeg));
+  new_vp->n_segs = 0;
+
+  if (vp->n_segs == 0)
+    return new_vp;
+
+  active_segs = art_new (int, vp->n_segs);
+  cursor = art_new (int, vp->n_segs);
+
+  seg_map = art_new (int, vp->n_segs);
+  n_ips = art_new (int, vp->n_segs);
+  n_ips_max = art_new (int, vp->n_segs);
+  ips = art_new (ArtPoint *, vp->n_segs);
+
+  n_points_max = art_new (int, n_segs_max);
+
+  n_active_segs = 0;
+  seg_idx = 0;
+  y = vp->segs[0].points[0].y;
+  while (seg_idx < vp->n_segs || n_active_segs > 0)
+    {
+#ifdef VERBOSE
+      printf ("y = %g\n", y);
+#endif
+
+      /* maybe move deletions to end of loop (to avoid so much special
+	 casing on the end of a segment)? */
+
+      /* delete segments ending at y from active list */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  if (vp->segs[asi].n_points - 1 == cursor[asi] &&
+	      vp->segs[asi].points[cursor[asi]].y == y)
+	    {
+	      do
+		{
+#ifdef VERBOSE
+		  printf ("deleting %d\n", asi);
+#endif
+		  art_free (ips[asi]);
+		  n_active_segs--;
+		  for (j = i; j < n_active_segs; j++)
+		    active_segs[j] = active_segs[j + 1];
+		  if (i < n_active_segs)
+		    asi = active_segs[i];
+		  else
+		    break;
+		}
+	      while (vp->segs[asi].n_points - 1 == cursor[asi] &&
+		     vp->segs[asi].points[cursor[asi]].y == y);
+
+	      /* test intersection of neighbors */
+	      if (i > 0 && i < n_active_segs)
+		intersect_neighbors (i, active_segs,
+				     n_ips, n_ips_max, ips,
+				     cursor, vp);
+
+	      i--;
+	    }
+	}
+
+      /* insert new segments into the active list */
+      while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
+	{
+#ifdef VERBOSE
+	  printf ("inserting %d\n", seg_idx);
+#endif
+	  cursor[seg_idx] = 0;
+	  for (i = 0; i < n_active_segs; i++)
+	    {
+	      asi = active_segs[i];
+	      if (x_order_2 (vp->segs[seg_idx].points[0],
+			     vp->segs[seg_idx].points[1],
+			     vp->segs[asi].points[cursor[asi]],
+			     vp->segs[asi].points[cursor[asi] + 1]) == -1)
+		break;
+	    }
+
+	  /* Create and initialize the intersection points data structure */
+	  n_ips[seg_idx] = 1;
+	  n_ips_max[seg_idx] = 2;
+	  ips[seg_idx] = art_new (ArtPoint, n_ips_max[seg_idx]);
+	  ips[seg_idx][0] = vp->segs[seg_idx].points[0];
+
+	  /* Start a new segment in the new vector path */
+	  pts = art_new (ArtPoint, 16);
+	  pts[0] = vp->segs[seg_idx].points[0];
+	  seg_num = art_svp_add_segment (&new_vp, &n_segs_max,
+					 &n_points_max,
+					 1, vp->segs[seg_idx].dir,
+					 pts,
+					 NULL);
+	  n_points_max[seg_num] = 16;
+	  seg_map[seg_idx] = seg_num;
+
+	  tmp1 = seg_idx;
+	  for (j = i; j < n_active_segs; j++)
+	    {
+	      tmp2 = active_segs[j];
+	      active_segs[j] = tmp1;
+	      tmp1 = tmp2;
+	    }
+	  active_segs[n_active_segs] = tmp1;
+	  n_active_segs++;
+
+	  if (i > 0)
+	    intersect_neighbors (i, active_segs,
+				 n_ips, n_ips_max, ips,
+				 cursor, vp);
+
+	  if (i + 1 < n_active_segs)
+	    intersect_neighbors (i + 1, active_segs,
+				 n_ips, n_ips_max, ips,
+				 cursor, vp);
+
+	  seg_idx++;
+	}
+
+      /* all active segs cross the y scanline (considering segs to be
+       closed on top and open on bottom) */
+#ifdef VERBOSE
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  printf ("%d ", asi);
+	  for (j = 0; j < n_ips[asi]; j++)
+	    printf ("(%g, %g) - ",
+		    ips[asi][j].x,
+		    ips[asi][j].y);
+	  printf ("(%g, %g) %s\n",
+		  vp->segs[asi].points[cursor[asi] + 1].x,
+		  vp->segs[asi].points[cursor[asi] + 1].y,
+		  vp->segs[asi].dir ? "v" : "^");
+	}
+#endif
+
+      /* advance y to the next event
+       Note: this is quadratic. We'd probably get decent constant
+       factor speed improvement by caching the y_curs values. */
+      if (n_active_segs == 0)
+	{
+	  if (seg_idx < vp->n_segs)
+	    y = vp->segs[seg_idx].points[0].y;
+	  /* else we're done */
+	}
+      else
+	{
+	  asi = active_segs[0];
+	  if (n_ips[asi] == 1)
+	    y = vp->segs[asi].points[cursor[asi] + 1].y;
+	  else
+	    y = ips[asi][1].y;
+	  for (i = 1; i < n_active_segs; i++)
+	    {
+	      asi = active_segs[i];
+	      if (n_ips[asi] == 1)
+		y_curs = vp->segs[asi].points[cursor[asi] + 1].y;
+	      else
+		y_curs = ips[asi][1].y;
+	      if (y > y_curs)
+		y = y_curs;
+	    }
+	  if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
+	    y = vp->segs[seg_idx].points[0].y;
+	}
+
+      first_share = -1;
+      share_x = 0; /* to avoid gcc warning, although share_x is never
+		      used when first_share is -1 */
+      /* advance cursors to reach new y */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  if (n_ips[asi] == 1)
+	    p_curs = vp->segs[asi].points[cursor[asi] + 1];
+	  else
+	    p_curs = ips[asi][1];
+	  if (p_curs.y == y)
+	    {
+	      svp_add_point (new_vp, n_points_max,
+			     p_curs, seg_map, active_segs, n_active_segs, i);
+
+	      n_ips[asi]--;
+	      for (j = 0; j < n_ips[asi]; j++)
+		ips[asi][j] = ips[asi][j + 1];
+
+	      if (n_ips[asi] == 0)
+		{
+		  ips[asi][0] = p_curs;
+		  n_ips[asi] = 1;
+		  cursor[asi]++;
+		}
+
+	      if (first_share < 0 || p_curs.x != share_x)
+		{
+		  /* this is where crossings are detected, and if
+		     found, the active segments switched around. */
+
+		  fix_crossing (first_share, i,
+				active_segs, n_active_segs,
+				cursor, ips, n_ips, n_ips_max, vp, seg_map,
+				&new_vp,
+				&n_segs_max, &n_points_max);
+
+		  first_share = i;
+		  share_x = p_curs.x;
+		}
+
+	      if (cursor[asi] < vp->segs[asi].n_points - 1)
+		{
+
+		  if (i > 0)
+		    intersect_neighbors (i, active_segs,
+					 n_ips, n_ips_max, ips,
+					 cursor, vp);
+
+		  if (i + 1 < n_active_segs)
+		    intersect_neighbors (i + 1, active_segs,
+					 n_ips, n_ips_max, ips,
+					 cursor, vp);
+		}
+	    }
+	  else
+	    {
+	      /* not on a cursor point */
+	      fix_crossing (first_share, i,
+			    active_segs, n_active_segs,
+			    cursor, ips, n_ips, n_ips_max, vp, seg_map,
+			    &new_vp,
+			    &n_segs_max, &n_points_max);
+	      first_share = -1;
+	    }
+	}
+
+      /* fix crossing on last shared group */
+      fix_crossing (first_share, i,
+		    active_segs, n_active_segs,
+		    cursor, ips, n_ips, n_ips_max, vp, seg_map,
+		    &new_vp,
+		    &n_segs_max, &n_points_max);
+
+#ifdef VERBOSE
+      printf ("\n");
+#endif
+    }
+
+  /* not necessary to sort, new segments only get added at y, which
+     increases monotonically */
+#if 0
+  qsort (&new_vp->segs, new_vp->n_segs, sizeof (svp_seg), svp_seg_compare);
+  {
+    int k;
+    for (k = 0; k < new_vp->n_segs - 1; k++)
+      {
+	printf ("(%g, %g) - (%g, %g) %s (%g, %g) - (%g, %g)\n",
+		new_vp->segs[k].points[0].x,
+		new_vp->segs[k].points[0].y,
+		new_vp->segs[k].points[1].x,
+		new_vp->segs[k].points[1].y,
+		svp_seg_compare (&new_vp->segs[k], &new_vp->segs[k + 1]) > 1 ? ">": "<",
+		new_vp->segs[k + 1].points[0].x,
+		new_vp->segs[k + 1].points[0].y,
+		new_vp->segs[k + 1].points[1].x,
+		new_vp->segs[k + 1].points[1].y);
+      }
+  }
+#endif
+
+  art_free (n_points_max);
+  art_free (seg_map);
+  art_free (n_ips_max);
+  art_free (n_ips);
+  art_free (ips);
+  art_free (cursor);
+  art_free (active_segs);
+
+  return new_vp;
+}
+
+#define noVERBOSE
+
+/* Rewind a svp satisfying the nocross invariant.
+
+   The winding number of a segment is defined as the winding number of
+   the points to the left while travelling in the direction of the
+   segment. Therefore it preincrements and postdecrements as a scan
+   line is traversed from left to right.
+
+   Status of this routine:
+
+   Basic correctness: Was ok in gfonted. However, this code does not
+   yet compute bboxes for the resulting svp segs.
+
+   Numerical stability: known problems in the case of horizontal
+   segments in polygons with any complexity. For actual use, randomly
+   perturbing the vertices is recommended.
+
+   Speed: good.
+
+   Precision: good, except that no attempt is made to remove "hair".
+   Doing random perturbation just makes matters worse.
+
+*/
+/**
+ * art_svp_rewind_uncrossed: Rewind an svp satisfying the nocross invariant.
+ * @vp: The original svp.
+ * @rule: The winding rule.
+ *
+ * Creates a new svp with winding number of 0 or 1 everywhere. The @rule
+ * argument specifies a rule for how winding numbers in the original
+ * @vp map to the winding numbers in the result.
+ *
+ * With @rule == ART_WIND_RULE_NONZERO, the resulting svp has a
+ * winding number of 1 where @vp has a nonzero winding number.
+ *
+ * With @rule == ART_WIND_RULE_INTERSECT, the resulting svp has a
+ * winding number of 1 where @vp has a winding number greater than
+ * 1. It is useful for computing intersections.
+ *
+ * With @rule == ART_WIND_RULE_ODDEVEN, the resulting svp has a
+ * winding number of 1 where @vp has an odd winding number. It is
+ * useful for implementing the even-odd winding rule of the
+ * PostScript imaging model.
+ *
+ * With @rule == ART_WIND_RULE_POSITIVE, the resulting svp has a
+ * winding number of 1 where @vp has a positive winding number. It is
+ * useful for implementing asymmetric difference.
+ *
+ * This routine needs to be redone from scratch with numerical robustness
+ * in mind. I'm working on it.
+ *
+ * Return value: The new svp.
+ **/
+ArtSVP *
+art_svp_rewind_uncrossed (ArtSVP *vp, ArtWindRule rule)
+{
+  int *active_segs;
+  int n_active_segs;
+  int *cursor;
+  int seg_idx;
+  double y;
+  int tmp1, tmp2;
+  int asi;
+  int i, j;
+
+  ArtSVP *new_vp;
+  int n_segs_max;
+  int *winding;
+  int left_wind;
+  int wind;
+  int keep, invert;
+
+#ifdef VERBOSE
+  print_svp (vp);
+#endif
+  n_segs_max = 16;
+  new_vp = (ArtSVP *)art_alloc (sizeof(ArtSVP) +
+				(n_segs_max - 1) * sizeof(ArtSVPSeg));
+  new_vp->n_segs = 0;
+
+  if (vp->n_segs == 0)
+    return new_vp;
+
+  winding = art_new (int, vp->n_segs);
+
+  active_segs = art_new (int, vp->n_segs);
+  cursor = art_new (int, vp->n_segs);
+
+  n_active_segs = 0;
+  seg_idx = 0;
+  y = vp->segs[0].points[0].y;
+  while (seg_idx < vp->n_segs || n_active_segs > 0)
+    {
+#ifdef VERBOSE
+      printf ("y = %g\n", y);
+#endif
+      /* delete segments ending at y from active list */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  if (vp->segs[asi].n_points - 1 == cursor[asi] &&
+	      vp->segs[asi].points[cursor[asi]].y == y)
+	    {
+#ifdef VERBOSE
+	      printf ("deleting %d\n", asi);
+#endif
+	      n_active_segs--;
+	      for (j = i; j < n_active_segs; j++)
+		active_segs[j] = active_segs[j + 1];
+	      i--;
+	    }
+	}
+
+      /* insert new segments into the active list */
+      while (seg_idx < vp->n_segs && y == vp->segs[seg_idx].points[0].y)
+	{
+#ifdef VERBOSE
+	  printf ("inserting %d\n", seg_idx);
+#endif
+	  cursor[seg_idx] = 0;
+	  for (i = 0; i < n_active_segs; i++)
+	    {
+	      asi = active_segs[i];
+	      if (x_order_2 (vp->segs[seg_idx].points[0],
+			     vp->segs[seg_idx].points[1],
+			     vp->segs[asi].points[cursor[asi]],
+			     vp->segs[asi].points[cursor[asi] + 1]) == -1)
+		break;
+	    }
+
+	  /* Determine winding number for this segment */
+	  if (i == 0)
+	    left_wind = 0;
+	  else if (vp->segs[active_segs[i - 1]].dir)
+	    left_wind = winding[active_segs[i - 1]];
+	  else
+	    left_wind = winding[active_segs[i - 1]] - 1;
+
+	  if (vp->segs[seg_idx].dir)
+	    wind = left_wind + 1;
+	  else
+	    wind = left_wind;
+
+	  winding[seg_idx] = wind;
+
+	  switch (rule)
+	    {
+	    case ART_WIND_RULE_NONZERO:
+	      keep = (wind == 1 || wind == 0);
+	      invert = (wind == 0);
+	      break;
+	    case ART_WIND_RULE_INTERSECT:
+	      keep = (wind == 2);
+	      invert = 0;
+	      break;
+	    case ART_WIND_RULE_ODDEVEN:
+	      keep = 1;
+	      invert = !(wind & 1);
+	      break;
+	    case ART_WIND_RULE_POSITIVE:
+	      keep = (wind == 1);
+	      invert = 0;
+	      break;
+	    default:
+	      keep = 0;
+	      invert = 0;
+	      break;
+	    }
+
+	  if (keep)
+	    {
+	      ArtPoint *points, *new_points;
+	      int n_points;
+	      int new_dir;
+
+#ifdef VERBOSE
+	      printf ("keeping segment %d\n", seg_idx);
+#endif
+	      n_points = vp->segs[seg_idx].n_points;
+	      points = vp->segs[seg_idx].points;
+	      new_points = art_new (ArtPoint, n_points);
+	      memcpy (new_points, points, n_points * sizeof (ArtPoint));
+	      new_dir = vp->segs[seg_idx].dir ^ invert;
+	      art_svp_add_segment (&new_vp, &n_segs_max,
+				   NULL,
+				   n_points, new_dir, new_points,
+				   &vp->segs[seg_idx].bbox);
+	    }
+
+	  tmp1 = seg_idx;
+	  for (j = i; j < n_active_segs; j++)
+	    {
+	      tmp2 = active_segs[j];
+	      active_segs[j] = tmp1;
+	      tmp1 = tmp2;
+	    }
+	  active_segs[n_active_segs] = tmp1;
+	  n_active_segs++;
+	  seg_idx++;
+	}
+
+#ifdef VERBOSE
+      /* all active segs cross the y scanline (considering segs to be
+       closed on top and open on bottom) */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  printf ("%d:%d (%g, %g) - (%g, %g) %s %d\n", asi,
+		  cursor[asi],
+		  vp->segs[asi].points[cursor[asi]].x,
+		  vp->segs[asi].points[cursor[asi]].y,
+		  vp->segs[asi].points[cursor[asi] + 1].x,
+		  vp->segs[asi].points[cursor[asi] + 1].y,
+		  vp->segs[asi].dir ? "v" : "^",
+		  winding[asi]);
+	}
+#endif
+
+      /* advance y to the next event */
+      if (n_active_segs == 0)
+	{
+	  if (seg_idx < vp->n_segs)
+	    y = vp->segs[seg_idx].points[0].y;
+	  /* else we're done */
+	}
+      else
+	{
+	  asi = active_segs[0];
+	  y = vp->segs[asi].points[cursor[asi] + 1].y;
+	  for (i = 1; i < n_active_segs; i++)
+	    {
+	      asi = active_segs[i];
+	      if (y > vp->segs[asi].points[cursor[asi] + 1].y)
+		y = vp->segs[asi].points[cursor[asi] + 1].y;
+	    }
+	  if (seg_idx < vp->n_segs && y > vp->segs[seg_idx].points[0].y)
+	    y = vp->segs[seg_idx].points[0].y;
+	}
+
+      /* advance cursors to reach new y */
+      for (i = 0; i < n_active_segs; i++)
+	{
+	  asi = active_segs[i];
+	  while (cursor[asi] < vp->segs[asi].n_points - 1 &&
+		 y >= vp->segs[asi].points[cursor[asi] + 1].y)
+	    cursor[asi]++;
+	}
+#ifdef VERBOSE
+      printf ("\n");
+#endif
+    }
+  art_free (cursor);
+  art_free (active_segs);
+  art_free (winding);
+
+  return new_vp;
+}
diff --git a/src/libart_lgpl/art_svp_wind.h b/src/libart_lgpl/art_svp_wind.h
new file mode 100644
index 0000000..dee8eaa
--- /dev/null
+++ b/src/libart_lgpl/art_svp_wind.h
@@ -0,0 +1,56 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_SVP_WIND_H__
+#define __ART_SVP_WIND_H__
+
+/* Primitive intersection and winding number operations on sorted
+   vector paths. */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifndef ART_WIND_RULE_DEFINED
+#define ART_WIND_RULE_DEFINED
+typedef enum {
+  ART_WIND_RULE_NONZERO,
+  ART_WIND_RULE_INTERSECT,
+  ART_WIND_RULE_ODDEVEN,
+  ART_WIND_RULE_POSITIVE
+} ArtWindRule;
+#endif
+
+ArtSVP *
+art_svp_uncross (ArtSVP *vp);
+
+ArtSVP *
+art_svp_rewind_uncrossed (ArtSVP *vp, ArtWindRule rule);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_SVP_WIND_H__ */
diff --git a/src/libart_lgpl/art_uta.c b/src/libart_lgpl/art_uta.c
new file mode 100644
index 0000000..f3a2d0a
--- /dev/null
+++ b/src/libart_lgpl/art_uta.c
@@ -0,0 +1,87 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta.h"
+
+#include <string.h>
+#include "art_misc.h"
+
+/**
+ * art_uta_new: Allocate a new uta.
+ * @x0: Left coordinate of uta.
+ * @y0: Top coordinate of uta.
+ * @x1: Right coordinate of uta.
+ * @y1: Bottom coordinate of uta.
+ *
+ * Allocates a new microtile array. The arguments are in units of
+ * tiles, not pixels.
+ *
+ * Returns: the newly allocated #ArtUta.
+ **/
+ArtUta *
+art_uta_new (int x0, int y0, int x1, int y1)
+{
+  ArtUta *uta;
+
+  uta = art_new (ArtUta, 1);
+  uta->x0 = x0;
+  uta->y0 = y0;
+  uta->width = x1 - x0;
+  uta->height = y1 - y0;
+
+  uta->utiles = art_new (ArtUtaBbox, uta->width * uta->height);
+
+  memset (uta->utiles, 0, uta->width * uta->height * sizeof(ArtUtaBbox));
+  return uta;
+  }
+
+/**
+ * art_uta_new_coords: Allocate a new uta, based on pixel coordinates.
+ * @x0: Left coordinate of uta.
+ * @y0: Top coordinate of uta.
+ * @x1: Right coordinate of uta.
+ * @y1: Bottom coordinate of uta.
+ *
+ * Allocates a new microtile array. The arguments are in pixels
+ *
+ * Returns: the newly allocated #ArtUta.
+ **/
+ArtUta *
+art_uta_new_coords (int x0, int y0, int x1, int y1)
+{
+  return art_uta_new (x0 >> ART_UTILE_SHIFT, y0 >> ART_UTILE_SHIFT,
+		      1 + (x1 >> ART_UTILE_SHIFT),
+		      1 + (y1 >> ART_UTILE_SHIFT));
+}
+
+/**
+ * art_uta_free: Free a uta.
+ * @uta: The uta to free.
+ *
+ * Frees the microtile array structure, including the actual microtile
+ * data.
+ **/
+void
+art_uta_free (ArtUta *uta)
+{
+  art_free (uta->utiles);
+  art_free (uta);
+}
+
+/* User to Aardvark! */
diff --git a/src/libart_lgpl/art_uta.h b/src/libart_lgpl/art_uta.h
new file mode 100644
index 0000000..0401df7
--- /dev/null
+++ b/src/libart_lgpl/art_uta.h
@@ -0,0 +1,71 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_H__
+#define __ART_UTA_H__
+
+/* Basic data structures and constructors for microtile arrays */
+
+#ifdef LIBART_COMPILATION
+#include "art_misc.h"
+#else
+#include <libart_lgpl/art_misc.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef art_u32 ArtUtaBbox;
+typedef struct _ArtUta ArtUta;
+
+#define ART_UTA_BBOX_CONS(x0, y0, x1, y1) (((x0) << 24) | ((y0) << 16) | \
+				       ((x1) << 8) | (y1))
+
+#define ART_UTA_BBOX_X0(ub) ((ub) >> 24)
+#define ART_UTA_BBOX_Y0(ub) (((ub) >> 16) & 0xff)
+#define ART_UTA_BBOX_X1(ub) (((ub) >> 8) & 0xff)
+#define ART_UTA_BBOX_Y1(ub) ((ub) & 0xff)
+
+#define ART_UTILE_SHIFT 5
+#define ART_UTILE_SIZE (1 << ART_UTILE_SHIFT)
+
+/* Coordinates are shifted right by ART_UTILE_SHIFT wrt the real
+   coordinates. */
+struct _ArtUta {
+  int x0;
+  int y0;
+  int width;
+  int height;
+  ArtUtaBbox *utiles;
+};
+
+ArtUta *
+art_uta_new (int x0, int y0, int x1, int y1);
+
+ArtUta *
+art_uta_new_coords (int x0, int y0, int x1, int y1);
+
+void
+art_uta_free (ArtUta *uta);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_H__ */
diff --git a/src/libart_lgpl/art_uta_ops.c b/src/libart_lgpl/art_uta_ops.c
new file mode 100644
index 0000000..af23b9e
--- /dev/null
+++ b/src/libart_lgpl/art_uta_ops.c
@@ -0,0 +1,111 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta_ops.h"
+
+#include <string.h>
+#include "art_misc.h"
+#include "art_uta.h"
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+/**
+ * art_uta_union: Compute union of two uta's.
+ * @uta1: One uta.
+ * @uta2: The other uta.
+ *
+ * Computes the union of @uta1 and @uta2. The union is approximate,
+ * but coverage is guaranteed over all pixels included in either of
+ * the arguments, ie more pixels may be covered than the "exact"
+ * union.
+ *
+ * Note: this routine is used in the Gnome Canvas to accumulate the
+ * region that needs to be repainted. However, since it copies over
+ * the entire uta (which might be largish) even when the update may be
+ * small, it can be a performance bottleneck. There are two approaches
+ * to this problem, both of which are probably worthwhile. First, the
+ * generated uta's should always be limited to the visible window,
+ * thus guaranteeing that uta's never become large. Second, there
+ * should be a new, destructive union operation that only touches a
+ * small part of the uta when the update is small.
+ *
+ * Return value: The new union uta.
+ **/
+ArtUta *
+art_uta_union (ArtUta *uta1, ArtUta *uta2)
+{
+  ArtUta *uta;
+  int x0, y0, x1, y1;
+  int x, y;
+  int ix, ix1, ix2;
+  ArtUtaBbox bb, bb1, bb2;
+
+  x0 = MIN(uta1->x0, uta2->x0);
+  y0 = MIN(uta1->y0, uta2->y0);
+  x1 = MAX(uta1->x0 + uta1->width, uta2->x0 + uta2->width);
+  y1 = MAX(uta1->y0 + uta1->height, uta2->y0 + uta2->height);
+  uta = art_uta_new (x0, y0, x1, y1);
+
+  /* could move the first two if/else statements out of the loop */
+  ix = 0;
+  for (y = y0; y < y1; y++)
+    {
+      ix1 = (y - uta1->y0) * uta1->width + x0 - uta1->x0;
+      ix2 = (y - uta2->y0) * uta2->width + x0 - uta2->x0;
+      for (x = x0; x < x1; x++)
+	{
+	  if (x < uta1->x0 || y < uta1->y0 ||
+	      x >= uta1->x0 + uta1->width || y >= uta1->y0 + uta1->height)
+	    bb1 = 0;
+	  else
+	    bb1 = uta1->utiles[ix1];
+
+	  if (x < uta2->x0 || y < uta2->y0 ||
+	      x >= uta2->x0 + uta2->width || y >= uta2->y0 + uta2->height)
+	    bb2 = 0;
+	  else
+	    bb2 = uta2->utiles[ix2];
+
+	  if (bb1 == 0)
+	    bb = bb2;
+	  else if (bb2 == 0)
+	    bb = bb1;
+	  else
+	    bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb1),
+				       ART_UTA_BBOX_X0(bb2)),
+				   MIN(ART_UTA_BBOX_Y0(bb1),
+				       ART_UTA_BBOX_Y0(bb2)),
+				   MAX(ART_UTA_BBOX_X1(bb1),
+				       ART_UTA_BBOX_X1(bb2)),
+				   MAX(ART_UTA_BBOX_Y1(bb1),
+				       ART_UTA_BBOX_Y1(bb2)));
+	  uta->utiles[ix] = bb;
+	  ix++;
+	  ix1++;
+	  ix2++;
+	}
+    }
+  return uta;
+}
diff --git a/src/libart_lgpl/art_uta_ops.h b/src/libart_lgpl/art_uta_ops.h
new file mode 100644
index 0000000..58ff3fb
--- /dev/null
+++ b/src/libart_lgpl/art_uta_ops.h
@@ -0,0 +1,41 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_OPS_H__
+#define __ART_UTA_OPS_H__
+
+/* Basic operations on microtile arrays */
+
+#ifdef LIBART_COMPILATION
+#include "art_uta.h"
+#else
+#include <libart_lgpl/art_uta.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_union (ArtUta *uta1, ArtUta *uta2);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_OPS_H__ */
diff --git a/src/libart_lgpl/art_uta_rect.c b/src/libart_lgpl/art_uta_rect.c
new file mode 100644
index 0000000..4be4ff2
--- /dev/null
+++ b/src/libart_lgpl/art_uta_rect.c
@@ -0,0 +1,110 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta_rect.h"
+
+#include "art_misc.h"
+#include "art_uta.h"
+#include "art_rect.h"
+
+/**
+ * art_uta_from_irect: Generate uta covering a rectangle.
+ * @bbox: The source rectangle.
+ *
+ * Generates a uta exactly covering @bbox. Please do not call this
+ * function with a @bbox with zero height or width.
+ *
+ * Return value: the new uta.
+ **/
+ArtUta *
+art_uta_from_irect (ArtIRect *bbox)
+{
+  ArtUta *uta;
+  ArtUtaBbox *utiles;
+  ArtUtaBbox bb;
+  int width, height;
+  int x, y;
+  int xf0, yf0, xf1, yf1;
+  int ix;
+
+  uta = art_new (ArtUta, 1);
+  uta->x0 = bbox->x0 >> ART_UTILE_SHIFT;
+  uta->y0 = bbox->y0 >> ART_UTILE_SHIFT;
+  width = ((bbox->x1 + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT) - uta->x0;
+  height = ((bbox->y1 + ART_UTILE_SIZE - 1) >> ART_UTILE_SHIFT) - uta->y0;
+  utiles = art_new (ArtUtaBbox, width * height);
+
+  uta->width = width;
+  uta->height = height;
+  uta->utiles = utiles;
+
+  xf0 = bbox->x0 & (ART_UTILE_SIZE - 1);
+  yf0 = bbox->y0 & (ART_UTILE_SIZE - 1);
+  xf1 = ((bbox->x1 - 1) & (ART_UTILE_SIZE - 1)) + 1;
+  yf1 = ((bbox->y1 - 1) & (ART_UTILE_SIZE - 1)) + 1;
+  if (height == 1)
+    {
+      if (width == 1)
+	utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, xf1, yf1);
+      else
+	{
+	  utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, ART_UTILE_SIZE, yf1);
+	  bb = ART_UTA_BBOX_CONS (0, yf0, ART_UTILE_SIZE, yf1);
+	  for (x = 1; x < width - 1; x++)
+	    utiles[x] = bb;
+	  utiles[x] = ART_UTA_BBOX_CONS (0, yf0, xf1, yf1);
+	}
+    }
+  else
+    {
+      if (width == 1)
+	{
+	  utiles[0] = ART_UTA_BBOX_CONS (xf0, yf0, xf1, ART_UTILE_SIZE);
+	  bb = ART_UTA_BBOX_CONS (xf0, 0, xf1, ART_UTILE_SIZE);
+	  for (y = 1; y < height - 1; y++)
+	    utiles[y] = bb;
+	  utiles[y] = ART_UTA_BBOX_CONS (xf0, 0, xf1, yf1);
+	}
+      else
+	{
+	  utiles[0] =
+	    ART_UTA_BBOX_CONS (xf0, yf0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+	  bb = ART_UTA_BBOX_CONS (0, yf0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+	  for (x = 1; x < width - 1; x++)
+	    utiles[x] = bb;
+	  utiles[x] = ART_UTA_BBOX_CONS (0, yf0, xf1, ART_UTILE_SIZE);
+	  ix = width;
+	  for (y = 1; y < height - 1; y++)
+	    {
+	      utiles[ix++] =
+		ART_UTA_BBOX_CONS (xf0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+	      bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, ART_UTILE_SIZE);
+	      for (x = 1; x < width - 1; x++)
+		utiles[ix++] = bb;
+	      utiles[ix++] = ART_UTA_BBOX_CONS (0, 0, xf1, ART_UTILE_SIZE);
+	    }
+	  utiles[ix++] = ART_UTA_BBOX_CONS (xf0, 0, ART_UTILE_SIZE, yf1);
+	  bb = ART_UTA_BBOX_CONS (0, 0, ART_UTILE_SIZE, yf1);
+	  for (x = 1; x < width - 1; x++)
+	    utiles[ix++] = bb;
+	  utiles[ix++] = ART_UTA_BBOX_CONS (0, 0, xf1, yf1);
+	}
+    }
+  return uta;
+}
diff --git a/src/libart_lgpl/art_uta_rect.h b/src/libart_lgpl/art_uta_rect.h
new file mode 100644
index 0000000..47dbe83
--- /dev/null
+++ b/src/libart_lgpl/art_uta_rect.h
@@ -0,0 +1,41 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_RECT_H__
+#define __ART_UTA_RECT_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_rect.h"
+#include "art_uta.h"
+#else
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_uta.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_from_irect (ArtIRect *bbox);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_RECT_H__ */
diff --git a/src/libart_lgpl/art_uta_svp.c b/src/libart_lgpl/art_uta_svp.c
new file mode 100644
index 0000000..1f7daa9
--- /dev/null
+++ b/src/libart_lgpl/art_uta_svp.c
@@ -0,0 +1,53 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* LGPL Copyright 1998 Raph Levien <raph acm org> */
+
+#include "config.h"
+#include "art_uta_svp.h"
+
+#include "art_misc.h"
+#include "art_vpath.h"
+#include "art_uta.h"
+#include "art_uta_vpath.h"
+#include "art_svp.h"
+#include "art_vpath_svp.h"
+
+/**
+ * art_uta_from_svp: Generate uta covering an svp.
+ * @svp: The source svp.
+ *
+ * Generates a uta covering @svp. The resulting uta is of course
+ * approximate, ie it may cover more pixels than covered by @svp.
+ *
+ * Note: I will want to replace this with a more direct
+ * implementation. But this gets the api in place.
+ *
+ * Return value: the new uta.
+ **/
+ArtUta *
+art_uta_from_svp (const ArtSVP *svp)
+{
+  ArtVpath *vpath;
+  ArtUta *uta;
+
+  vpath = art_vpath_from_svp (svp);
+  uta = art_uta_from_vpath (vpath);
+  art_free (vpath);
+  return uta;
+}
diff --git a/src/libart_lgpl/art_uta_svp.h b/src/libart_lgpl/art_uta_svp.h
new file mode 100644
index 0000000..527acce
--- /dev/null
+++ b/src/libart_lgpl/art_uta_svp.h
@@ -0,0 +1,44 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_SVP_H__
+#define __ART_UTA_SVP_H__
+
+/* Basic data structures and constructors for microtile arrays */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#include "art_uta.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_uta.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_from_svp (const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_SVP_H__ */
+
diff --git a/src/libart_lgpl/art_uta_vpath.c b/src/libart_lgpl/art_uta_vpath.c
new file mode 100644
index 0000000..b95a4ea
--- /dev/null
+++ b/src/libart_lgpl/art_uta_vpath.c
@@ -0,0 +1,381 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "art_uta_vpath.h"
+
+#include <math.h>
+
+#include "art_misc.h"
+#include "art_vpath.h"
+#include "art_uta.h"
+
+#ifndef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#endif /* MIN */
+
+/**
+ * art_uta_add_line: Add a line to the uta.
+ * @uta: The uta to modify.
+ * @x0: X coordinate of line start point.
+ * @y0: Y coordinate of line start point.
+ * @x1: X coordinate of line end point.
+ * @y1: Y coordinate of line end point.
+ * @rbuf: Buffer containing first difference of winding number.
+ * @rbuf_rowstride: Rowstride of @rbuf.
+ *
+ * Add the line (@x0, @y0) - (@x1, @y1) to @uta, and also update the
+ * winding number buffer used for rendering the interior. @rbuf
+ * contains the first partial difference (in the X direction) of the
+ * winding number, measured in grid cells. Thus, each time that a line
+ * crosses a horizontal uta grid line, an entry of @rbuf is
+ * incremented if @y1 > @y0, decremented otherwise.
+ *
+ * Note that edge handling is fairly delicate. Please rtfs for
+ * details.
+ **/
+void
+art_uta_add_line (ArtUta *uta, double x0, double y0, double x1, double y1,
+		  int *rbuf, int rbuf_rowstride)
+{
+  int xmin, ymin;
+  double xmax, ymax;
+  int xmaxf, ymaxf;
+  int xmaxc, ymaxc;
+  int xt0, yt0;
+  int xt1, yt1;
+  int xf0, yf0;
+  int xf1, yf1;
+  int ix, ix1;
+  ArtUtaBbox bb;
+
+  xmin = floor (MIN(x0, x1));
+  xmax = MAX(x0, x1);
+  xmaxf = floor (xmax);
+  xmaxc = ceil (xmax);
+  ymin = floor (MIN(y0, y1));
+  ymax = MAX(y0, y1);
+  ymaxf = floor (ymax);
+  ymaxc = ceil (ymax);
+  xt0 = (xmin >> ART_UTILE_SHIFT) - uta->x0;
+  yt0 = (ymin >> ART_UTILE_SHIFT) - uta->y0;
+  xt1 = (xmaxf >> ART_UTILE_SHIFT) - uta->x0;
+  yt1 = (ymaxf >> ART_UTILE_SHIFT) - uta->y0;
+  if (xt0 == xt1 && yt0 == yt1)
+    {
+      /* entirely inside a microtile, this is easy! */
+      xf0 = xmin & (ART_UTILE_SIZE - 1);
+      yf0 = ymin & (ART_UTILE_SIZE - 1);
+      xf1 = (xmaxf & (ART_UTILE_SIZE - 1)) + xmaxc - xmaxf;
+      yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf;
+
+      ix = yt0 * uta->width + xt0;
+      bb = uta->utiles[ix];
+      if (bb == 0)
+	bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1);
+      else
+	bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+			   MIN(ART_UTA_BBOX_Y0(bb), yf0),
+			   MAX(ART_UTA_BBOX_X1(bb), xf1),
+			   MAX(ART_UTA_BBOX_Y1(bb), yf1));
+      uta->utiles[ix] = bb;
+    }
+  else
+    {
+      double dx, dy;
+      int sx, sy;
+
+      dx = x1 - x0;
+      dy = y1 - y0;
+      sx = dx > 0 ? 1 : dx < 0 ? -1 : 0;
+      sy = dy > 0 ? 1 : dy < 0 ? -1 : 0;
+      if (ymin == ymaxf)
+	{
+	  /* special case horizontal (dx/dy slope would be infinite) */
+	  xf0 = xmin & (ART_UTILE_SIZE - 1);
+	  yf0 = ymin & (ART_UTILE_SIZE - 1);
+	  xf1 = (xmaxf & (ART_UTILE_SIZE - 1)) + xmaxc - xmaxf;
+	  yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf;
+
+	  ix = yt0 * uta->width + xt0;
+	  ix1 = yt0 * uta->width + xt1;
+	  while (ix != ix1)
+	    {
+	      bb = uta->utiles[ix];
+	      if (bb == 0)
+		bb = ART_UTA_BBOX_CONS(xf0, yf0, ART_UTILE_SIZE, yf1);
+	      else
+		bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+				   MIN(ART_UTA_BBOX_Y0(bb), yf0),
+				   ART_UTILE_SIZE,
+				   MAX(ART_UTA_BBOX_Y1(bb), yf1));
+	      uta->utiles[ix] = bb;
+	      xf0 = 0;
+	      ix++;
+	    }
+	  bb = uta->utiles[ix];
+	  if (bb == 0)
+	    bb = ART_UTA_BBOX_CONS(0, yf0, xf1, yf1);
+	  else
+	    bb = ART_UTA_BBOX_CONS(0,
+			       MIN(ART_UTA_BBOX_Y0(bb), yf0),
+			       MAX(ART_UTA_BBOX_X1(bb), xf1),
+			       MAX(ART_UTA_BBOX_Y1(bb), yf1));
+	  uta->utiles[ix] = bb;
+	}
+      else
+	{
+	  /* Do a Bresenham-style traversal of the line */
+	  double dx_dy;
+	  double x, y;
+	  double xn, yn;
+
+	  /* normalize coordinates to uta origin */
+	  x0 -= uta->x0 << ART_UTILE_SHIFT;
+	  y0 -= uta->y0 << ART_UTILE_SHIFT;
+	  x1 -= uta->x0 << ART_UTILE_SHIFT;
+	  y1 -= uta->y0 << ART_UTILE_SHIFT;
+	  if (dy < 0)
+	    {
+	      double tmp;
+
+	      tmp = x0;
+	      x0 = x1;
+	      x1 = tmp;
+
+	      tmp = y0;
+	      y0 = y1;
+	      y1 = tmp;
+
+	      dx = -dx;
+	      sx = -sx;
+	      dy = -dy;
+	      /* we leave sy alone, because it would always be 1,
+		 and we need it for the rbuf stuff. */
+	    }
+	  xt0 = ((int)floor (x0) >> ART_UTILE_SHIFT);
+	  xt1 = ((int)floor (x1) >> ART_UTILE_SHIFT);
+	  /* now [xy]0 is above [xy]1 */
+
+	  ix = yt0 * uta->width + xt0;
+	  ix1 = yt1 * uta->width + xt1;
+#ifdef VERBOSE
+	  printf ("%% ix = %d,%d; ix1 = %d,%d\n", xt0, yt0, xt1, yt1);
+#endif
+
+	  dx_dy = dx / dy;
+	  x = x0;
+	  y = y0;
+	  while (ix != ix1)
+	    {
+	      int dix;
+
+	      /* figure out whether next crossing is horizontal or vertical */
+#ifdef VERBOSE
+	      printf ("%% %d,%d\n", xt0, yt0);
+#endif
+	      yn = (yt0 + 1) << ART_UTILE_SHIFT;
+
+	      /* xn is the intercept with bottom edge of this tile. The
+		 following expression is careful to result in exactly
+		 x1 when yn = y1. */
+	      xn = x1 + dx_dy * (yn - y1);
+
+	      if (xt0 != (int)floor (xn) >> ART_UTILE_SHIFT)
+		{
+		  /* horizontal crossing */
+		  xt0 += sx;
+		  dix = sx;
+		  if (dx > 0)
+		    {
+		      xn = xt0 << ART_UTILE_SHIFT;
+		      yn = y0 + (xn - x0) / dx_dy;
+
+		      xf0 = (int)floor (x) & (ART_UTILE_SIZE - 1);
+		      xf1 = ART_UTILE_SIZE;
+		    }
+		  else
+		    {
+		      xn = (xt0 + 1) << ART_UTILE_SHIFT;
+		      yn = y0 + (xn - x0) / dx_dy;
+
+		      xf0 = 0;
+		      xmaxc = (int)ceil (x);
+		      xf1 = xmaxc - ((xt0 + 1) << ART_UTILE_SHIFT);
+		    }
+		  ymaxf = (int)floor (yn);
+		  ymaxc = (int)ceil (yn);
+		  yf1 = (ymaxf & (ART_UTILE_SIZE - 1)) + ymaxc - ymaxf;
+		}
+	      else
+		{
+		  /* vertical crossing */
+		  dix = uta->width;
+		  xf0 = (int)floor (MIN(x, xn)) & (ART_UTILE_SIZE - 1);
+		  xmax = MAX(x, xn);
+		  xmaxc = (int)ceil (xmax);
+		  xf1 = xmaxc - (xt0 << ART_UTILE_SHIFT);
+		  yf1 = ART_UTILE_SIZE;
+
+		  if (rbuf != NULL)
+		    rbuf[yt0 * rbuf_rowstride + xt0] += sy;
+
+		  yt0++;
+		}
+	      yf0 = (int)floor (y) & (ART_UTILE_SIZE - 1);
+	      bb = uta->utiles[ix];
+	      if (bb == 0)
+		bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1);
+	      else
+		bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+				       MIN(ART_UTA_BBOX_Y0(bb), yf0),
+				       MAX(ART_UTA_BBOX_X1(bb), xf1),
+				       MAX(ART_UTA_BBOX_Y1(bb), yf1));
+	      uta->utiles[ix] = bb;
+
+	      x = xn;
+	      y = yn;
+	      ix += dix;
+	    }
+	  xmax = MAX(x, x1);
+	  xmaxc = ceil (xmax);
+	  ymaxc = ceil (y1);
+	  xf0 = (int)floor (MIN(x1, x)) & (ART_UTILE_SIZE - 1);
+	  yf0 = (int)floor (y) & (ART_UTILE_SIZE - 1);
+	  xf1 = xmaxc - (xt0 << ART_UTILE_SHIFT);
+	  yf1 = ymaxc - (yt0 << ART_UTILE_SHIFT);
+	  bb = uta->utiles[ix];
+	  if (bb == 0)
+	    bb = ART_UTA_BBOX_CONS(xf0, yf0, xf1, yf1);
+	  else
+	    bb = ART_UTA_BBOX_CONS(MIN(ART_UTA_BBOX_X0(bb), xf0),
+				   MIN(ART_UTA_BBOX_Y0(bb), yf0),
+				   MAX(ART_UTA_BBOX_X1(bb), xf1),
+				   MAX(ART_UTA_BBOX_Y1(bb), yf1));
+	  uta->utiles[ix] = bb;
+	}
+    }
+}
+
+/**
+ * art_uta_from_vpath: Generate uta covering a vpath.
+ * @vec: The source vpath.
+ *
+ * Generates a uta covering @vec. The resulting uta is of course
+ * approximate, ie it may cover more pixels than covered by @vec.
+ *
+ * Return value: the new uta.
+ **/
+ArtUta *
+art_uta_from_vpath (const ArtVpath *vec)
+{
+  ArtUta *uta;
+  ArtIRect bbox;
+  int *rbuf;
+  int i;
+  double x, y;
+  int sum;
+  int xt, yt;
+  ArtUtaBbox *utiles;
+  ArtUtaBbox bb;
+  int width;
+  int height;
+  int ix;
+
+  art_vpath_bbox_irect (vec, &bbox);
+
+  uta = art_uta_new_coords (bbox.x0, bbox.y0, bbox.x1, bbox.y1);
+
+  width = uta->width;
+  height = uta->height;
+  utiles = uta->utiles;
+
+  rbuf = art_new (int, width * height);
+  for (i = 0; i < width * height; i++)
+    rbuf[i] = 0;
+
+  x = 0;
+  y = 0;
+  for (i = 0; vec[i].code != ART_END; i++)
+    {
+      switch (vec[i].code)
+	{
+	case ART_MOVETO:
+	  x = vec[i].x;
+	  y = vec[i].y;
+	  break;
+	case ART_LINETO:
+	  art_uta_add_line (uta, vec[i].x, vec[i].y, x, y, rbuf, width);
+	  x = vec[i].x;
+	  y = vec[i].y;
+	  break;
+	default:
+	  /* this shouldn't happen */
+          art_free (rbuf);
+          art_free (uta);
+          return NULL;
+	}
+    }
+
+  /* now add in the filling from rbuf */
+  ix = 0;
+  for (yt = 0; yt < height; yt++)
+    {
+      sum = 0;
+      for (xt = 0; xt < width; xt++)
+	{
+	  sum += rbuf[ix];
+	  /* Nonzero winding rule - others are possible, but hardly
+	     worth it. */
+	  if (sum != 0)
+	    {
+	      bb = utiles[ix];
+	      bb &= 0xffff0000;
+	      bb |= (ART_UTILE_SIZE << 8) | ART_UTILE_SIZE;
+	      utiles[ix] = bb;
+	      if (xt != width - 1)
+		{
+		  bb = utiles[ix + 1];
+		  bb &= 0xffff00;
+		  bb |= ART_UTILE_SIZE;
+		  utiles[ix + 1] = bb;
+		}
+	      if (yt != height - 1)
+		{
+		  bb = utiles[ix + width];
+		  bb &= 0xff0000ff;
+		  bb |= ART_UTILE_SIZE << 8;
+		  utiles[ix + width] = bb;
+		  if (xt != width - 1)
+		    {
+		      utiles[ix + width + 1] &= 0xffff;
+		    }
+		}
+	    }
+	  ix++;
+	}
+    }
+
+  art_free (rbuf);
+
+  return uta;
+}
diff --git a/src/libart_lgpl/art_uta_vpath.h b/src/libart_lgpl/art_uta_vpath.h
new file mode 100644
index 0000000..3b6b9e5
--- /dev/null
+++ b/src/libart_lgpl/art_uta_vpath.h
@@ -0,0 +1,49 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_UTA_VPATH_H__
+#define __ART_UTA_VPATH_H__
+
+/* Basic data structures and constructors for microtile arrays */
+
+#ifdef LIBART_COMPILATION
+#include "art_uta.h"
+#include "art_vpath.h"
+#else
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_vpath.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtUta *
+art_uta_from_vpath (const ArtVpath *vec);
+
+/* This is a private function: */
+void
+art_uta_add_line (ArtUta *uta, double x0, double y0, double x1, double y1,
+		  int *rbuf, int rbuf_rowstride);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_UTA_VPATH_H__ */
+
diff --git a/src/libart_lgpl/art_vpath.c b/src/libart_lgpl/art_vpath.c
new file mode 100644
index 0000000..650dfe1
--- /dev/null
+++ b/src/libart_lgpl/art_vpath.c
@@ -0,0 +1,240 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for vector paths */
+
+#include "config.h"
+#include "art_vpath.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "art_misc.h"
+
+#include "art_rect.h"
+
+/**
+ * art_vpath_add_point: Add point to vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in * p_vpath 
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @code: The pathcode for the new point.
+ * @x: The X coordinate of the new point.
+ * @y: The Y coordinate of the new point.
+ *
+ * Adds a new point to * p_vpath, reallocating and updating * p_vpath
+ * and * pn_points_max as necessary. * pn_points is incremented.
+ *
+ * This routine always adds the point after all points already in the
+ * vpath. Thus, it should be called in the order the points are
+ * desired.
+ **/
+void
+art_vpath_add_point (ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+		     ArtPathcode code, double x, double y)
+{
+  int i;
+
+  i = (*pn_points)++;
+  if (i == *pn_points_max)
+    art_expand (*p_vpath, ArtVpath, *pn_points_max);
+  (*p_vpath)[i].code = code;
+  (*p_vpath)[i].x = x;
+  (*p_vpath)[i].y = y;
+}
+
+/* number of steps should really depend on radius. */
+#define CIRCLE_STEPS 128
+
+/**
+ * art_vpath_new_circle: Create a new circle.
+ * @x: X coordinate of center.
+ * @y: Y coordinate of center.
+ * @r: radius.
+ *
+ * Creates a new polygon closely approximating a circle with center
+ * (@x, @y) and radius @r. Currently, the number of points used in the
+ * approximation is fixed, but that will probably change.
+ *
+ * Return value: The newly created #ArtVpath.
+ **/
+ArtVpath *
+art_vpath_new_circle (double x, double y, double r)
+{
+  int i;
+  ArtVpath *vec;
+  double theta;
+
+  vec = art_new (ArtVpath, CIRCLE_STEPS + 2);
+
+  for (i = 0; i < CIRCLE_STEPS + 1; i++)
+    {
+      vec[i].code = i ? ART_LINETO : ART_MOVETO;
+      theta = (i & (CIRCLE_STEPS - 1)) * (M_PI * 2.0 / CIRCLE_STEPS);
+      vec[i].x = x + r * cos (theta);
+      vec[i].y = y - r * sin (theta);
+    }
+  vec[i].code = ART_END;
+
+  return vec;
+}
+
+/**
+ * art_vpath_affine_transform: Affine transform a vpath.
+ * @src: Source vpath to transform.
+ * @matrix: Affine transform.
+ *
+ * Computes the affine transform of the vpath, using @matrix as the
+ * transform. @matrix is stored in the same format as PostScript, ie.
+ * x' = @matrix[0] * x + @matrix[2] * y + @matrix[4]
+ * y' = @matrix[1] * x + @matrix[3] * y + @matrix[5]
+ *
+ * Return value: the newly allocated vpath resulting from the transform.
+**/
+ArtVpath *
+art_vpath_affine_transform (const ArtVpath *src, const double matrix[6])
+{
+  int i;
+  int size;
+  ArtVpath *new;
+  double x, y;
+
+  for (i = 0; src[i].code != ART_END; i++);
+  size = i;
+
+  new = art_new (ArtVpath, size + 1);
+
+  for (i = 0; i < size; i++)
+    {
+      new[i].code = src[i].code;
+      x = src[i].x;
+      y = src[i].y;
+      new[i].x = matrix[0] * x + matrix[2] * y + matrix[4];
+      new[i].y = matrix[1] * x + matrix[3] * y + matrix[5];
+    }
+  new[i].code = ART_END;
+
+  return new;
+}
+
+/**
+ * art_vpath_bbox_drect: Determine bounding box of vpath.
+ * @vec: Source vpath.
+ * @drect: Where to store bounding box.
+ *
+ * Determines bounding box of @vec, and stores it in @drect.
+ **/
+void
+art_vpath_bbox_drect (const ArtVpath *vec, ArtDRect *drect)
+{
+  int i;
+  double x0, y0, x1, y1;
+
+  if (vec[0].code == ART_END)
+    {
+      x0 = y0 = x1 = y1 = 0;
+    }
+  else
+    {
+      x0 = x1 = vec[0].x;
+      y0 = y1 = vec[0].y;
+      for (i = 1; vec[i].code != ART_END; i++)
+	{
+	  if (vec[i].x < x0) x0 = vec[i].x;
+	  if (vec[i].x > x1) x1 = vec[i].x;
+	  if (vec[i].y < y0) y0 = vec[i].y;
+	  if (vec[i].y > y1) y1 = vec[i].y;
+	}
+    }
+  drect->x0 = x0;
+  drect->y0 = y0;
+  drect->x1 = x1;
+  drect->y1 = y1;
+}
+
+/**
+ * art_vpath_bbox_irect: Determine integer bounding box of vpath.
+ * @vec: Source vpath.
+ * idrect: Where to store bounding box.
+ *
+ * Determines integer bounding box of @vec, and stores it in @irect.
+ **/
+void
+art_vpath_bbox_irect (const ArtVpath *vec, ArtIRect *irect)
+{
+  ArtDRect drect;
+
+  art_vpath_bbox_drect (vec, &drect);
+  art_drect_to_irect (irect, &drect);
+}
+
+#define PERTURBATION 2e-3
+
+/**
+ * art_vpath_perturb: Perturb each point in vpath by small random amount.
+ * @src: Source vpath.
+ *
+ * Perturbs each of the points by a small random amount. This is
+ * helpful for cheating in cases when algorithms haven't attained
+ * numerical stability yet.
+ *
+ * Return value: Newly allocated vpath containing perturbed @src.
+ **/
+ArtVpath *
+art_vpath_perturb (ArtVpath *src)
+{
+  int i;
+  int size;
+  ArtVpath *new;
+  double x, y;
+  double x_start, y_start;
+  int open;
+
+  for (i = 0; src[i].code != ART_END; i++);
+  size = i;
+
+  new = art_new (ArtVpath, size + 1);
+
+  x_start = 0;
+  y_start = 0;
+  open = 0;
+  for (i = 0; i < size; i++)
+    {
+      new[i].code = src[i].code;
+      x = src[i].x + (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5;
+      y = src[i].y + (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5;
+      if (src[i].code == ART_MOVETO)
+	{
+	  x_start = x;
+	  y_start = y;
+	  open = 0;
+	}
+      else if (src[i].code == ART_MOVETO_OPEN)
+	open = 1;
+      if (!open && (i + 1 == size || src[i + 1].code != ART_LINETO))
+	{
+	  x = x_start;
+	  y = y_start;
+	}
+      new[i].x = x;
+      new[i].y = y;
+    }
+  new[i].code = ART_END;
+
+  return new;
+}
diff --git a/src/libart_lgpl/art_vpath.h b/src/libart_lgpl/art_vpath.h
new file mode 100644
index 0000000..2326de9
--- /dev/null
+++ b/src/libart_lgpl/art_vpath.h
@@ -0,0 +1,70 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_H__
+#define __ART_VPATH_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_rect.h"
+#include "art_pathcode.h"
+#else
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_pathcode.h>
+#endif
+
+/* Basic data structures and constructors for simple vector paths */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtVpath ArtVpath;
+
+/* CURVETO is not allowed! */
+struct _ArtVpath {
+  ArtPathcode code;
+  double x;
+  double y;
+};
+
+/* Some of the functions need to go into their own modules */
+
+void
+art_vpath_add_point (ArtVpath **p_vpath, int *pn_points, int *pn_points_max,
+		     ArtPathcode code, double x, double y);
+
+ArtVpath *
+art_vpath_new_circle (double x, double y, double r);
+
+ArtVpath *
+art_vpath_affine_transform (const ArtVpath *src, const double matrix[6]);
+
+void
+art_vpath_bbox_drect (const ArtVpath *vec, ArtDRect *drect);
+
+void
+art_vpath_bbox_irect (const ArtVpath *vec, ArtIRect *irect);
+
+ArtVpath *
+art_vpath_perturb (ArtVpath *src);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_H__ */
diff --git a/src/libart_lgpl/art_vpath_bpath.c b/src/libart_lgpl/art_vpath_bpath.c
new file mode 100644
index 0000000..37e2afb
--- /dev/null
+++ b/src/libart_lgpl/art_vpath_bpath.c
@@ -0,0 +1,327 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Basic constructors and operations for bezier paths */
+
+#include "config.h"
+#include "art_vpath_bpath.h"
+
+#include <math.h>
+
+#include "art_misc.h"
+
+#include "art_bpath.h"
+#include "art_vpath.h"
+
+/* p must be allocated 2^level points. */
+
+/* level must be >= 1 */
+ArtPoint *
+art_bezier_to_vec (double x0, double y0,
+		   double x1, double y1,
+		   double x2, double y2,
+		   double x3, double y3,
+		   ArtPoint *p,
+		   int level)
+{
+  double x_m, y_m;
+
+#ifdef VERBOSE
+  printf ("bezier_to_vec: %g,%g %g,%g %g,%g %g,%g %d\n",
+	  x0, y0, x1, y1, x2, y2, x3, y3, level);
+#endif
+  if (level == 1) {
+    x_m = (x0 + 3 * (x1 + x2) + x3) * 0.125;
+    y_m = (y0 + 3 * (y1 + y2) + y3) * 0.125;
+    p->x = x_m;
+    p->y = y_m;
+    p++;
+    p->x = x3;
+    p->y = y3;
+    p++;
+#ifdef VERBOSE
+    printf ("-> (%g, %g) -> (%g, %g)\n", x_m, y_m, x3, y3);
+#endif
+  } else {
+    double xa1, ya1;
+    double xa2, ya2;
+    double xb1, yb1;
+    double xb2, yb2;
+
+    xa1 = (x0 + x1) * 0.5;
+    ya1 = (y0 + y1) * 0.5;
+    xa2 = (x0 + 2 * x1 + x2) * 0.25;
+    ya2 = (y0 + 2 * y1 + y2) * 0.25;
+    xb1 = (x1 + 2 * x2 + x3) * 0.25;
+    yb1 = (y1 + 2 * y2 + y3) * 0.25;
+    xb2 = (x2 + x3) * 0.5;
+    yb2 = (y2 + y3) * 0.5;
+    x_m = (xa2 + xb1) * 0.5;
+    y_m = (ya2 + yb1) * 0.5;
+#ifdef VERBOSE
+    printf ("%g,%g %g,%g %g,%g %g,%g\n", xa1, ya1, xa2, ya2,
+	    xb1, yb1, xb2, yb2);
+#endif
+    p = art_bezier_to_vec (x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, p, level - 1);
+    p = art_bezier_to_vec (x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, p, level - 1);
+  }
+  return p;
+}
+
+#define RENDER_LEVEL 4
+#define RENDER_SIZE (1 << (RENDER_LEVEL))
+
+/**
+ * art_vpath_render_bez: Render a bezier segment into the vpath.
+ * @p_vpath: Where the pointer to the #ArtVpath structure is stored.
+ * @pn_points: Pointer to the number of points in * p_vpath 
+ * @pn_points_max: Pointer to the number of points allocated.
+ * @x0: X coordinate of starting bezier point.
+ * @y0: Y coordinate of starting bezier point.
+ * @x1: X coordinate of first bezier control point.
+ * @y1: Y coordinate of first bezier control point.
+ * @x2: X coordinate of second bezier control point.
+ * @y2: Y coordinate of second bezier control point.
+ * @x3: X coordinate of ending bezier point.
+ * @y3: Y coordinate of ending bezier point.
+ * @flatness: Flatness control.
+ *
+ * Renders a bezier segment into the vector path, reallocating and
+ * updating * p_vpath and * pn_vpath_max as necessary. * pn_vpath is
+ * incremented by the number of vector points added.
+ *
+ * This step includes (@x0, @y0) but not (@x3, @y3).
+ *
+ * The @flatness argument guides the amount of subdivision. The Adobe
+ * PostScript reference manual defines flatness as the maximum
+ * deviation between the any point on the vpath approximation and the
+ * corresponding point on the "true" curve, and we follow this
+ * definition here. A value of 0.25 should ensure high quality for aa
+ * rendering.
+**/
+static void
+art_vpath_render_bez (ArtVpath **p_vpath, int *pn, int *pn_max,
+		      double x0, double y0,
+		      double x1, double y1,
+		      double x2, double y2,
+		      double x3, double y3,
+		      double flatness)
+{
+  double x3_0, y3_0;
+  double z3_0_dot;
+  double z1_dot, z2_dot;
+  double z1_perp, z2_perp;
+  double max_perp_sq;
+
+  double x_m, y_m;
+  double xa1, ya1;
+  double xa2, ya2;
+  double xb1, yb1;
+  double xb2, yb2;
+
+  /* It's possible to optimize this routine a fair amount.
+
+     First, once the _dot conditions are met, they will also be met in
+     all further subdivisions. So we might recurse to a different
+     routine that only checks the _perp conditions.
+
+     Second, the distance _should_ decrease according to fairly
+     predictable rules (a factor of 4 with each subdivision). So it might
+     be possible to note that the distance is within a factor of 4 of
+     acceptable, and subdivide once. But proving this might be hard.
+
+     Third, at the last subdivision, x_m and y_m can be computed more
+     expeditiously (as in the routine above).
+
+     Finally, if we were able to subdivide by, say 2 or 3, this would
+     allow considerably finer-grain control, i.e. fewer points for the
+     same flatness tolerance. This would speed things up downstream.
+
+     In any case, this routine is unlikely to be the bottleneck. It's
+     just that I have this undying quest for more speed...
+
+  */
+
+  x3_0 = x3 - x0;
+  y3_0 = y3 - y0;
+
+  /* z3_0_dot is dist z0-z3 squared */
+  z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0;
+
+  if (z3_0_dot < 0.001)
+    {
+      /* if start and end point are almost identical, the flatness tests
+       * don't work properly, so fall back on testing whether both of
+       * the other two control points are the same as the start point,
+       * too.
+       */
+      if (hypot(x1 - x0, y1 - y0) < 0.001
+	  && hypot(x2 - x0, y2 - y0) < 0.001)
+	  goto nosubdivide;
+      else
+	  goto subdivide;
+    }
+
+  /* we can avoid subdivision if:
+
+     z1 has distance no more than flatness from the z0-z3 line
+
+     z1 is no more z0'ward than flatness past z0-z3
+
+     z1 is more z0'ward than z3'ward on the line traversing z0-z3
+
+     and correspondingly for z2 */
+
+  /* perp is distance from line, multiplied by dist z0-z3 */
+  max_perp_sq = flatness * flatness * z3_0_dot;
+
+  z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0;
+  if (z1_perp * z1_perp > max_perp_sq)
+    goto subdivide;
+
+  z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0;
+  if (z2_perp * z2_perp > max_perp_sq)
+    goto subdivide;
+
+  z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0;
+  if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq)
+    goto subdivide;
+
+  z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0;
+  if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq)
+    goto subdivide;
+
+  if (z1_dot + z1_dot > z3_0_dot)
+    goto subdivide;
+
+  if (z2_dot + z2_dot > z3_0_dot)
+    goto subdivide;
+
+
+ nosubdivide:
+  /* don't subdivide */
+  art_vpath_add_point (p_vpath, pn, pn_max,
+		       ART_LINETO, x3, y3);
+  return;
+
+ subdivide:
+
+  xa1 = (x0 + x1) * 0.5;
+  ya1 = (y0 + y1) * 0.5;
+  xa2 = (x0 + 2 * x1 + x2) * 0.25;
+  ya2 = (y0 + 2 * y1 + y2) * 0.25;
+  xb1 = (x1 + 2 * x2 + x3) * 0.25;
+  yb1 = (y1 + 2 * y2 + y3) * 0.25;
+  xb2 = (x2 + x3) * 0.5;
+  yb2 = (y2 + y3) * 0.5;
+  x_m = (xa2 + xb1) * 0.5;
+  y_m = (ya2 + yb1) * 0.5;
+#ifdef VERBOSE
+  printf ("%g,%g %g,%g %g,%g %g,%g\n", xa1, ya1, xa2, ya2,
+	  xb1, yb1, xb2, yb2);
+#endif
+  art_vpath_render_bez (p_vpath, pn, pn_max,
+			x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness);
+  art_vpath_render_bez (p_vpath, pn, pn_max,
+			x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness);
+}
+
+/**
+ * art_bez_path_to_vec: Create vpath from bezier path.
+ * @bez: Bezier path.
+ * @flatness: Flatness control.
+ *
+ * Creates a vector path closely approximating the bezier path defined by
+ * @bez. The @flatness argument controls the amount of subdivision. In
+ * general, the resulting vpath deviates by at most @flatness pixels
+ * from the "ideal" path described by @bez.
+ *
+ * Return value: Newly allocated vpath.
+ **/
+ArtVpath *
+art_bez_path_to_vec (const ArtBpath *bez, double flatness)
+{
+  ArtVpath *vec;
+  int vec_n, vec_n_max;
+  int bez_index;
+  double x, y;
+
+  vec_n = 0;
+  vec_n_max = RENDER_SIZE;
+  vec = art_new (ArtVpath, vec_n_max);
+
+  /* Initialization is unnecessary because of the precondition that the
+     bezier path does not begin with LINETO or CURVETO, but is here
+     to make the code warning-free. */
+  x = 0;
+  y = 0;
+
+  bez_index = 0;
+  do
+    {
+#ifdef VERBOSE
+      printf ("%s %g %g\n",
+	      bez[bez_index].code == ART_CURVETO ? "curveto" :
+	      bez[bez_index].code == ART_LINETO ? "lineto" :
+	      bez[bez_index].code == ART_MOVETO ? "moveto" :
+	      bez[bez_index].code == ART_MOVETO_OPEN ? "moveto-open" :
+	      "end", bez[bez_index].x3, bez[bez_index].y3);
+#endif
+      /* make sure space for at least one more code */
+      if (vec_n >= vec_n_max)
+	art_expand (vec, ArtVpath, vec_n_max);
+      switch (bez[bez_index].code)
+	{
+	case ART_MOVETO_OPEN:
+	case ART_MOVETO:
+	case ART_LINETO:
+	  x = bez[bez_index].x3;
+	  y = bez[bez_index].y3;
+	  vec[vec_n].code = bez[bez_index].code;
+	  vec[vec_n].x = x;
+	  vec[vec_n].y = y;
+	  vec_n++;
+	  break;
+	case ART_END:
+	  vec[vec_n].code = bez[bez_index].code;
+	  vec[vec_n].x = 0;
+	  vec[vec_n].y = 0;
+	  vec_n++;
+	  break;
+	case ART_CURVETO:
+#ifdef VERBOSE
+	  printf ("%g,%g %g,%g %g,%g %g,%g\n", x, y,
+			 bez[bez_index].x1, bez[bez_index].y1,
+			 bez[bez_index].x2, bez[bez_index].y2,
+			 bez[bez_index].x3, bez[bez_index].y3);
+#endif
+	  art_vpath_render_bez (&vec, &vec_n, &vec_n_max,
+				x, y,
+				bez[bez_index].x1, bez[bez_index].y1,
+				bez[bez_index].x2, bez[bez_index].y2,
+				bez[bez_index].x3, bez[bez_index].y3,
+				flatness);
+	  x = bez[bez_index].x3;
+	  y = bez[bez_index].y3;
+	  break;
+	}
+    }
+  while (bez[bez_index++].code != ART_END);
+  return vec;
+}
+
diff --git a/src/libart_lgpl/art_vpath_bpath.h b/src/libart_lgpl/art_vpath_bpath.h
new file mode 100644
index 0000000..d6b9a45
--- /dev/null
+++ b/src/libart_lgpl/art_vpath_bpath.h
@@ -0,0 +1,47 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_BPATH_H__
+#define __ART_VPATH_BPATH_H__
+
+#ifdef LIBART_COMPILATION
+#include "art_bpath.h"
+#include "art_vpath.h"
+#else
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtPoint *art_bezier_to_vec (double x0, double y0,
+			     double x1, double y1,
+			     double x2, double y2,
+			     double x3, double y3,
+			     ArtPoint *p,
+			     int level);
+
+ArtVpath *art_bez_path_to_vec (const ArtBpath *bez, double flatness);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_BPATH_H__ */
diff --git a/src/libart_lgpl/art_vpath_dash.c b/src/libart_lgpl/art_vpath_dash.c
new file mode 100644
index 0000000..c5146f3
--- /dev/null
+++ b/src/libart_lgpl/art_vpath_dash.c
@@ -0,0 +1,199 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* Apply a dash style to a vector path. */
+
+#include "config.h"
+#include "art_vpath_dash.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "art_misc.h"
+
+#include "art_vpath.h"
+
+
+/* Return the length of the largest subpath within vpath */
+static int
+art_vpath_dash_max_subpath (const ArtVpath *vpath)
+{
+  int max_subpath;
+  int i;
+  int start;
+
+  max_subpath = 0;
+  start = 0;
+  for (i = 0; vpath[i].code != ART_END; i++)
+    {
+      if (vpath[i].code == ART_MOVETO || vpath[i].code == ART_MOVETO_OPEN)
+	{
+	  if (i - start > max_subpath)
+	    max_subpath = i - start;
+	  start = i;
+	}
+    }
+  if (i - start > max_subpath)
+    max_subpath = i - start;
+
+  return max_subpath;
+}
+
+/**
+ * art_vpath_dash: Add dash style to vpath.
+ * @vpath: Original vpath.
+ * @dash: Dash style.
+ *
+ * Creates a new vpath that is the result of applying dash style @dash
+ * to @vpath.
+ *
+ * This implementation has two known flaws:
+ *
+ * First, it adds a spurious break at the beginning of the vpath. The
+ * only way I see to resolve this flaw is to run the state forward one
+ * dash break at the beginning, and fix up by looping back to the
+ * first dash break at the end. This is doable but of course adds some
+ * complexity.
+ *
+ * Second, it does not suppress output points that are within epsilon
+ * of each other.
+ *
+ * Return value: Newly created vpath.
+ **/
+ArtVpath *
+art_vpath_dash (const ArtVpath *vpath, const ArtVpathDash *dash)
+{
+  int max_subpath;
+  double *dists;
+  ArtVpath *result;
+  int n_result, n_result_max;
+  int start, end;
+  int i;
+  double total_dist;
+
+  /* state while traversing dasharray - offset is offset of current dash
+     value, toggle is 0 for "off" and 1 for "on", and phase is the distance
+     in, >= 0, < dash->dash[offset]. */
+  int offset, toggle;
+  double phase;
+
+  /* initial values */
+  int offset_init, toggle_init;
+  double phase_init;
+
+  max_subpath = art_vpath_dash_max_subpath (vpath);
+  dists = art_new (double, max_subpath);
+
+  n_result = 0;
+  n_result_max = 16;
+  result = art_new (ArtVpath, n_result_max);
+
+  /* determine initial values of dash state */
+  toggle_init = 1;
+  offset_init = 0;
+  phase_init = dash->offset;
+  while (phase_init >= dash->dash[offset_init])
+    {
+      toggle_init = !toggle_init;
+      phase_init -= dash->dash[offset_init];
+      offset_init++;
+      if (offset_init == dash->n_dash)
+	offset_init = 0;
+    }
+
+  for (start = 0; vpath[start].code != ART_END; start = end)
+    {
+      for (end = start + 1; vpath[end].code == ART_LINETO; end++);
+      /* subpath is [start..end) */
+      total_dist = 0;
+      for (i = start; i < end - 1; i++)
+	{
+	  double dx, dy;
+
+	  dx = vpath[i + 1].x - vpath[i].x;
+	  dy = vpath[i + 1].y - vpath[i].y;
+	  dists[i - start] = sqrt (dx * dx + dy * dy);
+	  total_dist += dists[i - start];
+	}
+      if (total_dist <= dash->dash[offset_init] - phase_init)
+	{
+	  /* subpath fits entirely within first dash */
+	  if (toggle_init)
+	    {
+	      for (i = start; i < end; i++)
+		art_vpath_add_point (&result, &n_result, &n_result_max,
+				     vpath[i].code, vpath[i].x, vpath[i].y);
+	    }
+	}
+      else
+	{
+	  /* subpath is composed of at least one dash - thus all
+	     generated pieces are open */
+	  double dist;
+
+	  phase = phase_init;
+	  offset = offset_init;
+	  toggle = toggle_init;
+	  dist = 0;
+	  i = start;
+	  if (toggle)
+	    art_vpath_add_point (&result, &n_result, &n_result_max,
+				 ART_MOVETO_OPEN, vpath[i].x, vpath[i].y);
+	  while (i != end - 1)
+	    {
+	      if (dists[i - start] - dist > dash->dash[offset] - phase)
+		{
+		  /* dash boundary is next */
+		  double a;
+		  double x, y;
+
+		  dist += dash->dash[offset] - phase;
+		  a = dist / dists[i - start];
+		  x = vpath[i].x + a * (vpath[i + 1].x - vpath[i].x);
+		  y = vpath[i].y + a * (vpath[i + 1].y - vpath[i].y);
+		  art_vpath_add_point (&result, &n_result, &n_result_max,
+				       toggle ? ART_LINETO : ART_MOVETO_OPEN,
+				       x, y);
+		  /* advance to next dash */
+		  toggle = !toggle;
+		  phase = 0;
+		  offset++;
+		  if (offset == dash->n_dash)
+		    offset = 0;
+		}
+	      else
+		{
+		  /* end of line in vpath is next */
+		  phase += dists[i - start] - dist;
+		  i++;
+		  dist = 0;
+		  if (toggle)
+		    art_vpath_add_point (&result, &n_result, &n_result_max,
+					 ART_LINETO, vpath[i].x, vpath[i].y);
+		}
+	    }
+	}
+    }
+
+  art_vpath_add_point (&result, &n_result, &n_result_max,
+		       ART_END, 0, 0);
+
+  art_free (dists);
+
+  return result;
+}
diff --git a/src/libart_lgpl/art_vpath_dash.h b/src/libart_lgpl/art_vpath_dash.h
new file mode 100644
index 0000000..1b44151
--- /dev/null
+++ b/src/libart_lgpl/art_vpath_dash.h
@@ -0,0 +1,49 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1999 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_DASH_H__
+#define __ART_VPATH_DASH_H__
+
+/* Apply a dash style to a vector path. */
+
+#ifdef LIBART_COMPILATION
+#include "art_vpath.h"
+#else
+#include <libart_lgpl/art_vpath.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtVpathDash ArtVpathDash;
+
+struct _ArtVpathDash {
+  double offset;
+  int n_dash;
+  double *dash;
+};
+
+ArtVpath *
+art_vpath_dash (const ArtVpath *vpath, const ArtVpathDash *dash);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_DASH_H__ */
diff --git a/src/libart_lgpl/art_vpath_svp.c b/src/libart_lgpl/art_vpath_svp.c
new file mode 100644
index 0000000..a497c93
--- /dev/null
+++ b/src/libart_lgpl/art_vpath_svp.c
@@ -0,0 +1,195 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998-2000 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* "Unsort" a sorted vector path into an ordinary vector path. */
+
+#include "config.h"
+#include "art_vpath_svp.h"
+
+#include <stdio.h> /* for printf - debugging */
+#include "art_misc.h"
+
+#include "art_vpath.h"
+#include "art_svp.h"
+
+typedef struct _ArtVpathSVPEnd ArtVpathSVPEnd;
+
+struct _ArtVpathSVPEnd {
+  int seg_num;
+  int which; /* 0 = top, 1 = bottom */
+  double x, y;
+};
+
+#define EPSILON 1e-6
+
+static int
+art_vpath_svp_point_compare (double x1, double y1, double x2, double y2)
+{
+  if (y1 - EPSILON > y2) return 1;
+  if (y1 + EPSILON < y2) return -1;
+  if (x1 - EPSILON > x2) return 1;
+  if (x1 + EPSILON < x2) return -1;
+  return 0;
+}
+
+static int
+art_vpath_svp_compare (const void *s1, const void *s2)
+{
+  const ArtVpathSVPEnd *e1 = s1;
+  const ArtVpathSVPEnd *e2 = s2;
+
+  return art_vpath_svp_point_compare (e1->x, e1->y, e2->x, e2->y);
+}
+
+/* Convert from sorted vector path representation into regular
+   vector path representation.
+
+   Status of this routine:
+
+   Basic correctness: Only works with closed paths.
+
+   Numerical stability: Not known to work when more than two segments
+   meet at a point.
+
+   Speed: Should be pretty good.
+
+   Precision: Does not degrade precision.
+
+*/
+/**
+ * art_vpath_from_svp: Convert from svp to vpath form.
+ * @svp: Original #ArtSVP.
+ *
+ * Converts the sorted vector path @svp into standard vpath form.
+ *
+ * Return value: the newly allocated vpath.
+ **/
+ArtVpath *
+art_vpath_from_svp (const ArtSVP *svp)
+{
+  int n_segs = svp->n_segs;
+  ArtVpathSVPEnd *ends;
+  ArtVpath *new;
+  int *visited;
+  int n_new, n_new_max;
+  int i, k;
+  int j = 0; /* Quiet compiler */
+  int seg_num;
+  int first;
+  double last_x, last_y;
+  int n_points;
+  int pt_num;
+
+  last_x = 0; /* to eliminate "uninitialized" warning */
+  last_y = 0;
+
+  ends = art_new (ArtVpathSVPEnd, n_segs * 2);
+  for (i = 0; i < svp->n_segs; i++)
+    {
+      int lastpt;
+
+      ends[i * 2].seg_num = i;
+      ends[i * 2].which = 0;
+      ends[i * 2].x = svp->segs[i].points[0].x;
+      ends[i * 2].y = svp->segs[i].points[0].y;
+
+      lastpt = svp->segs[i].n_points - 1;
+      ends[i * 2 + 1].seg_num = i;
+      ends[i * 2 + 1].which = 1;
+      ends[i * 2 + 1].x = svp->segs[i].points[lastpt].x;
+      ends[i * 2 + 1].y = svp->segs[i].points[lastpt].y;
+    }
+  qsort (ends, n_segs * 2, sizeof (ArtVpathSVPEnd), art_vpath_svp_compare);
+
+  n_new = 0;
+  n_new_max = 16; /* I suppose we _could_ estimate this from traversing
+		     the svp, so we don't have to reallocate */
+  new = art_new (ArtVpath, n_new_max);
+
+  visited = art_new (int, n_segs);
+  for (i = 0; i < n_segs; i++)
+    visited[i] = 0;
+
+  first = 1;
+  for (i = 0; i < n_segs; i++)
+    {
+      if (!first)
+	{
+	  /* search for the continuation of the existing subpath */
+	  /* This could be a binary search (which is why we sorted, above) */
+	  for (j = 0; j < n_segs * 2; j++)
+	    {
+	      if (!visited[ends[j].seg_num] &&
+		  art_vpath_svp_point_compare (last_x, last_y,
+					       ends[j].x, ends[j].y) == 0)
+		break;
+	    }
+	  if (j == n_segs * 2)
+	    first = 1;
+	}
+      if (first)
+	{
+	  /* start a new subpath */
+	  for (j = 0; j < n_segs * 2; j++)
+	    if (!visited[ends[j].seg_num])
+	      break;
+	}
+      if (j == n_segs * 2)
+	{
+	  printf ("failure\n");
+	}
+      seg_num = ends[j].seg_num;
+      n_points = svp->segs[seg_num].n_points;
+      for (k = 0; k < n_points; k++)
+	{
+	  pt_num = svp->segs[seg_num].dir ? k : n_points - (1 + k);
+	  if (k == 0)
+	    {
+	      if (first)
+		{
+		  art_vpath_add_point (&new, &n_new, &n_new_max,
+				       ART_MOVETO,
+				       svp->segs[seg_num].points[pt_num].x,
+				       svp->segs[seg_num].points[pt_num].y);
+		}
+	    }
+	  else
+	    {
+	      art_vpath_add_point (&new, &n_new, &n_new_max,
+				   ART_LINETO,
+				   svp->segs[seg_num].points[pt_num].x,
+				   svp->segs[seg_num].points[pt_num].y);
+	      if (k == n_points - 1)
+		{
+		  last_x = svp->segs[seg_num].points[pt_num].x;
+		  last_y = svp->segs[seg_num].points[pt_num].y;
+		  /* to make more robust, check for meeting first_[xy],
+		     set first if so */
+		}
+	    }
+	  first = 0;
+	}
+      visited[seg_num] = 1;
+    }
+
+  art_vpath_add_point (&new, &n_new, &n_new_max,
+		       ART_END, 0, 0);
+  art_free (visited);
+  art_free (ends);
+  return new;
+}
diff --git a/src/libart_lgpl/art_vpath_svp.h b/src/libart_lgpl/art_vpath_svp.h
new file mode 100644
index 0000000..cf62d5a
--- /dev/null
+++ b/src/libart_lgpl/art_vpath_svp.h
@@ -0,0 +1,42 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_VPATH_SVP_H__
+#define __ART_VPATH_SVP_H__
+
+/* "Unsort" a sorted vector path into an ordinary vector path. */
+
+#ifdef LIBART_COMPILATION
+#include "art_svp.h"
+#include "art_vpath.h"
+#else
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ArtVpath *art_vpath_from_svp (const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_VPATH_SVP_H__ */
diff --git a/src/libart_lgpl/gen_art_config.c b/src/libart_lgpl/gen_art_config.c
new file mode 100644
index 0000000..8d88815
--- /dev/null
+++ b/src/libart_lgpl/gen_art_config.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include "config.h"
+#include <stdlib.h>
+
+/**
+ * A little utility function to generate header info.
+ *
+ * Yes, it would be possible to do this using more "native" autoconf
+ * features, but I personally find this approach to be cleaner.
+ *
+ * The output of this program is generally written to art_config.h,
+ * which is installed in libart's include dir.
+ **/
+
+static void
+die (char *why)
+{
+  fprintf (stderr, "gen_art_config: %s\n", why);
+  exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+  printf ("/* Automatically generated by gen_art_config.c */\n"
+	  "\n"
+	  "#define ART_SIZEOF_CHAR %d\n"
+	  "#define ART_SIZEOF_SHORT %d\n"
+	  "#define ART_SIZEOF_INT %d\n"
+	  "#define ART_SIZEOF_LONG %d\n"
+	  "\n",
+	  (int)sizeof(char), (int)sizeof(short), (int)sizeof(int), (int)sizeof(long));
+
+  if (sizeof(char) == 1)
+    printf ("typedef unsigned char art_u8;\n");
+  else
+    die ("sizeof(char) != 1");
+
+  if (sizeof(short) == 2)
+    printf ("typedef unsigned short art_u16;\n");
+  else
+    die ("sizeof(short) != 2");
+
+  if (sizeof(int) == 4)
+    printf ("typedef unsigned int art_u32;\n");
+  else if (sizeof(long) == 4)
+    printf ("typedef unsigned long art_u32;\n");
+  else
+    die ("sizeof(int) != 4 and sizeof(long) != 4");
+
+  return 0;
+}
diff --git a/src/libart_lgpl/libart-config.in b/src/libart_lgpl/libart-config.in
new file mode 100644
index 0000000..0a2f3a9
--- /dev/null
+++ b/src/libart_lgpl/libart-config.in
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+prefix= prefix@
+exec_prefix= exec_prefix@
+exec_prefix_set=no
+
+usage="\
+Usage: libart-config [--prefix[=DIR]] [--exec-prefix[=DIR]] [--version] [--libs] [--cflags]"
+
+if test $# -eq 0; then
+      echo "${usage}" 1>&2
+      exit 1
+fi
+
+while test $# -gt 0; do
+  case "$1" in
+  -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+  *) optarg= ;;
+  esac
+
+  case $1 in
+    --prefix=*)
+      prefix=$optarg
+      if test $exec_prefix_set = no ; then
+        exec_prefix=$optarg
+      fi
+      ;;
+    --prefix)
+      echo $prefix
+      ;;
+    --exec-prefix=*)
+      exec_prefix=$optarg
+      exec_prefix_set=yes
+      ;;
+    --exec-prefix)
+      echo $exec_prefix
+      ;;
+    --version)
+      echo @LIBART_VERSION@
+      ;;
+    --cflags)
+      includes=-I includedir@/libart-2.0
+      echo $includes
+      ;;
+    --libs)
+      libdirs=-L libdir@
+      echo $libdirs -lart_lgpl_2 -lm
+      ;;
+    *)
+      echo "${usage}" 1>&2
+      exit 1
+      ;;
+  esac
+  shift
+done
+
diff --git a/src/libart_lgpl/libart-features.c b/src/libart_lgpl/libart-features.c
new file mode 100644
index 0000000..ff99945
--- /dev/null
+++ b/src/libart_lgpl/libart-features.c
@@ -0,0 +1,18 @@
+#include "libart-features.h"
+
+/* General initialization hooks */
+const unsigned int libart_major_version=LIBART_MAJOR_VERSION,
+  libart_minor_version=LIBART_MINOR_VERSION,
+  libart_micro_version=LIBART_MICRO_VERSION;
+
+const char *libart_version = LIBART_VERSION;
+
+void
+libart_preinit(void *app, void *modinfo)
+{
+}
+
+void
+libart_postinit(void *app, void *modinfo)
+{
+}
diff --git a/src/libart_lgpl/libart-features.h b/src/libart_lgpl/libart-features.h
new file mode 100644
index 0000000..c4f177b
--- /dev/null
+++ b/src/libart_lgpl/libart-features.h
@@ -0,0 +1,24 @@
+#ifndef LIBART_FEATURES_H
+#define LIBART_FEATURES_H 1
+
+#define LIBART_MAJOR_VERSION (2)
+#define LIBART_MINOR_VERSION (3)
+#define LIBART_MICRO_VERSION (19)
+#define LIBART_VERSION "2.3.19"
+
+#ifdef _WIN32
+#  ifdef LIBART_COMPILATION
+#    define LIBART_VAR __declspec(dllexport)
+#  else
+#    define LIBART_VAR extern __declspec(dllimport)
+#  endif
+#else
+#  define LIBART_VAR extern
+#endif
+
+LIBART_VAR const unsigned int libart_major_version, libart_minor_version, libart_micro_version;
+LIBART_VAR const char *libart_version;
+
+void libart_preinit(void *app, void *modinfo);
+void libart_postinit(void *app, void *modinfo);
+#endif
diff --git a/src/libart_lgpl/libart-features.h.in b/src/libart_lgpl/libart-features.h.in
new file mode 100644
index 0000000..88d4965
--- /dev/null
+++ b/src/libart_lgpl/libart-features.h.in
@@ -0,0 +1,24 @@
+#ifndef LIBART_FEATURES_H
+#define LIBART_FEATURES_H 1
+
+#define LIBART_MAJOR_VERSION (@LIBART_MAJOR_VERSION@)
+#define LIBART_MINOR_VERSION (@LIBART_MINOR_VERSION@)
+#define LIBART_MICRO_VERSION (@LIBART_MICRO_VERSION@)
+#define LIBART_VERSION "@LIBART_VERSION@"
+
+#ifdef _WIN32
+#  ifdef LIBART_COMPILATION
+#    define LIBART_VAR __declspec(dllexport)
+#  else
+#    define LIBART_VAR extern __declspec(dllimport)
+#  endif
+#else
+#  define LIBART_VAR extern
+#endif
+
+LIBART_VAR const unsigned int libart_major_version, libart_minor_version, libart_micro_version;
+LIBART_VAR const char *libart_version;
+
+void libart_preinit(void *app, void *modinfo);
+void libart_postinit(void *app, void *modinfo);
+#endif
diff --git a/src/libart_lgpl/libart.h b/src/libart_lgpl/libart.h
new file mode 100644
index 0000000..4a9eeea
--- /dev/null
+++ b/src/libart_lgpl/libart.h
@@ -0,0 +1,39 @@
+#ifndef LIBART_H
+#define LIBART_H 1
+
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_gray_svp.h>
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_pathcode.h>
+#include <libart_lgpl/art_pixbuf.h>
+#include <libart_lgpl/art_point.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_rect_svp.h>
+#include <libart_lgpl/art_rect_uta.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_affine.h>
+#include <libart_lgpl/art_rgb_bitmap_affine.h>
+#include <libart_lgpl/art_rgb_pixbuf_affine.h>
+#include <libart_lgpl/art_rgb_rgba_affine.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_ops.h>
+#include <libart_lgpl/art_svp_point.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_uta_ops.h>
+#include <libart_lgpl/art_uta_rect.h>
+#include <libart_lgpl/art_uta_svp.h>
+#include <libart_lgpl/art_uta_vpath.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_vpath_svp.h>
+
+#endif
diff --git a/src/libart_lgpl/libartConf.sh.in b/src/libart_lgpl/libartConf.sh.in
new file mode 100644
index 0000000..189db1f
--- /dev/null
+++ b/src/libart_lgpl/libartConf.sh.in
@@ -0,0 +1,6 @@
+#
+# Configuration file for using the LIBART library in GNOME applications
+#
+LIBART_LIBDIR="@LIBART_LIBDIR@"
+LIBART_LIBS="@LIBART_LIBS@"
+LIBART_INCLUDEDIR="@LIBART_INCLUDEDIR@"
diff --git a/src/libart_lgpl/makefile.msc b/src/libart_lgpl/makefile.msc
new file mode 100644
index 0000000..6451165
--- /dev/null
+++ b/src/libart_lgpl/makefile.msc
@@ -0,0 +1,104 @@
+PKG_CFLAGS = -I.. -DLIBART_COMPILATION -DDLL_EXPORT
+
+OBJECTS = \
+	art_affine.obj \
+	art_alphagamma.obj \
+	art_bpath.obj \
+	art_gray_svp.obj \
+	art_misc.obj \
+	art_pixbuf.obj \
+	art_rect.obj \
+	art_rect_svp.obj \
+	art_rect_uta.obj \
+	art_render.obj \
+	art_render_gradient.obj \
+	art_render_mask.obj \
+	art_render_svp.obj \
+	art_rgb.obj \
+	art_rgba.obj \
+	art_rgb_a_affine.obj \
+	art_rgb_affine.obj \
+	art_rgb_affine_private.obj \
+	art_rgb_bitmap_affine.obj \
+	art_rgb_pixbuf_affine.obj \
+	art_rgb_rgba_affine.obj \
+	art_rgb_svp.obj \
+	art_svp.obj \
+	art_svp_intersect.obj \
+	art_svp_ops.obj \
+	art_svp_point.obj \
+	art_svp_render_aa.obj \
+	art_svp_vpath.obj \
+	art_svp_vpath_stroke.obj \
+	art_svp_wind.obj \
+	art_uta.obj \
+	art_uta_ops.obj \
+	art_uta_rect.obj \
+	art_uta_svp.obj \
+	art_uta_vpath.obj \
+	art_vpath.obj \
+	art_vpath_bpath.obj \
+	art_vpath_dash.obj \
+	art_vpath_svp.obj \
+	libart-features.obj
+
+testOBJECTS = \
+	testart.obj \
+	testuta.obj \
+
+## common stuff
+## compiler and linker switches
+!IFNDEF DEBUG
+# Full optimization:
+OPTIMIZE = -Ox -MD
+LINKDEBUG =
+!ELSE
+# Debugging:
+OPTIMIZE = -Zi -MDd
+LINKDEBUG = /debug
+!ENDIF
+
+# cl -? describes the options
+CC = cl -G5 -GF $(OPTIMIZE) -W3 -nologo
+
+# No general LDFLAGS needed
+LDFLAGS = /link $(LINKDEBUG)
+INSTALL = copy
+
+CFLAGS = -I. -DHAVE_CONFIG_H
+
+## targets
+all : \
+	config.h \
+	art_config.h \
+	libart_lgpl_2-2.dll
+
+libart_lgpl_2-2.dll : $(OBJECTS) libart.def
+	$(CC) $(CFLAGS) -LD -Felibart_lgpl_2-2.dll $(OBJECTS) user32.lib advapi32.lib wsock32.lib $(LDFLAGS) /def:libart.def /implib:art_lgpl_2.lib
+
+config.h: config.h.win32
+	copy config.h.win32 config.h
+
+art_config.h : gen_art_config.exe
+	gen_art_config.exe > art_config.h
+
+gen_art_config.exe : gen_art_config.obj
+	$(CC) $(CFLAGS) -Fegen_art_config.exe gen_art_config.obj user32.lib
+
+.c.obj :
+	$(CC) $(CFLAGS) -GD -c $(PKG_CFLAGS) $<
+
+clean:
+	del config.h
+	del *.exe
+	del *.obj
+	del *.dll
+	del *.lib
+	del *.err
+	del *.map
+	del *.sym
+	del *.exp
+	del *.lk1
+	del *.mk1
+	del *.pdb
+	del *.ilk
diff --git a/src/libart_lgpl/test_gradient.c b/src/libart_lgpl/test_gradient.c
new file mode 100644
index 0000000..65e4a2e
--- /dev/null
+++ b/src/libart_lgpl/test_gradient.c
@@ -0,0 +1,376 @@
+/* test_gradient.c */
+
+#include <math.h>
+#include <gtk/gtk.h>
+#include "art_point.h"
+#include "art_misc.h"
+#include "art_affine.h"
+#include "art_svp.h"
+#include "art_svp_vpath.h"
+#include "art_rgb.h"
+#include "art_rgb_svp.h"
+#include "art_render.h"
+#include "art_render_gradient.h"
+
+GtkWidget *drawing_area;
+static art_u8 *rgbdata = NULL;
+int width, height;
+
+double pos_x[2], pos_y[2];
+int pos_nr = 0;
+
+double a = 1/32.0;
+double b = -1/33.00000;
+double c = -1/3.0;
+ArtGradientSpread spread = ART_GRADIENT_REPEAT;
+
+ArtVpath *
+randstar (int n)
+{
+  ArtVpath *vec;
+  int i;
+  double r, th;
+
+  vec = art_new (ArtVpath, n + 2);
+  for (i = 0; i < n; i++)
+    {
+      vec[i].code = i ? ART_LINETO : ART_MOVETO;
+      r = rand () * (250.0 / RAND_MAX);
+      th = i * 2 * M_PI / n;
+      vec[i].x = 250 + r * cos (th);
+      vec[i].y = 250 - r * sin (th);
+    }
+  vec[i].code = ART_LINETO;
+  vec[i].x = vec[0].x;
+  vec[i].y = vec[0].y;
+  i++;
+  vec[i].code = ART_END;
+  vec[i].x = 0;
+  vec[i].y = 0;
+  return vec;
+}
+
+ArtVpath *
+rect (void)
+{
+  ArtVpath *vec;
+  int i;
+  double r, th;
+
+#define START 0
+  
+  vec = art_new (ArtVpath, 6);
+  vec[0].code = ART_MOVETO;
+  vec[0].x = START;
+  vec[0].y = START; 
+  vec[1].code = ART_LINETO;
+  vec[1].x = START;
+  vec[1].y = 512-START; 
+  vec[2].code = ART_LINETO;
+  vec[2].x = 512-START;
+  vec[2].y = 512-START; 
+  vec[3].code = ART_LINETO;
+  vec[3].x = 512-START;
+  vec[3].y = START; 
+  vec[4].code = ART_LINETO;
+  vec[4].x = START;
+  vec[4].y = START; 
+  vec[5].code = ART_END;
+  vec[5].x = 0;
+  vec[5].y = 0; 
+  
+  return vec;
+}
+
+
+static void
+draw_test()
+{
+  static ArtVpath *vpath = NULL;
+  static ArtSVP *svp = NULL;
+  ArtRender *render;
+  ArtPixMaxDepth color[3] = {0x0000, 0x0000, 0x8000 };
+  ArtGradientLinear gradient;
+  ArtGradientStop stops[3] = {
+    { 0.02, { 0x7fff, 0x0000, 0x0000, 0x7fff }},
+    { 0.5, { 0x0000, 0x0000, 0x0000, 0x1000 }},
+    { 0.98, { 0x0000, 0x7fff, 0x0000, 0x7fff }}
+  };
+
+  if (!vpath) {
+    vpath = rect ();
+    svp = art_svp_from_vpath (vpath);
+  }
+
+  gradient.a = a;
+  gradient.b = b;
+  gradient.c = c;
+  gradient.spread = spread;
+  
+  gradient.n_stops = sizeof(stops) / sizeof(stops[0]);
+  gradient.stops = stops;
+  
+  render = art_render_new (0, 0,
+			   width, height,
+			   rgbdata, width * 3,
+			   3, 8, ART_ALPHA_NONE,
+			   NULL);
+  art_render_clear_rgb (render, 0xfff0c0);
+  art_render_svp (render, svp);
+  art_render_gradient_linear (render, &gradient, ART_FILTER_NEAREST);
+  //  art_render_image_solid (render, color);
+  art_render_invoke (render);
+}
+
+/* Create a new backing pixmap of the appropriate size */
+static gint configure_event( GtkWidget         *widget,
+                             GdkEventConfigure *event )
+{
+  if (rgbdata)
+    free(rgbdata);
+
+  rgbdata = malloc(3*widget->allocation.width*widget->allocation.height);
+  width = widget->allocation.width;
+  height = widget->allocation.height;
+
+  draw_test();
+  
+  return TRUE;
+}
+
+/* Redraw the screen from the backing pixmap */
+static gint expose_event( GtkWidget      *widget,
+                          GdkEventExpose *event )
+{
+  static GdkGC *copy_gc = NULL;
+
+  if (copy_gc == NULL) {
+    copy_gc = gdk_gc_new(widget->window);
+  }
+
+  gdk_draw_rgb_image(widget->window,
+		     copy_gc,
+		     event->area.x, event->area.y,
+		     event->area.width, event->area.height,
+		     GDK_RGB_DITHER_NONE,
+		     rgbdata + event->area.x*3+event->area.y*3*width,
+		     width*3 );
+
+  return FALSE;
+}
+
+static gint button_press_event( GtkWidget      *widget,
+                                GdkEventButton *event )
+{
+  static GdkGC *copy_gc = NULL;
+  int x, y;
+  
+  x = event->x;
+  y = event->y;
+
+  pos_x[pos_nr] = (double) x;
+  pos_y[pos_nr] = (double) y;
+
+  pos_nr = (pos_nr+1) % 2;
+
+  draw_test();
+
+  if (copy_gc == NULL) {
+    copy_gc = gdk_gc_new(widget->window);
+  }
+
+  gdk_draw_rgb_image(widget->window,
+		     copy_gc,
+		     0, 0,
+		     width, height,
+		     GDK_RGB_DITHER_NONE,
+		     rgbdata, width*3 );
+
+  return TRUE;
+}
+
+static gint motion_notify_event( GtkWidget *widget,
+                                 GdkEventMotion *event )
+{
+  static GdkGC *copy_gc = NULL;
+  int x, y;
+  GdkModifierType state;
+
+  if (event->is_hint) {
+    gdk_window_get_pointer (event->window, &x, &y, &state);
+  } else {
+    x = event->x;
+    y = event->y;
+    state = event->state;
+  }
+
+  if (state & GDK_BUTTON1_MASK && rgbdata != NULL) {
+    
+    pos_x[(pos_nr+1) % 2] = (double) x;
+    pos_y[(pos_nr+1) % 2] = (double) y;
+    
+    draw_test();
+    
+    if (copy_gc == NULL) {
+      copy_gc = gdk_gc_new(widget->window);
+    }
+    
+    gdk_draw_rgb_image(widget->window,
+		       copy_gc,
+		       0, 0,
+		       width, height,
+		       GDK_RGB_DITHER_NONE,
+		       rgbdata, width*3 );
+  }
+  
+  return TRUE;
+}
+
+void quit ()
+{
+  gtk_exit (0);
+}
+
+static void
+change_gradient (GtkEntry *entry,
+		 double *ptr)
+{
+  double d;
+  d = g_ascii_strtod (gtk_entry_get_text (entry), NULL);
+  if (d != 0.0)
+    *ptr = 1.0/d;
+  else
+    *ptr = 0.0;
+  draw_test();
+  gtk_widget_queue_draw (drawing_area);
+}
+
+static void
+change_spread (GtkEntry *entry,
+	       int *ptr)
+{
+  double d;
+  d = g_ascii_strtod (gtk_entry_get_text (entry), NULL);
+  *ptr = (int)d;
+  draw_test();
+  gtk_widget_queue_draw (drawing_area);
+}
+
+
+int main( int   argc, 
+          char *argv[] )
+{
+  GtkWidget *window;
+  GtkWidget *vbox;
+  char buf[G_ASCII_DTOSTR_BUF_SIZE];
+
+  GtkWidget *button;
+  GtkWidget *entry;
+
+  gtk_init (&argc, &argv);
+
+  gdk_rgb_init();
+
+  pos_x[0] = 100;
+  pos_y[0] = 100;
+  pos_x[1] = 300;
+  pos_y[1] = 300;
+  
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_name (window, "Test Input");
+
+  vbox = gtk_vbox_new (FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+  gtk_widget_show (vbox);
+
+  gtk_signal_connect (GTK_OBJECT (window), "destroy",
+		      GTK_SIGNAL_FUNC (quit), NULL);
+
+  /* Create the drawing area */
+
+  drawing_area = gtk_drawing_area_new ();
+  gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), 512, 512);
+  gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
+
+  gtk_widget_show (drawing_area);
+
+  /* Signals used to handle backing pixmap */
+
+  gtk_signal_connect (GTK_OBJECT (drawing_area), "expose_event",
+		      (GtkSignalFunc) expose_event, NULL);
+  gtk_signal_connect (GTK_OBJECT(drawing_area),"configure_event",
+		      (GtkSignalFunc) configure_event, NULL);
+
+  /* Event signals */
+
+  gtk_signal_connect (GTK_OBJECT (drawing_area), "motion_notify_event",
+		      (GtkSignalFunc) motion_notify_event, NULL);
+  gtk_signal_connect (GTK_OBJECT (drawing_area), "button_press_event",
+		      (GtkSignalFunc) button_press_event, NULL);
+
+  gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
+			 | GDK_LEAVE_NOTIFY_MASK
+			 | GDK_BUTTON_PRESS_MASK
+			 | GDK_POINTER_MOTION_MASK
+			 | GDK_POINTER_MOTION_HINT_MASK);
+
+  
+  entry = gtk_entry_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+
+  gtk_signal_connect (GTK_OBJECT (entry), "activate",
+		      GTK_SIGNAL_FUNC (change_gradient),
+		      &a);
+  gtk_entry_set_text  (GTK_ENTRY (entry),
+		       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
+				       1.0/a));
+  gtk_widget_show (entry);
+
+  entry = gtk_entry_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+
+  gtk_signal_connect (GTK_OBJECT (entry), "activate",
+		      GTK_SIGNAL_FUNC (change_gradient),
+		      &b);
+  gtk_entry_set_text  (GTK_ENTRY (entry),
+		       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
+				       1.0/b));
+  gtk_widget_show (entry);
+
+  entry = gtk_entry_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+
+  gtk_signal_connect (GTK_OBJECT (entry), "activate",
+		      GTK_SIGNAL_FUNC (change_gradient),
+		      &c);
+  gtk_entry_set_text  (GTK_ENTRY (entry),
+		       g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
+				       1.0/c));
+  gtk_widget_show (entry);
+
+  entry = gtk_entry_new ();
+  gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
+
+  gtk_signal_connect (GTK_OBJECT (entry), "activate",
+		      GTK_SIGNAL_FUNC (change_spread),
+		      &spread);
+  gtk_entry_set_text  (GTK_ENTRY (entry),
+		       g_strdup_printf ("%d", spread));
+  gtk_widget_show (entry);
+
+  
+  /* .. And a quit button */
+  button = gtk_button_new_with_label ("Quit");
+  gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+			     GTK_SIGNAL_FUNC (gtk_widget_destroy),
+			     GTK_OBJECT (window));
+  gtk_widget_show (button);
+
+  gtk_widget_show (window);
+
+  gtk_main ();
+
+  return 0;
+}
diff --git a/src/libart_lgpl/testart.c b/src/libart_lgpl/testart.c
new file mode 100644
index 0000000..5fa580c
--- /dev/null
+++ b/src/libart_lgpl/testart.c
@@ -0,0 +1,635 @@
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 1998, 1999 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "art_misc.h"
+#include "art_vpath.h"
+#include "art_svp.h"
+#include "art_svp_vpath.h"
+#include "art_gray_svp.h"
+#include "art_rgb_svp.h"
+#include "art_svp_vpath_stroke.h"
+#include "art_svp_ops.h"
+#include "art_affine.h"
+#include "art_rgb_affine.h"
+#include "art_rgb_bitmap_affine.h"
+#include "art_rgb_rgba_affine.h"
+#include "art_alphagamma.h"
+#include "art_svp_point.h"
+#include "art_vpath_dash.h"
+#include "art_render.h"
+#include "art_render_gradient.h"
+#include "art_render_svp.h"
+#include "art_svp_intersect.h"
+
+#ifdef DEAD_CODE
+static void
+test_affine (void) {
+  double src[6];
+  double dst[6];
+  double src2[6];
+  char str[128];
+  int i;
+  ArtPoint ps, pd, ptmp;
+
+  for (i = 0; i < 6; i++)
+    {
+      src[i] = (rand () * 2.0 / RAND_MAX) - 1.0;
+      src2[i] = (rand () * 2.0 / RAND_MAX) - 1.0;
+    }
+#if 0
+  src[0] = 0.9999999;
+  src[1] = -0.000001;
+  src[2] = 0.000001;
+  src[3] = 0.9999999;
+  src[4] = 0;
+  src[5] = 0;
+#if 1
+  src[0] = 0.98480775;
+  src[1] = -0.17364818;
+  src[2] = 0.17364818;
+  src[3] = 0.98480775;
+#endif
+
+  src2[0] = 0.98480775;
+  src2[1] = -0.17364818;
+  src2[2] = 0.17364818;
+  src2[3] = 0.98480775;
+#endif
+
+
+  ps.x = rand() * 100.0 / RAND_MAX;
+  ps.y = rand() * 100.0 / RAND_MAX;
+
+  art_affine_point (&pd, &ps, src);
+  art_affine_invert (dst, src);
+  art_affine_point (&ptmp, &pd, dst);
+  art_affine_to_string (str, src);
+  printf ("src = %s\n", str);
+  art_affine_to_string (str, dst);
+  printf ("dst = %s\n", str);
+  printf ("point (%g, %g) -> (%g, %g) -> (%g, %g)\n",
+	  ps.x, ps.y, pd.x, pd.y, ptmp.x, ptmp.y);
+
+  art_affine_point (&ptmp, &ps, src);
+  art_affine_point (&pd, &ptmp, src2);
+  art_affine_to_string (str, src2);
+  printf ("src2 = %s\n", str);
+  printf ("point (%g, %g) -> (%g, %g) -> (%g, %g)\n",
+	  ps.x, ps.y, ptmp.x, ptmp.y, pd.x, pd.y);
+  art_affine_multiply (dst, src, src2);
+  art_affine_to_string (str, dst);
+  printf ("dst = %s\n", str);
+  art_affine_point (&pd, &ps, dst);
+  printf ("point (%g, %g) -> (%g, %g)\n",
+	  ps.x, ps.y, pd.x, pd.y);
+
+}
+#endif
+
+static ArtVpath *
+randstar (int n)
+{
+  ArtVpath *vec;
+  int i;
+  double r, th;
+
+  vec = art_new (ArtVpath, n + 2);
+  for (i = 0; i < n; i++)
+    {
+      vec[i].code = i ? ART_LINETO : ART_MOVETO;
+      r = rand () * (250.0 / RAND_MAX);
+#if 0
+      r = r + 0.9 * (250 - r);
+#endif
+      th = i * 2 * M_PI / n;
+      vec[i].x = 250 + r * cos (th);
+      vec[i].y = 250 - r * sin (th);
+    }
+  vec[i].code = ART_LINETO;
+  vec[i].x = vec[0].x;
+  vec[i].y = vec[0].y;
+  i++;
+  vec[i].code = ART_END;
+  vec[i].x = 0;
+  vec[i].y = 0;
+  return vec;
+}
+
+#define TILE_SIZE 512
+#define NUM_ITERS 1
+#define COLOR
+
+#ifdef COLOR
+#define BYTES_PP 3
+#else
+#define BYTES_PP 1
+#endif
+
+#ifndef nDEAD_CODE
+static void
+print_svp (ArtSVP *vp)
+{
+  int i, j;
+
+  for (i = 0; i < vp->n_segs; i++)
+    {
+      printf ("segment %d, dir = %s (%f, %f) - (%f, %f)\n",
+	      i, vp->segs[i].dir ? "down" : "up",
+	      vp->segs[i].bbox.x0,
+	      vp->segs[i].bbox.y0,
+	      vp->segs[i].bbox.x1,
+	      vp->segs[i].bbox.y1);
+      for (j = 0; j < vp->segs[i].n_points; j++)
+        printf ("  (%g, %g)\n",
+                vp->segs[i].points[j].x,
+                vp->segs[i].points[j].y);
+    }
+}
+#endif
+
+static void
+print_vpath (ArtVpath *vpath)
+{
+  int i;
+
+  for (i = 0; vpath[i].code != ART_END; i++)
+    printf ("%g %g %s\n",
+	    vpath[i].x, vpath[i].y,
+	    vpath[i].code == ART_MOVETO_OPEN ? "moveto %open" :
+	    vpath[i].code == ART_MOVETO ? "moveto" :
+	    vpath[i].code == ART_LINETO ? "lineto" :
+	    "?");
+
+  printf ("stroke\n");
+}
+
+static void
+make_testpat (void)
+{
+  ArtVpath *vpath, *vpath2, *vpath3;
+  ArtSVP *svp, *svp2;
+  ArtSVP *svp3;
+  art_u8 buf[512 * 512 * BYTES_PP];
+  int i, j;
+  int iter;
+  art_u8 colorimg[256][256][3];
+  art_u8 rgbaimg[256][256][4];
+  art_u8 bitimg[16][2];
+  int x, y;
+  double affine[6];
+  double affine2[6];
+  double affine3[6];
+  ArtAlphaGamma *alphagamma;
+  double dash_data[] = { 20 };
+  ArtVpathDash dash;
+
+  dash.offset = 0;
+  dash.n_dash = 1;
+  dash.dash = dash_data;
+
+#ifdef TEST_AFFINE
+  test_affine ();
+  exit (0);
+#endif
+
+  vpath = randstar (50);
+  svp = art_svp_from_vpath (vpath);
+  art_free (vpath);
+
+  vpath2 = randstar (50);
+#if 1
+  vpath3 = art_vpath_dash (vpath2, &dash);
+  art_free (vpath2);
+  svp2 = art_svp_vpath_stroke (vpath3,
+			       ART_PATH_STROKE_JOIN_MITER,
+			       ART_PATH_STROKE_CAP_BUTT,
+			       15,
+			       4,
+			       0.5);
+  art_free (vpath3);
+#else
+  svp2 = art_svp_from_vpath (vpath2);
+#endif
+
+#if 1
+  svp3 = art_svp_intersect (svp, svp2);
+#else
+  svp3 = svp2;
+#endif
+
+#if 0
+  print_svp (svp);
+#endif
+
+  for (y = 0; y < 256; y++)
+    for (x = 0; x < 256; x++)
+      {
+	colorimg[y][x][0] = (x + y) >> 1;
+	colorimg[y][x][1] = (x + (255 - y)) >> 1;
+	colorimg[y][x][2] = ((255 - x) + y) >> 1;
+
+	rgbaimg[y][x][0] = (x + y) >> 1;
+	rgbaimg[y][x][1] = (x + (255 - y)) >> 1;
+	rgbaimg[y][x][2] = ((255 - x) + y) >> 1;
+	rgbaimg[y][x][3] = y;
+      }
+
+  for (y = 0; y < 16; y++)
+    for (x = 0; x < 2; x++)
+      bitimg[y][x] = (x << 4) | y;
+
+  affine[0] = 0.5;
+  affine[1] = .2;
+  affine[2] = -.2;
+  affine[3] = 0.5;
+  affine[4] = 64;
+  affine[5] = 64;
+
+  affine2[0] = 1;
+  affine2[1] = -.2;
+  affine2[2] = .2;
+  affine2[3] = 1;
+  affine2[4] = 128;
+  affine2[5] = 128;
+
+  affine3[0] = 5;
+  affine3[1] = -.2;
+  affine3[2] = .2;
+  affine3[3] = 5;
+  affine3[4] = 384;
+  affine3[5] = 32;
+
+#if 0
+  alphagamma = art_alphagamma_new (1.8);
+#else
+  alphagamma = NULL;
+#endif
+
+#ifdef COLOR
+  printf ("P6\n512 512\n255\n");
+#else
+  printf ("P5\n512 512\n255\n");
+#endif
+  for (iter = 0; iter < NUM_ITERS; iter++)
+    for (j = 0; j < 512; j += TILE_SIZE)
+      for (i = 0; i < 512; i += TILE_SIZE)
+	{
+#ifdef COLOR
+	  art_rgb_svp_aa (svp, i, j, i + TILE_SIZE, j + TILE_SIZE,
+			  0xffe0a0, 0x100040,
+			  buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP,
+			  alphagamma);
+	  art_rgb_svp_alpha (svp2, i, j, i + TILE_SIZE, j + TILE_SIZE,
+			     0xff000080,
+			     buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP,
+			     alphagamma);
+	  art_rgb_svp_alpha (svp3, i, j, i + TILE_SIZE, j + TILE_SIZE,
+			     0x00ff0080,
+			     buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP,
+			     alphagamma);
+	  art_rgb_affine (buf + (j * 512 + i) * BYTES_PP,
+			  i, j, i + TILE_SIZE, j + TILE_SIZE, 512 * BYTES_PP,
+			  (art_u8 *)colorimg, 256, 256, 256 * 3,
+			  affine,
+			  ART_FILTER_NEAREST, alphagamma);
+	  art_rgb_rgba_affine (buf + (j * 512 + i) * BYTES_PP,
+			       i, j, i + TILE_SIZE, j + TILE_SIZE,
+			       512 * BYTES_PP,
+			       (art_u8 *)rgbaimg, 256, 256, 256 * 4,
+			       affine2,
+			       ART_FILTER_NEAREST, alphagamma);
+	  art_rgb_bitmap_affine (buf + (j * 512 + i) * BYTES_PP,
+				 i, j, i + TILE_SIZE, j + TILE_SIZE,
+				 512 * BYTES_PP,
+				 (art_u8 *)bitimg, 16, 16, 2,
+				 0xffff00ff,
+				 affine3,
+				 ART_FILTER_NEAREST, alphagamma);
+#else
+	  art_gray_svp_aa (svp, i, j, i + TILE_SIZE, j + TILE_SIZE,
+			   buf + (j * 512 + i) * BYTES_PP, 512 * BYTES_PP);
+#endif
+	}
+
+  art_svp_free (svp2);
+  art_svp_free (svp3);
+  art_svp_free (svp);
+
+#if 1
+  fwrite (buf, 1, 512 * 512 * BYTES_PP, stdout);
+#endif
+}
+
+static void
+test_dist (void)
+{
+  ArtVpath *vpath;
+  ArtSVP *svp;
+  art_u8 buf[512 * 512 * BYTES_PP];
+  int x, y;
+  int ix;
+  double dist;
+  int wind;
+
+  vpath = randstar (20);
+#ifdef NO_STROKE
+  svp = art_svp_from_vpath (vpath);
+#else
+  svp = art_svp_vpath_stroke (vpath,
+			       ART_PATH_STROKE_JOIN_MITER,
+			       ART_PATH_STROKE_CAP_BUTT,
+			       15,
+			       4,
+			       0.5);
+#endif
+
+  art_rgb_svp_aa (svp, 0, 0, 512, 512,
+		  0xffe0a0, 0x100040,
+		  buf, 512 * BYTES_PP,
+		  NULL);
+
+  ix = 0;
+  for (y = 0; y < 512; y++)
+    {
+      for (x = 0; x < 512; x++)
+	{
+	  wind = art_svp_point_wind (svp, x, y);
+	  buf[ix] = 204 - wind * 51;
+	  dist = art_svp_point_dist (svp, x, y);
+	  if (((x | y) & 0x3f) == 0)
+	    {
+	      fprintf (stderr, "%d,%d: %f\n", x, y, dist);
+	    }
+	  buf[ix + 1] = 255 - dist;
+	  ix += 3;
+	}
+    }
+
+  printf ("P6\n512 512\n255\n");
+  fwrite (buf, 1, 512 * 512 * BYTES_PP, stdout);
+
+}
+
+static void
+test_dash (void)
+{
+  ArtVpath *vpath, *vpath2;
+  double dash_data[] = { 10, 4, 1, 4};
+  ArtVpathDash dash;
+
+  dash.offset = 0;
+  dash.n_dash = 3;
+  dash.dash = dash_data;
+
+  vpath = randstar (50);
+  vpath2 = art_vpath_dash (vpath, &dash);
+  printf ("%%!\n");
+  print_vpath (vpath2);
+  printf ("showpage\n");
+  art_free (vpath);
+  art_free (vpath2);
+}
+
+static void
+test_render_gradient (art_u8 *buf)
+{
+  ArtGradientLinear gradient;
+  ArtGradientStop stops[3] = {
+    { 0.0, { 0x7fff, 0x0000, 0x0000, 0x7fff }},
+    { 0.5, { 0x0000, 0x0000, 0x0000, 0x1000 }},
+    { 1.0, { 0x0000, 0x7fff, 0x0000, 0x7fff }}
+  };
+  ArtVpath *vpath;
+  ArtSVP *svp;
+  ArtRender *render;
+
+  gradient.a = 0.003;
+  gradient.b = -0.0015;
+  gradient.c = 0.1;
+  gradient.spread = ART_GRADIENT_PAD;
+  gradient.n_stops = sizeof(stops) / sizeof(stops[0]);
+  gradient.stops = stops;
+
+  vpath = randstar (50);
+  svp = art_svp_from_vpath (vpath);
+
+  render = art_render_new (0, 0, 512, 512, buf, 512 * 3, 3, 8, ART_ALPHA_NONE,
+			   NULL);
+  art_render_svp (render, svp);
+  art_render_gradient_linear (render, &gradient, ART_FILTER_NEAREST);
+  art_render_invoke (render);
+
+}
+
+static void
+test_render_rad_gradient (art_u8 *buf)
+{
+  ArtGradientRadial gradient;
+  ArtGradientStop stops[3] = {
+    { 0.0, { 0xffff, 0x0000, 0x0000, 0xffff }},
+    { 0.5, { 0xe000, 0xe000, 0x0000, 0xe000 }},
+    { 1.0, { 0x0000, 0x0000, 0x0000, 0x0000 }}
+  };
+  ArtVpath *vpath;
+  ArtSVP *svp;
+  ArtRender *render;
+
+  gradient.affine[0] = 3.0 / 512;
+  gradient.affine[1] = 0;
+  gradient.affine[2] = 0;
+  gradient.affine[3] = 3.0 / 512;
+  gradient.affine[4] = -1.5;
+  gradient.affine[5] = -1.5;
+  gradient.fx = 0.9;
+  gradient.fy = 0.1;
+
+  gradient.n_stops = sizeof(stops) / sizeof(stops[0]);
+  gradient.stops = stops;
+
+  vpath = randstar (50);
+  svp = art_svp_from_vpath (vpath);
+
+  render = art_render_new (0, 0, 512, 512, buf, 512 * 3, 3, 8, ART_ALPHA_NONE,
+			   NULL);
+  art_render_svp (render, svp);
+  art_render_gradient_radial (render, &gradient, ART_FILTER_NEAREST);
+  art_render_invoke (render);
+
+}
+
+static void
+test_gradient (void)
+{
+  ArtVpath *vpath;
+  ArtSVP *svp;
+  art_u8 buf[512 * 512 * 3];
+  ArtRender *render;
+  ArtPixMaxDepth color[3] = {0x0000, 0x0000, 0x8000 };
+  int i;
+  const int n_iter = 1;
+
+  vpath = randstar (50);
+  svp = art_svp_from_vpath (vpath);
+
+  for (i = 0; i < n_iter; i++)
+    {
+#define USE_RENDER
+#ifdef USE_RENDER
+      render = art_render_new (0, 0, 512, 512, buf, 512 * 3, 3, 8, ART_ALPHA_NONE,
+			       NULL);
+      art_render_clear_rgb (render, 0xfff0c0);
+      art_render_svp (render, svp);
+      art_render_image_solid (render, color);
+      art_render_invoke (render);
+#else
+      art_rgb_svp_aa (svp, 0, 0, 512, 512, 0xfff0c0, 0x000080,
+		      buf, 512 * 3, NULL);
+#endif
+    }
+
+#if 1
+  test_render_gradient (buf);
+#endif
+  test_render_rad_gradient (buf);
+
+  printf ("P6\n512 512\n255\n");
+  fwrite (buf, 1, 512 * 512 * 3, stdout);
+}
+
+#if 0
+static void
+output_svp_ppm (const ArtSVP *svp)
+{
+  art_u8 buf[512 * 512 * 3];
+  art_rgb_svp_aa (svp, 0, 0, 512, 512, 0xfff0c0, 0x000080,
+		  buf, 512 * 3, NULL);
+  printf ("P6\n512 512\n255\n");
+  fwrite (buf, 1, 512 * 512 * 3, stdout);
+}
+#endif
+
+static void
+test_intersect (void)
+{
+  ArtVpath vpath[] = {
+
+#if 0
+    /* two triangles */
+    { ART_MOVETO, 100, 100 },
+    { ART_LINETO, 300, 400 },
+    { ART_LINETO, 400, 200 },
+    { ART_LINETO, 100, 100 },
+    { ART_MOVETO, 110, 110 },
+    { ART_LINETO, 310, 410 },
+    { ART_LINETO, 410, 210 },
+    { ART_LINETO, 110, 110 },
+#endif
+
+#if 0
+    /* a bowtie */
+    { ART_MOVETO, 100, 100 },
+    { ART_LINETO, 400, 400 },
+    { ART_LINETO, 400, 100 },
+    { ART_LINETO, 100, 400 },
+    { ART_LINETO, 100, 100 },
+#endif
+
+#if 1
+    /* a square */
+    { ART_MOVETO, 100, 100 },
+    { ART_LINETO, 100, 400 },
+    { ART_LINETO, 400, 400 },
+    { ART_LINETO, 400, 100 },
+    { ART_LINETO, 100, 100 },
+#endif
+
+#if 1
+    /* another square */
+#define XOFF 10
+#define YOFF 10
+    { ART_MOVETO, 100 + XOFF, 100 + YOFF },
+    { ART_LINETO, 100 + XOFF, 400 + YOFF },
+    { ART_LINETO, 400 + XOFF, 400 + YOFF },
+    { ART_LINETO, 400 + XOFF, 100 + YOFF },
+    { ART_LINETO, 100 + XOFF, 100 + YOFF },
+#endif
+
+    { ART_END, 0, 0}
+  };
+  ArtSVP *svp, *svp2;
+  ArtSvpWriter *swr;
+
+  svp = art_svp_from_vpath (vpath);
+
+#define RUN_INTERSECT
+#ifdef RUN_INTERSECT
+  swr = art_svp_writer_rewind_new (ART_WIND_RULE_ODDEVEN);
+  art_svp_intersector (svp, swr);
+
+  svp2 = art_svp_writer_rewind_reap (swr);
+#endif
+
+#if 0
+  output_svp_ppm (svp2);
+#else
+  print_svp (svp2);
+#endif
+
+  art_svp_free (svp);
+
+#ifdef RUN_INTERSECT
+  art_svp_free (svp2);
+#endif
+}
+
+static void
+usage (void)
+{
+  fprintf (stderr, "usage: testart <test>\n"
+"  where <test> is one of:\n"
+"  testpat    -- make random star + gradients test pattern\n"
+"  gradient   -- test pattern for rendered gradients\n"
+"  dash       -- dash test (output is valid PostScript)\n"
+"  dist       -- distance test\n"
+"  intersect  -- softball test for intersector\n");
+  exit (1);
+}
+
+int
+main (int argc, char **argv)
+{
+  if (argc < 2)
+    usage ();
+
+  if (!strcmp (argv[1], "testpat"))
+    make_testpat ();
+  else if (!strcmp (argv[1], "gradient"))
+    test_gradient ();
+  else if (!strcmp (argv[1], "dist"))
+    test_dist ();
+  else if (!strcmp (argv[1], "dash"))
+    test_dash ();
+  else if (!strcmp (argv[1], "intersect"))
+    test_intersect ();
+  else
+    usage ();
+  return 0;
+}
diff --git a/src/libart_lgpl/testuta.c b/src/libart_lgpl/testuta.c
new file mode 100644
index 0000000..48b8db4
--- /dev/null
+++ b/src/libart_lgpl/testuta.c
@@ -0,0 +1,232 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "art_misc.h"
+#include "art_uta.h"
+#include "art_vpath.h"
+#include "art_uta_vpath.h"
+#include "art_rect.h"
+#include "art_rect_uta.h"
+#include "art_uta_rect.h"
+
+#undef LIBART_COMPILATION
+#include "libart-features.h"
+
+#define TEST_UTA
+#define noTEST_UTA_SPEED
+
+#define XOFF 50
+#define YOFF 700
+
+static void
+print_uta_ps (ArtUta *uta)
+{
+  int x, y;
+  int x0, y0, x1, y1;
+  int width = uta->width;
+  ArtUtaBbox ub;
+
+  for (y = 0; y < uta->height; y++)
+    for (x = 0; x < width; x++)
+      {
+	ub = uta->utiles[y * width + x];
+	if (ub != 0)
+	  {
+	    x0 = (uta->x0 + x) * ART_UTILE_SIZE + ART_UTA_BBOX_X0(ub);
+	    x1 = (uta->x0 + x) * ART_UTILE_SIZE + ART_UTA_BBOX_X1(ub);
+	    y0 = (uta->y0 + y) * ART_UTILE_SIZE + ART_UTA_BBOX_Y0(ub);
+	    y1 = (uta->y0 + y) * ART_UTILE_SIZE + ART_UTA_BBOX_Y1(ub);
+	    printf ("%% tile %d, %d: %d %d %d %d\n",
+		    x, y,
+		    ART_UTA_BBOX_X0(ub),
+		    ART_UTA_BBOX_Y0(ub),
+		    ART_UTA_BBOX_X1(ub),
+		    ART_UTA_BBOX_Y1(ub));
+	    printf ("%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath fill\n",
+		    XOFF + x0, YOFF - y0, XOFF + x1, YOFF - y0,
+		    XOFF + x1, YOFF - y1, XOFF + x0, YOFF - y1);
+	  }
+      }
+}
+
+static void
+print_rbuf_ps (int *rbuf, int width, int height)
+{
+  int x, y;
+
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++)
+      if (1 && rbuf[y * width + x] != 0)
+	printf ("%d %d moveto (%d) show\n", x * ART_UTILE_SIZE, y * ART_UTILE_SIZE,
+		rbuf[y * width + x]);
+}
+
+#if 0
+void
+randline (ArtUta *uta, int *rbuf, int rbuf_rowstride)
+{
+  double x0, y0, x1, y1;
+
+  x0 = rand () * (500.0 / RAND_MAX);
+  y0 = rand () * (500.0 / RAND_MAX);
+  x1 = rand () * (500.0 / RAND_MAX);
+  y1 = rand () * (500.0 / RAND_MAX);
+
+  printf ("%g %g moveto %g %g lineto stroke\n", x0, y0, x1, y1);
+  art_uta_add_line (uta, x0, y0, x1, y1, rbuf, rbuf_rowstride);
+}
+#endif
+
+static void
+print_ps_vpath (ArtVpath *vpath)
+{
+  int i;
+
+  for (i = 0; vpath[i].code != ART_END; i++)
+    {
+      switch (vpath[i].code)
+	{
+	case ART_MOVETO:
+	  printf ("%g %g moveto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+	  break;
+	case ART_LINETO:
+	  printf ("%g %g lineto\n", XOFF + vpath[i].x, YOFF - vpath[i].y);
+	  break;
+	default:
+	  break;
+	}
+    }
+  printf ("stroke\n");
+}
+
+static ArtVpath *
+randstar (int n)
+{
+  ArtVpath *vec;
+  int i;
+  double r, th;
+
+  vec = art_new (ArtVpath, n + 2);
+  for (i = 0; i < n; i++)
+    {
+      vec[i].code = i ? ART_LINETO : ART_MOVETO;
+      r = rand () * (250.0 / RAND_MAX);
+      th = i * 2 * M_PI / n;
+      vec[i].x = 250 + r * cos (th);
+      vec[i].y = 250 - r * sin (th);
+    }
+  vec[i].code = ART_LINETO;
+  vec[i].x = vec[0].x;
+  vec[i].y = vec[0].y;
+  i++;
+  vec[i].code = ART_END;
+  vec[i].x = 0;
+  vec[i].y = 0;
+  return vec;
+}
+
+int
+main (int argc, char **argv)
+{
+  ArtUta *uta;
+  int i;
+  int *rbuf;
+  ArtVpath *vec;
+  ArtIRect *rects;
+  int n_rects;
+
+  if (argc == 2)
+    srand (atoi (argv[1]));
+
+#ifdef TEST_UTA
+  printf ("%%!PS-Adobe\n");
+  printf ("%% libart version: %d.%d.%d\n",
+	  libart_major_version, libart_minor_version, libart_micro_version);
+  printf ("/Helvetica findfont 12 scalefont setfont\n");
+  printf ("0.5 setlinewidth\n");
+
+  printf ("0.5 setgray\n");
+  for (i = 0; i < 500; i += ART_UTILE_SIZE)
+    {
+      printf ("%d %d moveto %d %d lineto stroke\n",
+	      XOFF, YOFF - i, XOFF + 500, YOFF - i);
+      printf ("%d %d moveto %d %d lineto stroke\n",
+	      XOFF + i, YOFF, XOFF + i, YOFF - 500);
+    }
+
+  printf ("/a {\n");
+
+#if 1
+  vec = randstar (50);
+  print_ps_vpath (vec);
+  uta = art_uta_from_vpath (vec);
+#ifdef TEST_UTA_RECT
+  {
+    ArtIRect bbox = {5, 5, 450, 450};
+    uta = art_uta_from_irect (&bbox);
+  }
+#endif
+  rbuf = NULL;
+#else
+  uta = art_uta_new_coords (0, 0, 500, 500);
+
+  rbuf = malloc (sizeof(int) * (500 >> ART_UTILE_SHIFT) * (500 >> ART_UTILE_SHIFT));
+  for (i = 0; i < 10; i++)
+    randline (uta, rbuf, 500 >> ART_UTILE_SHIFT);
+#endif
+
+  printf ("} def 1 0.5 0.5 setrgbcolor\n");
+
+  print_uta_ps (uta);
+
+  printf ("0 0 0.5 setrgbcolor\n");
+
+  if (rbuf)
+    print_rbuf_ps (rbuf, 500 >> ART_UTILE_SHIFT, 500 >> ART_UTILE_SHIFT);
+
+  printf ("0 setgray a\n");
+
+  rects = art_rect_list_from_uta (uta, 256, 64, &n_rects);
+
+  printf ("%% %d rectangles:\n0 0 1 setrgbcolor\n", n_rects);
+
+  for (i = 0; i < n_rects; i++)
+    printf ("%d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath stroke\n",
+	    XOFF + rects[i].x0, YOFF - rects[i].y0,
+	    XOFF + rects[i].x1, YOFF - rects[i].y0,
+	    XOFF + rects[i].x1, YOFF - rects[i].y1,
+	    XOFF + rects[i].x0, YOFF - rects[i].y1);
+
+  printf ("showpage\n");
+#endif
+
+#ifdef TEST_UTA_SPEED
+  for (i = 0; i < 1000; i++)
+    {
+      vec = randstar (50);
+      uta = art_uta_from_vpath (vec);
+      art_free (vec);
+      art_uta_free (uta);
+    }
+#endif
+
+  return 0;
+}
diff --git a/src/libgnomecanvas/.gitignore b/src/libgnomecanvas/.gitignore
new file mode 100644
index 0000000..1eb948a
--- /dev/null
+++ b/src/libgnomecanvas/.gitignore
@@ -0,0 +1,3 @@
+gnome-canvas-marshal.c
+gnome-canvas-marshal.h
+
diff --git a/src/libgnomecanvas/Makefile.am b/src/libgnomecanvas/Makefile.am
new file mode 100644
index 0000000..3c7cb7e
--- /dev/null
+++ b/src/libgnomecanvas/Makefile.am
@@ -0,0 +1,75 @@
+
+INCLUDES = \
+	-I$(top_srcdir) 				\
+	-I$(top_builddir)				\
+	-I$(top_srcdir)/src				\
+	$(WARN_CFLAGS) 					\
+	$(GCOMPRIS_CFLAGS) \
+	$(FT2_CFLAGS)					\
+        -DGNOMECANVASLIBDIR=\""$(libdir)"\" 		\
+        -DGNOMECANVASDATADIR=\""$(datadir)"\" 		\
+        -DGNOMECANVASPIXMAPDIR=\""$(datadir)/pixmaps"\"	\
+        -DGNOMECANVASBINDIR=\""$(bindir)"\" 		\
+        -DGNOMECANVASLOCALSTATEDIR=\""$(localstatedir)"\" 	\
+	-DG_LOG_DOMAIN=\"GnomeCanvas\"
+
+noinst_LIBRARIES = libgnomecanvas-2.a
+
+libgnomecanvas_2_a_LIBADD = \
+	$(LIBGNOMECANVAS_LIBS)
+
+libgnomecanvasinclude_headers =			\
+	gnome-canvas-shape.h			\
+	gnome-canvas-bpath.h			\
+	gnome-canvas-clipgroup.h		\
+	gnome-canvas-line.h			\
+	gnome-canvas-pixbuf.h			\
+	gnome-canvas-polygon.h			\
+	gnome-canvas-rect-ellipse.h		\
+	gnome-canvas-text.h			\
+	gnome-canvas-rich-text.h		\
+	gnome-canvas-util.h			\
+	gnome-canvas-widget.h			\
+	gnome-canvas-path-def.h			\
+	gnome-canvas.h				\
+	libgnomecanvas.h
+
+libgnomecanvas_2_a_SOURCES =			\
+	$(libgnomecanvasinclude_headers)	\
+	gnome-canvas-shape.c			\
+	gnome-canvas-shape-private.h		\
+	gnome-canvas-bpath.c			\
+	gnome-canvas-path-def.c			\
+	gnome-canvas-clipgroup.c		\
+	gnome-canvas-i18n.h			\
+	gnome-canvas-line.c			\
+	gnome-canvas-pixbuf.c			\
+	gnome-canvas-polygon.c			\
+	gnome-canvas-rect-ellipse.c		\
+	gnome-canvas-text.c			\
+	gnome-canvas-rich-text.c		\
+	gnome-canvas-util.c			\
+	gnome-canvas-widget.c			\
+	gnome-canvas.c				\
+	libgnomecanvastypes.c
+
+$(libgnomecanvas_2_a_OBJECTS): $(marshal_sources)
+
+marshal_sources =                               \
+        gnome-canvas-marshal.c                  \
+        gnome-canvas-marshal.h
+
+BUILT_SOURCES =                                 \
+        $(marshal_sources)
+
+gnome-canvas-marshal.h: gnome-canvas-marshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --header --prefix=gnome_canvas_marshal > $@
+gnome-canvas-marshal.c: gnome-canvas-marshal.list $(GLIB_GENMARSHAL)
+	$(GLIB_GENMARSHAL) $< --body --prefix=gnome_canvas_marshal > $@
+
+CLEANFILES = $(marshal_sources)
+
+DONT_DIST_SOURCE = $(marshal_sources)
+
+EXTRA_DIST = \
+	gnome-canvas-marshal.list
diff --git a/src/libgnomecanvas/gnome-canvas-bpath.c b/src/libgnomecanvas/gnome-canvas-bpath.c
new file mode 100644
index 0000000..9c59aa3
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-bpath.c
@@ -0,0 +1,175 @@
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph acm org>
+ *          Lauris Kaplinski <lauris ximian com>
+ *          Miguel de Icaza <miguel kernel org>
+ *          Cody Russell <bratsche gnome org>
+ *          Rusty Conover <rconover bangtail net>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+   is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtkobject.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+
+#include "gnome-canvas-bpath.h"
+#include "gnome-canvas-shape.h"
+#include "gnome-canvas-shape-private.h"
+#include "gnome-canvas-path-def.h"
+
+enum {
+	PROP_0,
+	PROP_BPATH
+};
+
+static void gnome_canvas_bpath_class_init   (GnomeCanvasBpathClass *class);
+static void gnome_canvas_bpath_init         (GnomeCanvasBpath      *bpath);
+static void gnome_canvas_bpath_destroy      (GtkObject               *object);
+static void gnome_canvas_bpath_set_property (GObject               *object,
+					     guint                  param_id,
+					     const GValue          *value,
+                                             GParamSpec            *pspec);
+static void gnome_canvas_bpath_get_property (GObject               *object,
+					     guint                  param_id,
+					     GValue                *value,
+                                             GParamSpec            *pspec);
+
+static void   gnome_canvas_bpath_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+
+static GnomeCanvasShapeClass *parent_class;
+
+GtkType
+gnome_canvas_bpath_get_type (void)
+{
+	static GType bpath_type;
+
+	if (!bpath_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasBpathClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_bpath_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasBpath),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_bpath_init,
+			NULL			/* value_table */
+		};
+
+		bpath_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasBpath",
+						     &object_info, 0);
+	}
+
+	return bpath_type;
+}
+
+static void
+gnome_canvas_bpath_class_init (GnomeCanvasBpathClass *class)
+{
+	GObjectClass         *gobject_class;
+	GtkObjectClass       *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	/* when this gets checked into libgnomeui, change the
+           GTK_TYPE_POINTER to GTK_TYPE_GNOME_CANVAS_BPATH, and add an
+           entry to gnome-boxed.defs */
+
+	gobject_class->set_property = gnome_canvas_bpath_set_property;
+	gobject_class->get_property = gnome_canvas_bpath_get_property;
+
+	object_class->destroy = gnome_canvas_bpath_destroy;
+
+	g_object_class_install_property (gobject_class,
+                                         PROP_BPATH,
+                                         g_param_spec_boxed ("bpath", NULL, NULL,
+                                                             GNOME_TYPE_CANVAS_PATH_DEF,
+                                                             (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	item_class->update = gnome_canvas_bpath_update;
+}
+
+static void
+gnome_canvas_bpath_init (GnomeCanvasBpath *bpath)
+{
+
+}
+
+static void
+gnome_canvas_bpath_destroy (GtkObject *object)
+{
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gnome_canvas_bpath_set_property (GObject      *object,
+                                 guint         param_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+	GnomeCanvasItem         *item;
+	GnomeCanvasPathDef      *gpp;
+
+	item = GNOME_CANVAS_ITEM (object);
+
+	switch (param_id) {
+	case PROP_BPATH:
+		gpp = (GnomeCanvasPathDef*) g_value_get_boxed (value);
+
+		gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (object), gpp);
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+static void
+gnome_canvas_bpath_get_property (GObject     *object,
+                                 guint        param_id,
+                                 GValue      *value,
+                                 GParamSpec  *pspec)
+{
+	GnomeCanvasShape        *shape;
+
+	shape = GNOME_CANVAS_SHAPE(object);
+
+	switch (param_id) {
+	case PROP_BPATH:
+		g_value_set_boxed (value, shape->priv->path);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_bpath_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	if(GNOME_CANVAS_ITEM_CLASS(parent_class)->update) {
+		(* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(item, affine, clip_path, flags);
+	}
+}
diff --git a/src/libgnomecanvas/gnome-canvas-bpath.h b/src/libgnomecanvas/gnome-canvas-bpath.h
new file mode 100644
index 0000000..d7a9269
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-bpath.h
@@ -0,0 +1,61 @@
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph acm org>
+ *          Lauris Kaplinski <lauris ximian com>
+ *          Rusty Conover <rconover bangtail net>
+ */
+
+#ifndef GNOME_CANVAS_BPATH_H
+#define GNOME_CANVAS_BPATH_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-shape.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Bpath item for the canvas.
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * bpath		GnomeCanvasPathDef *		RW		Pointer to an GnomeCanvasPathDef structure.
+ *								This can be created by a call to
+ *								gp_path_new() in (gp-path.h).
+ */
+
+#define GNOME_TYPE_CANVAS_BPATH            (gnome_canvas_bpath_get_type ())
+#define GNOME_CANVAS_BPATH(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_BPATH, GnomeCanvasBpath))
+#define GNOME_CANVAS_BPATH_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_BPATH, GnomeCanvasBpathClass))
+#define GNOME_IS_CANVAS_BPATH(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_BPATH))
+#define GNOME_IS_CANVAS_BPATH_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_BPATH))
+
+
+typedef struct _GnomeCanvasBpath GnomeCanvasBpath;
+typedef struct _GnomeCanvasBpathPriv GnomeCanvasBpathPriv;
+typedef struct _GnomeCanvasBpathClass GnomeCanvasBpathClass;
+
+struct _GnomeCanvasBpath {
+	GnomeCanvasShape item;
+	
+};
+
+struct _GnomeCanvasBpathClass {
+	GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_bpath_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-clipgroup.c b/src/libgnomecanvas/gnome-canvas-clipgroup.c
new file mode 100644
index 0000000..ce77f49
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-clipgroup.c
@@ -0,0 +1,451 @@
+#define GNOME_CANVAS_CLIPGROUP_C
+
+/* Clipping group for GnomeCanvas
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Author:
+ *          Lauris Kaplinski <lauris ximian com>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+   is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtkobject.h>
+#include <gtk/gtkwidget.h>
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rect_svp.h>
+#include <libart_lgpl/art_gray_svp.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_svp_ops.h>
+
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-clipgroup.h"
+
+enum {
+	PROP_0,
+	PROP_PATH,
+	PROP_WIND
+};
+
+static void gnome_canvas_clipgroup_class_init      (GnomeCanvasClipgroupClass *klass);
+static void gnome_canvas_clipgroup_init            (GnomeCanvasClipgroup      *clipgroup);
+static void gnome_canvas_clipgroup_destroy         (GtkObject                 *object);
+static void gnome_canvas_clipgroup_set_property    (GObject                   *object,
+                                                    guint                      param_id,
+                                                    const GValue              *value,
+                                                    GParamSpec                *pspec);
+static void gnome_canvas_clipgroup_get_property    (GObject                   *object,
+                                                    guint                      param_id,
+                                                    GValue                    *value,
+                                                    GParamSpec                *pspec);
+static void gnome_canvas_clipgroup_update          (GnomeCanvasItem           *item,
+                                                    double                    *affine,
+                                                    ArtSVP                    *clip_path,
+                                                    int                        flags);
+
+/*
+ * Generic clipping stuff
+ *
+ * This is somewhat slow and memory-hungry - we add extra
+ * composition, extra SVP render and allocate 65536
+ * bytes for each clip level. It could be done more
+ * efficently per-object basis - but to make clipping
+ * universal, there is no alternative to double
+ * buffering (although it should be done into RGBA
+ * buffer by other method than ::render to make global
+ * opacity possible).
+ * Using art-render could possibly optimize that a bit,
+ * although I am not sure.
+ */
+
+#define GCG_BUF_WIDTH 128
+#define GCG_BUF_HEIGHT 128
+#define GCG_BUF_PIXELS (GCG_BUF_WIDTH * GCG_BUF_HEIGHT)
+#define GCG_BUF_SIZE (GCG_BUF_WIDTH * GCG_BUF_HEIGHT * 3)
+
+#define noSHOW_SHADOW
+
+static guchar *gcg_buf_new (void);
+static void gcg_buf_free (guchar *buf);
+static guchar *gcg_mask_new (void);
+static void gcg_mask_free (guchar *mask);
+
+static void gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+static GnomeCanvasGroupClass *parent_class;
+
+GType
+gnome_canvas_clipgroup_get_type (void)
+{
+	static GType clipgroup_type;
+
+	if (!clipgroup_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasClipgroupClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_clipgroup_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasClipgroup),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_clipgroup_init,
+			NULL			/* value_table */
+		};
+
+		clipgroup_type = g_type_register_static (GNOME_TYPE_CANVAS_GROUP, "GnomeCanvasClipgroup",
+							 &object_info, 0);
+	}
+
+	return clipgroup_type;
+}
+
+static void
+gnome_canvas_clipgroup_class_init (GnomeCanvasClipgroupClass *klass)
+{
+        GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+        gobject_class = (GObjectClass *) klass;
+	object_class = (GtkObjectClass *) klass;
+	item_class = (GnomeCanvasItemClass *) klass;
+	parent_class = g_type_class_ref (GNOME_TYPE_CANVAS_GROUP);
+
+	object_class->destroy	    = gnome_canvas_clipgroup_destroy;
+	gobject_class->set_property = gnome_canvas_clipgroup_set_property;
+	gobject_class->get_property = gnome_canvas_clipgroup_get_property;
+	item_class->update	    = gnome_canvas_clipgroup_update;
+	item_class->render	    = gnome_canvas_clipgroup_render;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_PATH,
+                                         g_param_spec_pointer ("path", NULL, NULL,
+                                                               (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_WIND,
+                                         g_param_spec_uint ("wind", NULL, NULL,
+                                                            0, G_MAXUINT, 0,
+                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+}
+
+static void
+gnome_canvas_clipgroup_init (GnomeCanvasClipgroup *clipgroup)
+{
+	clipgroup->path = NULL;
+	clipgroup->wind = ART_WIND_RULE_NONZERO; /* default winding rule */
+	clipgroup->svp = NULL;
+}
+
+static void
+gnome_canvas_clipgroup_destroy (GtkObject *object)
+{
+	GnomeCanvasClipgroup *clipgroup;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_CLIPGROUP (object));
+
+	clipgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+	if (clipgroup->path) {
+		gnome_canvas_path_def_unref (clipgroup->path);
+		clipgroup->path = NULL;
+	}
+	
+	if (clipgroup->svp) {
+		art_svp_free (clipgroup->svp);
+		clipgroup->svp = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+static void
+gnome_canvas_clipgroup_set_property (GObject      *object,
+                                     guint         param_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasClipgroup *cgroup;
+	GnomeCanvasPathDef *gpp;
+
+	item = GNOME_CANVAS_ITEM (object);
+	cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+	switch (param_id) {
+	case PROP_PATH:
+		gpp = g_value_get_pointer (value);
+
+		if (cgroup->path) {
+			gnome_canvas_path_def_unref (cgroup->path);
+			cgroup->path = NULL;
+		}
+		if (gpp != NULL) {
+			cgroup->path = gnome_canvas_path_def_closed_parts (gpp);
+		}
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIND:
+		cgroup->wind = g_value_get_uint (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void
+gnome_canvas_clipgroup_get_property (GObject    *object,
+                                     guint       param_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+	GnomeCanvasClipgroup * cgroup;
+
+	cgroup = GNOME_CANVAS_CLIPGROUP (object);
+
+	switch (param_id) {
+	case PROP_PATH:
+		g_value_set_pointer (value, cgroup->path);
+		break;
+
+	case PROP_WIND:
+		g_value_set_uint (value, cgroup->wind);
+		break;
+
+	default:
+	        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_clipgroup_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasClipgroup *clipgroup;
+	ArtSvpWriter *swr;
+	ArtBpath *bp;
+	ArtBpath *bpath;
+	ArtVpath *vpath;
+	ArtSVP *svp, *svp1, *svp2;
+
+	clipgroup = GNOME_CANVAS_CLIPGROUP (item);
+
+	if (clipgroup->svp) {
+		art_svp_free (clipgroup->svp);
+		clipgroup->svp = NULL;
+	}
+
+	if (clipgroup->path) {
+		bp = gnome_canvas_path_def_bpath (clipgroup->path);
+		bpath = art_bpath_affine_transform (bp, affine);
+
+		vpath = art_bez_path_to_vec (bpath, 0.25);
+		art_free (bpath);
+
+		svp1 = art_svp_from_vpath (vpath);
+		art_free (vpath);
+		
+		swr = art_svp_writer_rewind_new (clipgroup->wind);
+		art_svp_intersector (svp1, swr);
+
+		svp2 = art_svp_writer_rewind_reap (swr);
+		art_svp_free (svp1);
+		
+		if (clip_path != NULL) {
+			svp = art_svp_intersect (svp2, clip_path);
+			art_svp_free (svp2);
+		} else {
+			svp = svp2;
+		}
+
+		clipgroup->svp = svp;
+	}
+
+	if (GNOME_CANVAS_ITEM_CLASS (parent_class)->update)
+		(GNOME_CANVAS_ITEM_CLASS (parent_class)->update) (item, affine, NULL, flags);
+
+	if (clipgroup->svp) {
+		ArtDRect cbox;
+		art_drect_svp (&cbox, clipgroup->svp);
+		item->x1 = MAX (item->x1, cbox.x0 - 1.0);
+		item->y1 = MAX (item->y1, cbox.y0 - 1.0);
+		item->x2 = MIN (item->x2, cbox.x1 + 1.0);
+		item->y2 = MIN (item->y2, cbox.y1 + 1.0);
+	}
+}
+
+/* non-premultiplied composition into RGB */
+
+#define COMPOSEN11(fc,fa,bc) (((255 - (guint) (fa)) * (guint) (bc) + (guint) (fc) * (guint) (fa) + 127) / 255)
+
+static void
+gnome_canvas_clipgroup_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+	GnomeCanvasClipgroup *cg;
+	GnomeCanvasBuf lbuf;
+	guchar *mask;
+
+	cg = GNOME_CANVAS_CLIPGROUP (item);
+
+	if (cg->svp) {
+		gint bw, bh, sw, sh;
+		gint x, y;
+
+		/* fixme: We could optimize background handling (lauris) */
+
+		if (buf->is_bg) {
+			gnome_canvas_buf_ensure_buf (buf);
+			buf->is_bg = FALSE;
+			buf->is_buf = TRUE;
+		}
+
+		bw = buf->rect.x1 - buf->rect.x0;
+		bh = buf->rect.y1 - buf->rect.y0;
+		if ((bw < 1) || (bh < 1)) return;
+
+		if (bw * bh <= GCG_BUF_PIXELS) {
+			/* We can go with single buffer */
+			sw = bw;
+			sh = bh;
+		} else if (bw <= (GCG_BUF_PIXELS >> 3)) {
+			/* Go with row buffer */
+			sw = bw;
+			sh =  GCG_BUF_PIXELS / bw;
+		} else if (bh <= (GCG_BUF_PIXELS >> 3)) {
+			/* Go with column buffer */
+			sw = GCG_BUF_PIXELS / bh;
+			sh = bh;
+		} else {
+			/* Tile buffer */
+			sw = GCG_BUF_WIDTH;
+			sh = GCG_BUF_HEIGHT;
+		}
+
+		/* Set up local buffer */
+		lbuf.buf = gcg_buf_new ();
+		lbuf.bg_color = buf->bg_color;
+		lbuf.is_bg = FALSE;
+		lbuf.is_buf = TRUE;
+		/* Allocate mask */
+		mask = gcg_mask_new ();
+
+		for (y = buf->rect.y0; y < buf->rect.y1; y += sh) {
+			for (x = buf->rect.x0; x < buf->rect.x1; x += sw) {
+				gint r, xx, yy;
+				/* Set up local buffer */
+				lbuf.rect.x0 = x;
+				lbuf.rect.y0 = y;
+				lbuf.rect.x1 = MIN (x + sw, buf->rect.x1);
+				lbuf.rect.y1 = MIN (y + sh, buf->rect.y1);
+				lbuf.buf_rowstride = 3 * (lbuf.rect.x1 - lbuf.rect.x0);
+				/* Copy background */
+				for (r = lbuf.rect.y0; r < lbuf.rect.y1; r++) {
+					memcpy (lbuf.buf + (r - lbuf.rect.y0) * lbuf.buf_rowstride,
+						buf->buf + (r - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3,
+						(lbuf.rect.x1 - lbuf.rect.x0) * 3);
+				}
+				/* Invoke render method */
+				if (((GnomeCanvasItemClass *) parent_class)->render)
+					((GnomeCanvasItemClass *) parent_class)->render (item, &lbuf);
+				/* Render mask */
+				art_gray_svp_aa (cg->svp, lbuf.rect.x0, lbuf.rect.y0, lbuf.rect.x1, lbuf.rect.y1,
+						 mask, lbuf.rect.x1 - lbuf.rect.x0);
+				/* Combine */
+				for (yy = lbuf.rect.y0; yy < lbuf.rect.y1; yy++) {
+					guchar *s, *m, *d;
+					s = lbuf.buf + (yy - lbuf.rect.y0) * lbuf.buf_rowstride;
+					m = mask + (yy - lbuf.rect.y0) * (lbuf.rect.x1 - lbuf.rect.x0);
+					d = buf->buf + (yy - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
+					for (xx = lbuf.rect.x0; xx < lbuf.rect.x1; xx++) {
+#ifndef SHOW_SHADOW
+						d[0] = COMPOSEN11 (s[0], m[0], d[0]);
+						d[1] = COMPOSEN11 (s[1], m[0], d[1]);
+						d[2] = COMPOSEN11 (s[2], m[0], d[2]);
+#else
+						d[0] = COMPOSEN11 (s[0], m[0] | 0x7f, d[0]);
+						d[1] = COMPOSEN11 (s[1], m[0] | 0x7f, d[1]);
+						d[2] = COMPOSEN11 (s[2], m[0] | 0x7f, d[2]);
+#endif
+						s += 3;
+						m += 1;
+						d += 3;
+					}
+				}
+			}
+		}
+		/* Free buffers */
+		gcg_mask_free (mask);
+		gcg_buf_free (lbuf.buf);
+	} else {
+		if (((GnomeCanvasItemClass *) parent_class)->render)
+			((GnomeCanvasItemClass *) parent_class)->render (item, buf);
+	}
+}
+
+static GSList *gcg_buffers = NULL;
+static GSList *gcg_masks = NULL;
+
+static guchar *
+gcg_buf_new (void)
+{
+	guchar *buf;
+
+	if (!gcg_buffers) {
+		buf = g_new (guchar, GCG_BUF_SIZE);
+	} else {
+		buf = (guchar *) gcg_buffers->data;
+		gcg_buffers = g_slist_remove (gcg_buffers, buf);
+	}
+
+	return buf;
+}
+
+static void
+gcg_buf_free (guchar *buf)
+{
+	gcg_buffers = g_slist_prepend (gcg_buffers, buf);
+}
+
+static guchar *
+gcg_mask_new (void)
+{
+	guchar *mask;
+
+	if (!gcg_masks) {
+		mask = g_new (guchar, GCG_BUF_PIXELS);
+	} else {
+		mask = (guchar *) gcg_masks->data;
+		gcg_masks = g_slist_remove (gcg_masks, mask);
+	}
+
+	return mask;
+}
+
+static void
+gcg_mask_free (guchar *mask)
+{
+	gcg_masks = g_slist_prepend (gcg_masks, mask);
+}
diff --git a/src/libgnomecanvas/gnome-canvas-clipgroup.h b/src/libgnomecanvas/gnome-canvas-clipgroup.h
new file mode 100644
index 0000000..269ad3f
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-clipgroup.h
@@ -0,0 +1,58 @@
+#ifndef GNOME_CANVAS_CLIPGROUP_H
+#define GNOME_CANVAS_CLIPGROUP_H
+
+/* Clipping group implementation for GnomeCanvas
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * TODO: Implement this in libgnomeui, possibly merge with real group
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Author:
+ *          Lauris Kaplinski <lauris ximian com>
+ */
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+#define GNOME_TYPE_CANVAS_CLIPGROUP            (gnome_canvas_clipgroup_get_type ())
+#define GNOME_CANVAS_CLIPGROUP(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_CLIPGROUP, GnomeCanvasClipgroup))
+#define GNOME_CANVAS_CLIPGROUP_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_CLIPGROUP, GnomeCanvasClipgroupClass))
+#define GNOME_IS_CANVAS_CLIPGROUP(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_CLIPGROUP))
+#define GNOME_IS_CANVAS_CLIPGROUP_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_CLIPGROUP))
+
+
+typedef struct _GnomeCanvasClipgroup GnomeCanvasClipgroup;
+typedef struct _GnomeCanvasClipgroupClass GnomeCanvasClipgroupClass;
+
+struct _GnomeCanvasClipgroup {
+	GnomeCanvasGroup group;
+
+	GnomeCanvasPathDef * path;
+	ArtWindRule wind;
+
+	ArtSVP * svp;
+};
+
+struct _GnomeCanvasClipgroupClass {
+	GnomeCanvasGroupClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_clipgroup_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-i18n.h b/src/libgnomecanvas/gnome-canvas-i18n.h
new file mode 100644
index 0000000..a768438
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-i18n.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/*
+ * Handles all of the internationalization configuration options.
+ * Author: Tom Tromey <tromey creche cygnus com>
+ */
+
+#ifndef __LIBGNOME_CANVAS_I18N_H__
+#define __LIBGNOME_CANVAS_I18N_H__ 
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !defined(__LIBGNOME_CANVAS_I18NP_H__)
+
+#ifdef ENABLE_NLS
+#    include <libintl.h>
+#    ifdef GNOME_EXPLICIT_TRANSLATION_DOMAIN
+#        undef _
+#        define _(String) dgettext (GNOME_EXPLICIT_TRANSLATION_DOMAIN, String)
+#    else 
+#        define _(String) gettext (String)
+#    endif
+#    ifdef gettext_noop
+#        define N_(String) gettext_noop (String)
+#    else
+#        define N_(String) (String)
+#    endif
+#else
+/* Stubs that do something close enough.  */
+#    define textdomain(String) (String)
+#    define gettext(String) (String)
+#    define dgettext(Domain,Message) (Message)
+#    define dcgettext(Domain,Message,Type) (Message)
+#    define bindtextdomain(Domain,Directory) (Domain)
+#    define _(String) (String)
+#    define N_(String) (String)
+#endif
+
+#endif
+
+G_END_DECLS
+
+#endif /* __LIBGNOME_CANVAS_I18N_H__ */
diff --git a/src/libgnomecanvas/gnome-canvas-line.c b/src/libgnomecanvas/gnome-canvas-line.c
new file mode 100644
index 0000000..8546c44
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-line.c
@@ -0,0 +1,1422 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/* Line/curve item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "libart_lgpl/art_vpath.h"
+#include "libart_lgpl/art_svp.h"
+#include "libart_lgpl/art_svp_vpath.h"
+#include "libart_lgpl/art_svp_vpath_stroke.h"
+#include "libgnomecanvas.h"
+
+#define noVERBOSE
+
+#define DEFAULT_SPLINE_STEPS 12		/* this is what Tk uses */
+#define NUM_ARROW_POINTS     6		/* number of points in an arrowhead */
+#define NUM_STATIC_POINTS    256	/* number of static points to use to avoid allocating arrays */
+
+
+#define GROW_BOUNDS(bx1, by1, bx2, by2, x, y) {	\
+	if (x < bx1)				\
+		bx1 = x;			\
+						\
+	if (x > bx2)				\
+		bx2 = x;			\
+						\
+	if (y < by1)				\
+		by1 = y;			\
+						\
+	if (y > by2)				\
+		by2 = y;			\
+}
+
+
+enum {
+	PROP_0,
+	PROP_POINTS,
+	PROP_FILL_COLOR,
+	PROP_FILL_COLOR_GDK,
+	PROP_FILL_COLOR_RGBA,
+	PROP_FILL_STIPPLE,
+	PROP_WIDTH_PIXELS,
+	PROP_WIDTH_UNITS,
+	PROP_CAP_STYLE,
+	PROP_JOIN_STYLE,
+	PROP_LINE_STYLE,
+	PROP_FIRST_ARROWHEAD,
+	PROP_LAST_ARROWHEAD,
+	PROP_SMOOTH,
+	PROP_SPLINE_STEPS,
+	PROP_ARROW_SHAPE_A,
+	PROP_ARROW_SHAPE_B,
+	PROP_ARROW_SHAPE_C
+};
+
+
+static void gnome_canvas_line_class_init   (GnomeCanvasLineClass *class);
+static void gnome_canvas_line_init         (GnomeCanvasLine      *line);
+static void gnome_canvas_line_destroy      (GtkObject            *object);
+static void gnome_canvas_line_set_property (GObject              *object,
+					    guint                 param_id,
+					    const GValue         *value,
+					    GParamSpec           *pspec);
+static void gnome_canvas_line_get_property (GObject              *object,
+					    guint                 param_id,
+					    GValue               *value,
+					    GParamSpec           *pspec);
+
+static void   gnome_canvas_line_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void   gnome_canvas_line_realize     (GnomeCanvasItem *item);
+static void   gnome_canvas_line_unrealize   (GnomeCanvasItem *item);
+static void   gnome_canvas_line_draw        (GnomeCanvasItem *item, GdkDrawable *drawable,
+					     int x, int y, int width, int height);
+static double gnome_canvas_line_point       (GnomeCanvasItem *item, double x, double y,
+					     int cx, int cy, GnomeCanvasItem **actual_item);
+static void   gnome_canvas_line_bounds      (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+static void   gnome_canvas_line_render      (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+
+static GnomeCanvasItemClass *parent_class;
+
+
+GType
+gnome_canvas_line_get_type (void)
+{
+	static GType line_type;
+
+	if (!line_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasLineClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_line_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasLine),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_line_init,
+			NULL			/* value_table */
+		};
+
+		line_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasLine",
+						    &object_info, 0);
+	}
+
+	return line_type;
+}
+
+static void
+gnome_canvas_line_class_init (GnomeCanvasLineClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_line_set_property;
+	gobject_class->get_property = gnome_canvas_line_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_POINTS,
+                 g_param_spec_boxed ("points", NULL, NULL,
+				     GNOME_TYPE_CANVAS_POINTS,
+				     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_string ("fill_color", NULL, NULL,
+                                      NULL,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_GDK,
+                 g_param_spec_boxed ("fill_color_gdk", NULL, NULL,
+				     GDK_TYPE_COLOR,
+				     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_RGBA,
+                 g_param_spec_uint ("fill_color_rgba", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_STIPPLE,
+                 g_param_spec_object ("fill_stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_PIXELS,
+                 g_param_spec_uint ("width_pixels", NULL, NULL,
+				    0, G_MAXUINT, 0,
+				    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_UNITS,
+                 g_param_spec_double ("width_units", NULL, NULL,
+				      0.0, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CAP_STYLE,
+                 g_param_spec_enum ("cap_style", NULL, NULL,
+                                    GDK_TYPE_CAP_STYLE,
+                                    GDK_CAP_BUTT,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_JOIN_STYLE,
+                 g_param_spec_enum ("join_style", NULL, NULL,
+                                    GDK_TYPE_JOIN_STYLE,
+                                    GDK_JOIN_MITER,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_LINE_STYLE,
+                 g_param_spec_enum ("line_style", NULL, NULL,
+                                    GDK_TYPE_LINE_STYLE,
+                                    GDK_LINE_SOLID,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FIRST_ARROWHEAD,
+                 g_param_spec_boolean ("first_arrowhead", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_LAST_ARROWHEAD,
+                 g_param_spec_boolean ("last_arrowhead", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_SMOOTH,
+                 g_param_spec_boolean ("smooth", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_SPLINE_STEPS,
+                 g_param_spec_uint ("spline_steps", NULL, NULL,
+				    0, G_MAXUINT, DEFAULT_SPLINE_STEPS,
+				    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ARROW_SHAPE_A,
+                 g_param_spec_double ("arrow_shape_a", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ARROW_SHAPE_B,
+                 g_param_spec_double ("arrow_shape_b", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ARROW_SHAPE_C,
+                 g_param_spec_double ("arrow_shape_c", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_line_destroy;
+
+	item_class->update = gnome_canvas_line_update;
+	item_class->realize = gnome_canvas_line_realize;
+	item_class->unrealize = gnome_canvas_line_unrealize;
+	item_class->draw = gnome_canvas_line_draw;
+	item_class->point = gnome_canvas_line_point;
+	item_class->bounds = gnome_canvas_line_bounds;
+
+	item_class->render = gnome_canvas_line_render;
+}
+
+static void
+gnome_canvas_line_init (GnomeCanvasLine *line)
+{
+	line->width = 0.0;
+	line->cap = GDK_CAP_BUTT;
+	line->join = GDK_JOIN_MITER;
+	line->line_style = GDK_LINE_SOLID;
+	line->shape_a = 0.0;
+	line->shape_b = 0.0;
+	line->shape_c = 0.0;
+	line->spline_steps = DEFAULT_SPLINE_STEPS;
+}
+
+static void
+gnome_canvas_line_destroy (GtkObject *object)
+{
+	GnomeCanvasLine *line;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+	line = GNOME_CANVAS_LINE (object);
+
+	/* remember, destroy can be run multiple times! */
+
+	if (line->coords)
+		g_free (line->coords);
+	line->coords = NULL;
+
+	if (line->first_coords)
+		g_free (line->first_coords);
+	line->first_coords = NULL;
+
+	if (line->last_coords)
+		g_free (line->last_coords);
+	line->last_coords = NULL;
+
+	if (line->stipple)
+		gdk_bitmap_unref (line->stipple);
+	line->stipple = NULL;
+
+	if (line->fill_svp)
+		art_svp_free (line->fill_svp);
+	line->fill_svp = NULL;
+
+	if (line->first_svp)
+		art_svp_free (line->first_svp);
+	line->first_svp = NULL;
+
+	if (line->last_svp)
+		art_svp_free (line->last_svp);
+	line->last_svp = NULL;
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/* Computes the bounding box of the line, including its arrow points.  Assumes that the number of
+ * points in the line is not zero.
+ */
+static void
+get_bounds (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2)
+{
+	double *coords;
+	double x1, y1, x2, y2;
+	double width;
+	int i;
+
+	if (!line->coords) {
+	    *bx1 = *by1 = *bx2 = *by2 = 0.0;
+	    return;
+	}
+	
+	/* Find bounding box of line's points */
+
+	x1 = x2 = line->coords[0];
+	y1 = y2 = line->coords[1];
+
+	for (i = 1, coords = line->coords + 2; i < line->num_points; i++, coords += 2)
+		GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+	/* Add possible over-estimate for wide lines */
+
+	if (line->width_pixels)
+		width = line->width / line->item.canvas->pixels_per_unit;
+	else
+		width = line->width;
+
+	x1 -= width;
+	y1 -= width;
+	x2 += width;
+	y2 += width;
+
+	/* For mitered lines, make a second pass through all the points.  Compute the location of
+	 * the two miter vertex points and add them to the bounding box.
+	 */
+
+	if (line->join == GDK_JOIN_MITER)
+		for (i = line->num_points, coords = line->coords; i >= 3; i--, coords += 2) {
+			double mx1, my1, mx2, my2;
+
+			if (gnome_canvas_get_miter_points (coords[0], coords[1],
+							   coords[2], coords[3],
+							   coords[4], coords[5],
+							   width,
+							   &mx1, &my1, &mx2, &my2)) {
+				GROW_BOUNDS (x1, y1, x2, y2, mx1, my1);
+				GROW_BOUNDS (x1, y1, x2, y2, mx2, my2);
+			}
+		}
+
+	/* Add the arrow points, if any */
+
+	if (line->first_arrow && line->first_coords)
+		for (i = 0, coords = line->first_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+			GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+	if (line->last_arrow && line->last_coords)
+		for (i = 0, coords = line->last_coords; i < NUM_ARROW_POINTS; i++, coords += 2)
+			GROW_BOUNDS (x1, y1, x2, y2, coords[0], coords[1]);
+
+	/* Done */
+
+	*bx1 = x1;
+	*by1 = y1;
+	*bx2 = x2;
+	*by2 = y2;
+}
+
+/* Computes the bounding box of the line, in canvas coordinates.  Assumes that the number of points in the polygon is
+ * not zero. Affine is the i2c transformation.
+ */
+static void
+get_bounds_canvas (GnomeCanvasLine *line, double *bx1, double *by1, double *bx2, double *by2, double affine[6])
+{
+	/* It would be possible to tighten the bounds somewhat by transforming the individual points before
+	   aggregating them into the bbox. But it hardly seems worth it. */
+	ArtDRect bbox_world;
+	ArtDRect bbox_canvas;
+
+	get_bounds (line, &bbox_world.x0, &bbox_world.y0, &bbox_world.x1, &bbox_world.y1);
+
+	art_drect_affine_transform (&bbox_canvas, &bbox_world, affine);
+	/* include 1 pixel of fudge */
+	*bx1 = bbox_canvas.x0 - 1;
+	*by1 = bbox_canvas.y0 - 1;
+	*bx2 = bbox_canvas.x1 + 1;
+	*by2 = bbox_canvas.y1 + 1;
+}
+
+/* Recalculates the arrow polygons for the line */
+static void
+reconfigure_arrows (GnomeCanvasLine *line)
+{
+	double *poly, *coords;
+	double dx, dy, length;
+	double sin_theta, cos_theta, tmp;
+	double frac_height;	/* Line width as fraction of arrowhead width */
+	double backup;		/* Distance to backup end points so the line ends in the middle of the arrowhead */
+	double vx, vy;		/* Position of arrowhead vertex */
+	double shape_a, shape_b, shape_c;
+	double width;
+	int i;
+
+	if (line->num_points == 0)
+		return;
+
+	/* Set up things */
+
+	if (line->first_arrow) {
+		if (line->first_coords) {
+			line->coords[0] = line->first_coords[0];
+			line->coords[1] = line->first_coords[1];
+		} else
+			line->first_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+	} else if (line->first_coords) {
+		line->coords[0] = line->first_coords[0];
+		line->coords[1] = line->first_coords[1];
+
+		g_free (line->first_coords);
+		line->first_coords = NULL;
+	}
+
+	i = 2 * (line->num_points - 1);
+
+	if (line->last_arrow) {
+		if (line->last_coords) {
+			line->coords[i] = line->last_coords[0];
+			line->coords[i + 1] = line->last_coords[1];
+		} else
+			line->last_coords = g_new (double, 2 * NUM_ARROW_POINTS);
+	} else if (line->last_coords) {
+		line->coords[i] = line->last_coords[0];
+		line->coords[i + 1] = line->last_coords[1];
+
+		g_free (line->last_coords);
+		line->last_coords = NULL;
+	}
+
+	if (!line->first_arrow && !line->last_arrow)
+		return;
+
+	if (line->width_pixels)
+		width = line->width / line->item.canvas->pixels_per_unit;
+	else
+		width = line->width;
+
+	/* Add fudge value for better-looking results */
+
+	shape_a = line->shape_a;
+	shape_b = line->shape_b;
+	shape_c = line->shape_c + width / 2.0;
+
+	if (line->width_pixels) {
+		shape_a /= line->item.canvas->pixels_per_unit;
+		shape_b /= line->item.canvas->pixels_per_unit;
+		shape_c /= line->item.canvas->pixels_per_unit;
+	}
+
+	shape_a += 0.001;
+	shape_b += 0.001;
+	shape_c += 0.001;
+
+	/* Compute the polygon for the first arrowhead and adjust the first point in the line so
+	 * that the line does not stick out past the leading edge of the arrowhead.
+	 */
+
+	frac_height = (line->width / 2.0) / shape_c;
+	backup = frac_height * shape_b + shape_a * (1.0 - frac_height) / 2.0;
+
+	if (line->first_arrow) {
+		poly = line->first_coords;
+		poly[0] = poly[10] = line->coords[0];
+		poly[1] = poly[11] = line->coords[1];
+
+		dx = poly[0] - line->coords[2];
+		dy = poly[1] - line->coords[3];
+		length = sqrt (dx * dx + dy * dy);
+		if (length < GNOME_CANVAS_EPSILON)
+			sin_theta = cos_theta = 0.0;
+		else {
+			sin_theta = dy / length;
+			cos_theta = dx / length;
+		}
+
+		vx = poly[0] - shape_a * cos_theta;
+		vy = poly[1] - shape_a * sin_theta;
+
+		tmp = shape_c * sin_theta;
+
+		poly[2] = poly[0] - shape_b * cos_theta + tmp;
+		poly[8] = poly[2] - 2.0 * tmp;
+
+		tmp = shape_c * cos_theta;
+
+		poly[3] = poly[1] - shape_b * sin_theta - tmp;
+		poly[9] = poly[3] + 2.0 * tmp;
+
+		poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+		poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+		poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+		poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+		/* Move the first point towards the second so that the corners at the end of the
+		 * line are inside the arrowhead.
+		 */
+
+		line->coords[0] = poly[0] - backup * cos_theta;
+		line->coords[1] = poly[1] - backup * sin_theta;
+	}
+
+	/* Same process for last arrowhead */
+
+	if (line->last_arrow) {
+		coords = line->coords + 2 * (line->num_points - 2);
+		poly = line->last_coords;
+		poly[0] = poly[10] = coords[2];
+		poly[1] = poly[11] = coords[3];
+
+		dx = poly[0] - coords[0];
+		dy = poly[1] - coords[1];
+		length = sqrt (dx * dx + dy * dy);
+		if (length < GNOME_CANVAS_EPSILON)
+			sin_theta = cos_theta = 0.0;
+		else {
+			sin_theta = dy / length;
+			cos_theta = dx / length;
+		}
+
+		vx = poly[0] - shape_a * cos_theta;
+		vy = poly[1] - shape_a * sin_theta;
+
+		tmp = shape_c * sin_theta;
+
+		poly[2] = poly[0] - shape_b * cos_theta + tmp;
+		poly[8] = poly[2] - 2.0 * tmp;
+
+		tmp = shape_c * cos_theta;
+
+		poly[3] = poly[1] - shape_b * sin_theta - tmp;
+		poly[9] = poly[3] + 2.0 * tmp;
+
+		poly[4] = poly[2] * frac_height + vx * (1.0 - frac_height);
+		poly[5] = poly[3] * frac_height + vy * (1.0 - frac_height);
+		poly[6] = poly[8] * frac_height + vx * (1.0 - frac_height);
+		poly[7] = poly[9] * frac_height + vy * (1.0 - frac_height);
+
+		coords[2] = poly[0] - backup * cos_theta;
+		coords[3] = poly[1] - backup * sin_theta;
+	}
+}
+
+/* Convenience function to set the line's GC's foreground color */
+static void
+set_line_gc_foreground (GnomeCanvasLine *line)
+{
+	GdkColor c;
+
+	if (!line->gc)
+		return;
+
+	c.pixel = line->fill_pixel;
+	gdk_gc_set_foreground (line->gc, &c);
+}
+
+/* Recalculate the line's width and set it in its GC */
+static void
+set_line_gc_width (GnomeCanvasLine *line)
+{
+	int width;
+
+	if (!line->gc)
+		return;
+
+	if (line->width_pixels)
+		width = (int) line->width;
+	else
+		width = (int) (line->width * line->item.canvas->pixels_per_unit + 0.5);
+
+	gdk_gc_set_line_attributes (line->gc,
+				    width,
+				    line->line_style,
+				    (line->first_arrow || line->last_arrow) ? GDK_CAP_BUTT : line->cap,
+				    line->join);
+}
+
+/* Sets the stipple pattern for the line */
+static void
+set_stipple (GnomeCanvasLine *line, GdkBitmap *stipple, int reconfigure)
+{
+	if (line->stipple && !reconfigure)
+		gdk_bitmap_unref (line->stipple);
+
+	line->stipple = stipple;
+	if (stipple && !reconfigure)
+		gdk_bitmap_ref (stipple);
+
+	if (line->gc) {
+		if (stipple) {
+			gdk_gc_set_stipple (line->gc, stipple);
+			gdk_gc_set_fill (line->gc, GDK_STIPPLED);
+		} else
+			gdk_gc_set_fill (line->gc, GDK_SOLID);
+	}
+}
+
+static void
+gnome_canvas_line_set_property (GObject              *object,
+				guint                 param_id,
+				const GValue         *value,
+				GParamSpec           *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasLine *line;
+	GnomeCanvasPoints *points;
+	GdkColor color = { 0, 0, 0, 0, };
+	GdkColor *pcolor;
+	gboolean color_changed;
+	int have_pixel;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	line = GNOME_CANVAS_LINE (object);
+
+	color_changed = FALSE;
+	have_pixel = FALSE;
+
+	switch (param_id) {
+	case PROP_POINTS:
+		points = g_value_get_boxed (value);
+
+		if (line->coords) {
+			g_free (line->coords);
+			line->coords = NULL;
+		}
+
+		if (!points)
+			line->num_points = 0;
+		else {
+			line->num_points = points->num_points;
+			line->coords = g_new (double, 2 * line->num_points);
+			memcpy (line->coords, points->coords, 2 * line->num_points * sizeof (double));
+		}
+
+		/* Drop the arrowhead polygons if they exist -- they will be regenerated */
+
+		if (line->first_coords) {
+			g_free (line->first_coords);
+			line->first_coords = NULL;
+		}
+
+		if (line->last_coords) {
+			g_free (line->last_coords);
+			line->last_coords = NULL;
+		}
+
+		/* Since the line's points have changed, we need to re-generate arrowheads in
+		 * addition to recalculating the bounds.
+		 */
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_FILL_COLOR:
+		if (g_value_get_string (value))
+			gdk_color_parse (g_value_get_string (value), &color);
+		line->fill_rgba = ((color.red & 0xff00) << 16 |
+				   (color.green & 0xff00) << 8 |
+				   (color.blue & 0xff00) |
+				   0xff);
+		color_changed = TRUE;
+		break;
+
+	case PROP_FILL_COLOR_GDK:
+		pcolor = g_value_get_boxed (value);
+		if (pcolor) {
+			GdkColormap *colormap;
+			color = *pcolor;
+
+			colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+			gdk_rgb_find_color (colormap, &color);
+
+			have_pixel = TRUE;
+		}
+
+		line->fill_rgba = ((color.red & 0xff00) << 16 |
+				   (color.green & 0xff00) << 8 |
+				   (color.blue & 0xff00) |
+				   0xff);
+		color_changed = TRUE;
+		break;
+
+	case PROP_FILL_COLOR_RGBA:
+		line->fill_rgba = g_value_get_uint (value);
+		color_changed = TRUE;
+		break;
+
+	case PROP_FILL_STIPPLE:
+		set_stipple (line, (GdkBitmap *) g_value_get_object (value), FALSE);
+		gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		line->width = g_value_get_uint (value);
+		line->width_pixels = TRUE;
+		set_line_gc_width (line);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_UNITS:
+		line->width = fabs (g_value_get_double (value));
+		line->width_pixels = FALSE;
+		set_line_gc_width (line);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_CAP_STYLE:
+		line->cap = g_value_get_enum (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_JOIN_STYLE:
+		line->join = g_value_get_enum (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_LINE_STYLE:
+		line->line_style = g_value_get_enum (value);
+		set_line_gc_width (line);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_FIRST_ARROWHEAD:
+		line->first_arrow = g_value_get_boolean (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_LAST_ARROWHEAD:
+		line->last_arrow = g_value_get_boolean (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_SMOOTH:
+		/* FIXME */
+		break;
+
+	case PROP_SPLINE_STEPS:
+		/* FIXME */
+		break;
+
+	case PROP_ARROW_SHAPE_A:
+		line->shape_a = fabs (g_value_get_double (value));
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_ARROW_SHAPE_B:
+		line->shape_b = fabs (g_value_get_double (value));
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_ARROW_SHAPE_C:
+		line->shape_c = fabs (g_value_get_double (value));
+		gnome_canvas_item_request_update (item);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+
+	if (color_changed) {
+		if (have_pixel)
+			line->fill_pixel = color.pixel;
+		else
+			line->fill_pixel = gnome_canvas_get_color_pixel (item->canvas,
+									 line->fill_rgba);
+
+		if (!item->canvas->aa)
+			set_line_gc_foreground (line);
+
+		gnome_canvas_item_request_redraw_svp (item, line->fill_svp);
+
+		if (line->first_svp) 
+			gnome_canvas_item_request_redraw_svp (item, line->first_svp);
+
+		if (line->last_svp) 
+			gnome_canvas_item_request_redraw_svp (item, line->last_svp);
+
+	}
+}
+
+/* Returns a copy of the line's points without the endpoint adjustments for
+ * arrowheads.
+ */
+static GnomeCanvasPoints *
+get_points (GnomeCanvasLine *line)
+{
+	GnomeCanvasPoints *points;
+	int start_ofs, end_ofs;
+
+	if (line->num_points == 0)
+		return NULL;
+
+	start_ofs = end_ofs = 0;
+
+	points = gnome_canvas_points_new (line->num_points);
+
+	/* Invariant:  if first_coords or last_coords exist, then the line's
+	 * endpoints have been adjusted.
+	 */
+
+	if (line->first_coords) {
+		start_ofs = 1;
+
+		points->coords[0] = line->first_coords[0];
+		points->coords[1] = line->first_coords[1];
+	}
+
+	if (line->last_coords) {
+		end_ofs = 1;
+
+		points->coords[2 * (line->num_points - 1)] = line->last_coords[0];
+		points->coords[2 * (line->num_points - 1) + 1] = line->last_coords[1];
+	}
+
+	memcpy (points->coords + 2 * start_ofs,
+		line->coords + 2 * start_ofs,
+		2 * (line->num_points - (start_ofs + end_ofs)) * sizeof (double));
+
+	return points;
+}
+
+static void
+gnome_canvas_line_get_property (GObject              *object,
+				guint                 param_id,
+				GValue               *value,
+				GParamSpec           *pspec)
+{
+	GnomeCanvasLine *line;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_LINE (object));
+
+	line = GNOME_CANVAS_LINE (object);
+
+	switch (param_id) {
+	case PROP_POINTS:
+		g_value_set_boxed (value, get_points (line));
+		break;
+
+	case PROP_FILL_COLOR:
+		g_value_set_string_take_ownership (value,
+						   g_strdup_printf ("#%02x%02x%02x",
+								    line->fill_rgba >> 24,
+								    (line->fill_rgba >> 16) & 0xff,
+								    (line->fill_rgba >> 8) & 0xff));
+		break;
+
+	case PROP_FILL_COLOR_GDK: {
+		GnomeCanvas *canvas = GNOME_CANVAS_ITEM (line)->canvas;
+		GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+		GdkColor color;
+
+		gdk_colormap_query_color (colormap, line->fill_pixel, &color);
+		g_value_set_boxed (value, &color);
+		break;
+	}
+
+	case PROP_FILL_COLOR_RGBA:
+		g_value_set_uint (value, line->fill_rgba);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		g_value_set_object (value, line->stipple);
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		g_value_set_uint (value, line->width);
+		break;
+		
+	case PROP_WIDTH_UNITS:
+		g_value_set_double (value, line->width);
+		break;
+		
+	case PROP_CAP_STYLE:
+		g_value_set_enum (value, line->cap);
+		break;
+
+	case PROP_JOIN_STYLE:
+		g_value_set_enum (value, line->join);
+		break;
+
+	case PROP_LINE_STYLE:
+		g_value_set_enum (value, line->line_style);
+		break;
+
+	case PROP_FIRST_ARROWHEAD:
+		g_value_set_boolean (value, line->first_arrow);
+		break;
+
+	case PROP_LAST_ARROWHEAD:
+		g_value_set_boolean (value, line->last_arrow);
+		break;
+
+	case PROP_SMOOTH:
+		g_value_set_boolean (value, line->smooth);
+		break;
+
+	case PROP_SPLINE_STEPS:
+		g_value_set_uint (value, line->spline_steps);
+		break;
+
+	case PROP_ARROW_SHAPE_A:
+		g_value_set_double (value, line->shape_a);
+		break;
+
+	case PROP_ARROW_SHAPE_B:
+		g_value_set_double (value, line->shape_b);
+		break;
+
+	case PROP_ARROW_SHAPE_C:
+		g_value_set_double (value, line->shape_c);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_line_render (GnomeCanvasItem *item,
+			     GnomeCanvasBuf *buf)
+{
+	GnomeCanvasLine *line;
+
+	line = GNOME_CANVAS_LINE (item);
+
+	if (line->fill_svp != NULL)
+		gnome_canvas_render_svp (buf, line->fill_svp, line->fill_rgba);
+
+	if (line->first_svp != NULL)
+		gnome_canvas_render_svp (buf, line->first_svp, line->fill_rgba);
+
+	if (line->last_svp != NULL)
+		gnome_canvas_render_svp (buf, line->last_svp, line->fill_rgba);
+}
+
+
+static ArtSVP *
+svp_from_points (const double *item_coords, int num_points, const double affine[6])
+{
+	ArtVpath *vpath;
+	ArtSVP *svp;
+	double x, y;
+	int i;
+
+	vpath = art_new (ArtVpath, num_points + 2);
+
+	for (i = 0; i < num_points; i++) {
+		vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
+		x = item_coords[i * 2];
+		y = item_coords[i * 2 + 1];
+		vpath[i].x = x * affine[0] + y * affine[2] + affine[4];
+		vpath[i].y = x * affine[1] + y * affine[3] + affine[5];
+	}
+#if 0
+	vpath[i].code = ART_LINETO;
+	vpath[i].x = vpath[0].x;
+	vpath[i].y = vpath[0].y;
+	i++;
+#endif
+	vpath[i].code = ART_END;
+	vpath[i].x = 0;
+	vpath[i].y = 0;
+
+	svp = art_svp_from_vpath (vpath);
+
+	art_free (vpath);
+
+	return svp;
+}
+
+static void
+gnome_canvas_line_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasLine *line;
+	int i;
+	ArtVpath *vpath;
+	ArtPoint pi, pc;
+	double width;
+	ArtSVP *svp;
+	double x1, y1, x2, y2;
+
+	line = GNOME_CANVAS_LINE (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, affine, clip_path, flags);
+
+	reconfigure_arrows (line);
+
+	if (item->canvas->aa) {
+		gnome_canvas_item_reset_bounds (item);
+
+		vpath = art_new (ArtVpath, line->num_points + 2);
+
+		for (i = 0; i < line->num_points; i++) {
+			pi.x = line->coords[i * 2];
+			pi.y = line->coords[i * 2 + 1];
+			art_affine_point (&pc, &pi, affine);
+			vpath[i].code = i == 0 ? ART_MOVETO : ART_LINETO;
+			vpath[i].x = pc.x;
+			vpath[i].y = pc.y;
+		}
+		vpath[i].code = ART_END;
+		vpath[i].x = 0;
+		vpath[i].y = 0;
+
+		if (line->width_pixels)
+			width = line->width;
+		else
+			width = line->width * art_affine_expansion (affine);
+
+		if (width < 0.5)
+			width = 0.5;
+
+		svp = art_svp_vpath_stroke (vpath,
+					    gnome_canvas_join_gdk_to_art (line->join),
+					    gnome_canvas_cap_gdk_to_art (line->cap),
+					    width,
+					    4,
+					    0.25);
+		art_free (vpath);
+
+		gnome_canvas_item_update_svp_clip (item, &line->fill_svp, svp, clip_path);
+
+		if (line->first_arrow && line->first_coords) {
+			svp = svp_from_points (line->first_coords, NUM_ARROW_POINTS, affine);
+                        gnome_canvas_item_update_svp_clip (item, 
+                                        &line->first_svp, svp, clip_path);
+                }
+
+
+		if (line->last_arrow && line->last_coords) {
+			svp = svp_from_points (line->last_coords, NUM_ARROW_POINTS, affine);
+                        gnome_canvas_item_update_svp_clip (item, 
+                                        &line->last_svp, svp, clip_path);
+                }
+
+
+	} else {
+		set_line_gc_foreground (line);
+		set_line_gc_width (line);
+		set_stipple (line, line->stipple, TRUE);
+
+		get_bounds_canvas (line, &x1, &y1, &x2, &y2, affine);
+		gnome_canvas_update_bbox (item, x1, y1, x2, y2);
+	}
+}
+
+static void
+gnome_canvas_line_realize (GnomeCanvasItem *item)
+{
+	GnomeCanvasLine *line;
+
+	line = GNOME_CANVAS_LINE (item);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (item);
+
+	line->gc = gdk_gc_new (item->canvas->layout.bin_window);
+
+#if 0
+	(* GNOME_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0);
+#endif
+}
+
+static void
+gnome_canvas_line_unrealize (GnomeCanvasItem *item)
+{
+	GnomeCanvasLine *line;
+
+	line = GNOME_CANVAS_LINE (item);
+
+	gdk_gc_unref (line->gc);
+	line->gc = NULL;
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (item);
+}
+
+static void
+item_to_canvas (GnomeCanvas *canvas, double *item_coords, GdkPoint *canvas_coords, int num_points,
+		int *num_drawn_points, double i2c[6], int x, int y)
+{
+	int i;
+	int old_cx, old_cy;
+	int cx, cy;
+	ArtPoint pi, pc;
+
+#ifdef VERBOSE
+	{
+		char str[128];
+		art_affine_to_string (str, i2c);
+		g_print ("line item_to_canvas %s\n", str);
+	}
+#endif
+
+	/* the first point is always drawn */
+
+	pi.x = item_coords[0];
+	pi.y = item_coords[1];
+	art_affine_point (&pc, &pi, i2c);
+	cx = floor (pc.x + 0.5);
+	cy = floor (pc.y + 0.5);
+	canvas_coords->x = cx - x;
+	canvas_coords->y = cy - y;
+	canvas_coords++;
+	old_cx = cx;
+	old_cy = cy;
+	*num_drawn_points = 1;
+
+	for (i = 1; i < num_points; i++) {
+		pi.x = item_coords[i * 2];
+		pi.y = item_coords[i * 2 + 1];
+		art_affine_point (&pc, &pi, i2c);
+		cx = floor (pc.x + 0.5);
+		cy = floor (pc.y + 0.5);
+		if (old_cx != cx || old_cy != cy) {
+			canvas_coords->x = cx - x;
+			canvas_coords->y = cy - y;
+			old_cx = cx;
+			old_cy = cy;
+			canvas_coords++;
+			(*num_drawn_points)++;
+		}
+	}
+}
+
+static void
+gnome_canvas_line_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+			int x, int y, int width, int height)
+{
+	GnomeCanvasLine *line;
+	GdkPoint static_points[NUM_STATIC_POINTS];
+	GdkPoint *points;
+	int actual_num_points_drawn;
+	double i2c[6];
+
+	line = GNOME_CANVAS_LINE (item);
+
+	if (line->num_points == 0)
+		return;
+
+	/* Build array of canvas pixel coordinates */
+
+	if (line->num_points <= NUM_STATIC_POINTS)
+		points = static_points;
+	else
+		points = g_new (GdkPoint, line->num_points);
+
+
+	gnome_canvas_item_i2c_affine (item, i2c);
+
+	item_to_canvas (item->canvas, line->coords, points, line->num_points,
+			&actual_num_points_drawn, i2c, x, y);
+
+	if (line->stipple)
+		gnome_canvas_set_stipple_origin (item->canvas, line->gc);
+
+	gdk_draw_lines (drawable, line->gc, points, actual_num_points_drawn);
+
+	if (points != static_points)
+		g_free (points);
+
+	/* Draw arrowheads */
+
+	points = static_points;
+
+	if (line->first_arrow) {
+		item_to_canvas (item->canvas, line->first_coords, points, NUM_ARROW_POINTS,
+				&actual_num_points_drawn, i2c, x, y);
+		gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+	}
+
+	if (line->last_arrow) {
+		item_to_canvas (item->canvas, line->last_coords, points, NUM_ARROW_POINTS,
+				&actual_num_points_drawn, i2c, x, y);
+		gdk_draw_polygon (drawable, line->gc, TRUE, points, actual_num_points_drawn );
+	}
+}
+
+static double
+gnome_canvas_line_point (GnomeCanvasItem *item, double x, double y,
+			 int cx, int cy, GnomeCanvasItem **actual_item)
+{
+	GnomeCanvasLine *line;
+	double *line_points = NULL, *coords;
+	double static_points[2 * NUM_STATIC_POINTS];
+	double poly[10];
+	double best, dist;
+	double dx, dy;
+	double width;
+	int num_points = 0, i;
+	int changed_miter_to_bevel;
+
+#ifdef VERBOSE
+	g_print ("gnome_canvas_line_point x, y = (%g, %g); cx, cy = (%d, %d)\n", x, y, cx, cy);
+#endif
+
+	line = GNOME_CANVAS_LINE (item);
+
+	*actual_item = item;
+
+	best = 1.0e36;
+
+	/* Handle smoothed lines by generating an expanded set ot points */
+
+	if (line->smooth && (line->num_points > 2)) {
+		/* FIXME */
+	} else {
+		num_points = line->num_points;
+		line_points = line->coords;
+	}
+
+	/* Compute a polygon for each edge of the line and test the point against it.  The effective
+	 * width of the line is adjusted so that it will be at least one pixel thick (so that zero
+	 * pixel-wide lines can be pickedup as well).
+	 */
+
+	if (line->width_pixels)
+		width = line->width / item->canvas->pixels_per_unit;
+	else
+		width = line->width;
+
+	if (width < (1.0 / item->canvas->pixels_per_unit))
+		width = 1.0 / item->canvas->pixels_per_unit;
+
+	changed_miter_to_bevel = 0;
+
+	for (i = num_points, coords = line_points; i >= 2; i--, coords += 2) {
+		/* If rounding is done around the first point, then compute distance between the
+		 * point and the first point.
+		 */
+
+		if (((line->cap == GDK_CAP_ROUND) && (i == num_points))
+		    || ((line->join == GDK_JOIN_ROUND) && (i != num_points))) {
+			dx = coords[0] - x;
+			dy = coords[1] - y;
+			dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+			if (dist < GNOME_CANVAS_EPSILON) {
+				best = 0.0;
+				goto done;
+			} else if (dist < best)
+				best = dist;
+		}
+
+		/* Compute the polygonal shape corresponding to this edge, with two points for the
+		 * first point of the edge and two points for the last point of the edge.
+		 */
+
+		if (i == num_points)
+			gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+						      width, (line->cap == GDK_CAP_PROJECTING),
+						      poly, poly + 1, poly + 2, poly + 3);
+		else if ((line->join == GDK_JOIN_MITER) && !changed_miter_to_bevel) {
+			poly[0] = poly[6];
+			poly[1] = poly[7];
+			poly[2] = poly[4];
+			poly[3] = poly[5];
+		} else {
+			gnome_canvas_get_butt_points (coords[2], coords[3], coords[0], coords[1],
+						      width, FALSE,
+						      poly, poly + 1, poly + 2, poly + 3);
+
+			/* If this line uses beveled joints, then check the distance to a polygon
+			 * comprising the last two points of the previous polygon and the first two
+			 * from this polygon; this checks the wedges that fill the mitered point.
+			 */
+
+			if ((line->join == GDK_JOIN_BEVEL) || changed_miter_to_bevel) {
+				poly[8] = poly[0];
+				poly[9] = poly[1];
+
+				dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
+				if (dist < GNOME_CANVAS_EPSILON) {
+					best = 0.0;
+					goto done;
+				} else if (dist < best)
+					best = dist;
+
+				changed_miter_to_bevel = FALSE;
+			}
+		}
+
+		if (i == 2)
+			gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+						      width, (line->cap == GDK_CAP_PROJECTING),
+						      poly + 4, poly + 5, poly + 6, poly + 7);
+		else if (line->join == GDK_JOIN_MITER) {
+			if (!gnome_canvas_get_miter_points (coords[0], coords[1],
+							    coords[2], coords[3],
+							    coords[4], coords[5],
+							    width,
+							    poly + 4, poly + 5, poly + 6, poly + 7)) {
+				changed_miter_to_bevel = TRUE;
+				gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+							      width, FALSE,
+							      poly + 4, poly + 5, poly + 6, poly + 7);
+			}
+		} else
+			gnome_canvas_get_butt_points (coords[0], coords[1], coords[2], coords[3],
+						      width, FALSE,
+						      poly + 4, poly + 5, poly + 6, poly + 7);
+
+		poly[8] = poly[0];
+		poly[9] = poly[1];
+
+		dist = gnome_canvas_polygon_to_point (poly, 5, x, y);
+		if (dist < GNOME_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else if (dist < best)
+			best = dist;
+	}
+
+	/* If caps are rounded, check the distance to the cap around the final end point of the line */
+
+	if (line->cap == GDK_CAP_ROUND) {
+		dx = coords[0] - x;
+		dy = coords[1] - y;
+		dist = sqrt (dx * dx + dy * dy) - width / 2.0;
+		if (dist < GNOME_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else
+			best = dist;
+	}
+
+	/* sometimes the GnomeCanvasItem::update signal will not have
+           been processed between deleting the arrow points and a call
+           to this routine -- this can cause a segfault here */
+	if ((line->first_arrow && !line->first_coords) ||
+	    (line->last_arrow && !line->last_coords))
+		reconfigure_arrows(line);
+
+	/* If there are arrowheads, check the distance to them */
+
+	if (line->first_arrow) {
+		dist = gnome_canvas_polygon_to_point (line->first_coords, NUM_ARROW_POINTS, x, y);
+		if (dist < GNOME_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else
+			best = dist;
+	}
+
+	if (line->last_arrow) {
+		dist = gnome_canvas_polygon_to_point (line->last_coords, NUM_ARROW_POINTS, x, y);
+		if (dist < GNOME_CANVAS_EPSILON) {
+			best = 0.0;
+			goto done;
+		} else
+			best = dist;
+	}
+
+done:
+
+	if ((line_points != static_points) && (line_points != line->coords))
+		g_free (line_points);
+
+	return best;
+}
+
+static void
+gnome_canvas_line_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	GnomeCanvasLine *line;
+
+	line = GNOME_CANVAS_LINE (item);
+
+	if (line->num_points == 0) {
+		*x1 = *y1 = *x2 = *y2 = 0.0;
+		return;
+	}
+
+	get_bounds (line, x1, y1, x2, y2);
+}
diff --git a/src/libgnomecanvas/gnome-canvas-line.h b/src/libgnomecanvas/gnome-canvas-line.h
new file mode 100644
index 0000000..29893ab
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-line.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+/* Line/curve item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef GNOME_CANVAS_LINE_H
+#define GNOME_CANVAS_LINE_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Line item for the canvas.  This is a polyline with configurable width, cap/join styles, and arrowheads.
+ * If arrowheads are enabled, then three values are used to specify their shape:
+ *
+ *	arrow_shape_a:  Distance from tip of arrowhead to the center point.
+ *	arrow_shape_b:  Distance from tip of arrowhead to trailing point, measured along the shaft.
+ *	arrow_shape_c:	Distance of trailing point from outside edge of shaft.
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * points		GnomeCanvasPoints*	RW		Pointer to a GnomeCanvasPoints structure.
+ *								This can be created by a call to
+ *								gnome_canvas_points_new() (in gnome-canvas-util.h).
+ *								X coordinates are in the even indices of the
+ *								points->coords array, Y coordinates are in
+ *								the odd indices.
+ * fill_color		string			W		X color specification for line
+ * fill_color_gdk	GdkColor*		RW		Pointer to an allocated GdkColor
+ * fill_stipple		GdkBitmap*		RW		Stipple pattern for the line
+ * width_pixels		uint			R		Width of the line in pixels.  The line width
+ *								will not be scaled when the canvas zoom factor changes.
+ * width_units		double			R		Width of the line in canvas units.  The line width
+ *								will be scaled when the canvas zoom factor changes.
+ * cap_style		GdkCapStyle		RW		Cap ("endpoint") style for the line.
+ * join_style		GdkJoinStyle		RW		Join ("vertex") style for the line.
+ * line_style		GdkLineStyle		RW		Line dash style
+ * first_arrowhead	boolean			RW		Specifies whether to draw an arrowhead on the
+ *								first point of the line.
+ * last_arrowhead	boolean			RW		Specifies whether to draw an arrowhead on the
+ *								last point of the line.
+ * smooth		boolean			RW		Specifies whether to smooth the line using
+ *								parabolic splines.
+ * spline_steps		uint			RW		Specifies the number of steps to use when rendering curves.
+ * arrow_shape_a	double			RW		First arrow shape specifier.
+ * arrow_shape_b	double			RW		Second arrow shape specifier.
+ * arrow_shape_c	double			RW		Third arrow shape specifier.
+ */
+
+
+#define GNOME_TYPE_CANVAS_LINE            (gnome_canvas_line_get_type ())
+#define GNOME_CANVAS_LINE(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLine))
+#define GNOME_CANVAS_LINE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLineClass))
+#define GNOME_IS_CANVAS_LINE(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_LINE))
+#define GNOME_IS_CANVAS_LINE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_LINE))
+#define GNOME_CANVAS_LINE_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_LINE, GnomeCanvasLineClass))
+
+
+typedef struct _GnomeCanvasLine GnomeCanvasLine;
+typedef struct _GnomeCanvasLineClass GnomeCanvasLineClass;
+
+struct _GnomeCanvasLine {
+	GnomeCanvasItem item;
+
+	double *coords;		/* Array of coordinates for the line's points.  X coords are in the
+				 * even indices, Y coords are in the odd indices.  If the line has
+				 * arrowheads then the first and last points have been adjusted to
+				 * refer to the necks of the arrowheads rather than their tips.  The
+				 * actual endpoints are stored in the first_arrow and last_arrow
+				 * arrays, if they exist.
+				 */
+
+	double *first_coords;	/* Array of points describing polygon for the first arrowhead */
+	double *last_coords;	/* Array of points describing polygon for the last arrowhead */
+
+	GdkGC *gc;		/* GC for drawing line */
+
+	GdkBitmap *stipple;	/* Stipple pattern */
+
+        ArtSVP *fill_svp;		/* The SVP for the outline shape */ /*AA*/
+	ArtSVP *first_svp;		/* The SVP for the first arrow */ /*AA*/
+	ArtSVP *last_svp;		/* The SVP for the last arrow */ /*AA*/
+
+	double width;		/* Width of the line */
+
+	double shape_a;		/* Distance from tip of arrowhead to center */
+	double shape_b;		/* Distance from tip of arrowhead to trailing point, measured along shaft */
+	double shape_c;		/* Distance of trailing points from outside edge of shaft */
+
+	GdkCapStyle cap;	/* Cap style for line */
+	GdkJoinStyle join;	/* Join style for line */
+	GdkLineStyle line_style;/* Style for the line */
+
+	gulong fill_pixel;	/* Color for line */
+
+	guint32 fill_rgba;		/* RGBA color for outline */ /*AA*/
+
+	int num_points;		/* Number of points in the line */
+	guint fill_color;	/* Fill color, RGBA */
+
+	int spline_steps;	/* Number of steps in each spline segment */
+
+	guint width_pixels : 1;	/* Is the width specified in pixels or units? */
+	guint first_arrow : 1;	/* Draw first arrowhead? */
+	guint last_arrow : 1;	/* Draw last arrowhead? */
+	guint smooth : 1;	/* Smooth line (with parabolic splines)? */
+};
+
+struct _GnomeCanvasLineClass {
+	GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_line_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-marshal.list b/src/libgnomecanvas/gnome-canvas-marshal.list
new file mode 100644
index 0000000..5ad61bf
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-marshal.list
@@ -0,0 +1,2 @@
+VOID:OBJECT,INT,INT,INT,INT
+BOOLEAN:BOXED
diff --git a/src/libgnomecanvas/gnome-canvas-path-def.c b/src/libgnomecanvas/gnome-canvas-path-def.c
new file mode 100644
index 0000000..11b0924
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-path-def.c
@@ -0,0 +1,1287 @@
+#define GNOME_CANVAS_PATH_DEF_C
+
+/*
+ * GnomeCanvasPathDef
+ *
+ * (C) 1999-2000 Lauris Kaplinski <lauris ximian com>
+ * Released under LGPL
+ *
+ * Authors: Lauris Kaplinski <lauris ximian com>
+ *          Rusty Conover <rconover bangtail net>
+ *
+ * Copyright 1999-2001 Ximian Inc. and authors.
+ */
+
+#include <string.h>
+#include <libart_lgpl/art_misc.h>
+#include "gnome-canvas-path-def.h"
+
+/* The number of points to allocate at once */
+#define GNOME_CANVAS_PATH_DEF_LENSTEP 32
+
+struct _GnomeCanvasPathDef {
+	gint refcount;
+	ArtBpath * bpath;
+	gint end;		/* ART_END position */
+	gint length;		/* Num allocated Bpaths */
+	gint substart;		/* subpath start */
+	gdouble x, y;		/* previous moveto position */
+	guint sbpath : 1;	/* Bpath is static */
+	guint hascpt : 1;	/* Currentpoint is defined */
+	guint posset : 1;	/* Previous was moveto */
+	guint moving : 1;	/* Bpath end is moving */
+	guint allclosed : 1;	/* All subpaths are closed */
+	guint allopen : 1;	/* All subpaths are open */
+};
+
+static gboolean sp_bpath_good (ArtBpath * bpath);
+static ArtBpath * sp_bpath_check_subpath (ArtBpath * bpath);
+static gint sp_bpath_length (const ArtBpath * bpath);
+static gboolean sp_bpath_all_closed (const ArtBpath * bpath);
+static gboolean sp_bpath_all_open (const ArtBpath * bpath);
+
+/* Boxed Type Support */
+
+static GnomeCanvasPathDef *
+path_def_boxed_copy (GnomeCanvasPathDef * path_def)
+{
+	if (path_def)
+		gnome_canvas_path_def_ref (path_def);
+	return path_def;
+}
+
+GType
+gnome_canvas_path_def_get_type (void)
+{
+	static GType t = 0;
+	if (t == 0)
+		t = g_boxed_type_register_static
+				("GnomeCanvasPathDef",
+				 (GBoxedCopyFunc)path_def_boxed_copy,
+				 (GBoxedFreeFunc)gnome_canvas_path_def_unref);
+	return t;
+}
+
+/* Constructors */
+
+/**
+ * gnome_canvas_path_def_new:
+ * 
+ * This function creates a new empty #gnome_canvas_path_def.
+ *
+ * Returns: the new canvas path definition. 
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new (void)
+{
+	GnomeCanvasPathDef * path;
+
+	path = gnome_canvas_path_def_new_sized (GNOME_CANVAS_PATH_DEF_LENSTEP);
+
+	return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_sized:
+ * @length: number of points to allocate for the path
+ *
+ * This function creates a new #gnome_canvas_path_def with @length
+ * number of points allocated. It is useful, if you know the exact
+ * number of points in path, so you can avoid automatic point
+ * array reallocation.
+ *
+ * Returns: the new canvas path definition
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_sized (gint length)
+{
+	GnomeCanvasPathDef * path;
+
+	g_return_val_if_fail (length > 0, NULL);
+
+	path = g_new (GnomeCanvasPathDef, 1);
+
+	path->refcount = 1;
+	path->bpath = art_new (ArtBpath, length);
+	path->end = 0;
+	path->bpath[path->end].code = ART_END;
+	path->length = length;
+	path->sbpath = FALSE;
+	path->hascpt = FALSE;
+	path->posset = FALSE;
+	path->moving = FALSE;
+	path->allclosed = TRUE;
+	path->allopen = TRUE;
+
+	return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and uses the
+ * passed @bpath as the contents.  The passed bpath should not be
+ * static as the path definition is editable when constructed with
+ * this function. Also, passed bpath will be freed with art_free, if
+ * path is destroyed, so use it with caution.
+ * For constructing a #gnome_canvas_path_def
+ * from (non-modifiable) bpath use
+ * #gnome_canvas_path_def_new_from_static_bpath.
+ *
+ * Returns: the new canvas path definition that is populated with the
+ * passed bezier path, if the @bpath is bad NULL is returned.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_bpath (ArtBpath * bpath)
+{
+	GnomeCanvasPathDef * path;
+
+	g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+	path = g_new (GnomeCanvasPathDef, 1);
+
+	path->refcount = 1;
+	path->bpath = bpath;
+	path->length = sp_bpath_length (bpath);
+	path->end = path->length - 1;
+	path->sbpath = FALSE;
+	path->hascpt = FALSE;
+	path->posset = FALSE;
+	path->moving = FALSE;
+	path->allclosed = sp_bpath_all_closed (bpath);
+	path->allopen = sp_bpath_all_open (bpath);
+
+	return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_static_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and
+ * references the passed @bpath as its contents.  The
+ * #gnome_canvas_path_def returned from this function is to be
+ * considered static and non-editable (meaning you cannot change the
+ * path from what you passed in @bpath). The bpath will not be freed,
+ * if path will be destroyed, so use it with caution.
+ *
+ * Returns: the new canvas path definition that references the passed
+ * @bpath, or if the path is bad NULL is returned.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_static_bpath (ArtBpath * bpath)
+{
+	GnomeCanvasPathDef * path;
+
+	g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+	path = g_new (GnomeCanvasPathDef, 1);
+
+	path->refcount = 1;
+	path->bpath = bpath;
+	path->length = sp_bpath_length (bpath);
+	path->end = path->length - 1;
+	path->sbpath = TRUE;
+	path->hascpt = FALSE;
+	path->posset = FALSE;
+	path->moving = FALSE;
+	path->allclosed = sp_bpath_all_closed (bpath);
+	path->allopen = sp_bpath_all_open (bpath);
+
+	return path;
+}
+
+/**
+ * gnome_canvas_path_def_new_from_foreign_bpath:
+ * @bpath: libart bezier path
+ *
+ * This function constructs a new #gnome_canvas_path_def and
+ * duplicates the contents of the passed @bpath in the definition.
+ *
+ * Returns: the newly created #gnome_canvas_path_def or NULL if the
+ * path is invalid.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_new_from_foreign_bpath (ArtBpath * bpath)
+{
+	GnomeCanvasPathDef * path;
+	gint length;
+
+	g_return_val_if_fail (sp_bpath_good (bpath), NULL);
+
+	length = sp_bpath_length (bpath);
+
+	path = gnome_canvas_path_def_new_sized (length);
+	memcpy (path->bpath, bpath, sizeof (ArtBpath) * length);
+	path->end = length - 1;
+	path->allclosed = sp_bpath_all_closed (bpath);
+	path->allopen = sp_bpath_all_open (bpath);
+
+	return path;
+}
+
+/**
+ * gnome_canvas_path_def_ref:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Increment the reference count of the GnomeCanvasPathDef.
+ */
+void
+gnome_canvas_path_def_ref (GnomeCanvasPathDef * path)
+{
+	g_return_if_fail (path != NULL);
+
+	path->refcount++;
+}
+
+/**
+ * gnome_canvas_path_def_finish:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Trims dynamic point array to exact length of path.
+ */
+void
+gnome_canvas_path_def_finish (GnomeCanvasPathDef * path)
+{
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (path->sbpath);
+
+	if ((path->end + 1) < path->length) {
+		path->bpath = art_renew (path->bpath, ArtBpath, path->end + 1);
+		path->length = path->end + 1;
+	}
+
+	path->hascpt = FALSE;
+	path->posset = FALSE;
+	path->moving = FALSE;
+}
+/**
+ * gnome_canvas_path_def_ensure_space:
+ * @path: a GnomeCanvasPathDef
+ * @space: number of points to guarantee are allocated at the end of
+ * the path.
+ *
+ * This function ensures that enough space for @space points is
+ * allocated at the end of the path.
+ */
+void
+gnome_canvas_path_def_ensure_space (GnomeCanvasPathDef * path, gint space)
+{
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (space > 0);
+
+	if (path->end + space < path->length) return;
+
+	if (space < GNOME_CANVAS_PATH_DEF_LENSTEP) space = GNOME_CANVAS_PATH_DEF_LENSTEP;
+
+	path->bpath = art_renew (path->bpath, ArtBpath, path->length + space);
+
+	path->length += space;
+}
+
+/**
+ * gnome_canvas_path_def_copy:
+ * @dst: a GnomeCanvasPathDef where the contents of @src will be stored.
+ * @src: a GnomeCanvasPathDef whose contents will be copied it @src.
+ *
+ * This function copies the contents @src to @dest. The old @dest path
+ * array is freed and @dest is marked as non-static (editable),
+ * regardless of the status of @src.
+ */
+void 
+gnome_canvas_path_def_copy (GnomeCanvasPathDef * dst, const GnomeCanvasPathDef * src)
+{
+	g_return_if_fail (dst != NULL);
+	g_return_if_fail (src != NULL);
+
+	if (!dst->sbpath) g_free (dst->bpath);
+
+	memcpy (dst, src, sizeof (GnomeCanvasPathDef));
+
+	dst->bpath = g_new (ArtBpath, src->end + 1);
+	memcpy (dst->bpath, src->bpath, (src->end + 1) * sizeof (ArtBpath));
+
+	dst->sbpath = FALSE;
+}
+
+
+/**
+ * gnome_canvas_path_def_duplicate:
+ * @path: a GnomeCanvasPathDef to duplicate
+ *
+ * This function duplicates the passed @path. The new path is
+ * marked as non-static regardless of the state of original.
+ *
+ * Returns: a GnomeCanvasPathDef which is a duplicate of @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_duplicate (const GnomeCanvasPathDef * path)
+{
+	GnomeCanvasPathDef * new;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	new = gnome_canvas_path_def_new_from_foreign_bpath (path->bpath);
+	new->x = path->x;
+	new->y = path->y;
+	new->hascpt = path->hascpt;
+	new->posset = path->posset;
+	new->moving = path->moving;
+	new->allclosed = path->allclosed;
+	new->allopen = path->allopen;
+
+	return new;
+}
+
+/**
+ * gnome_canvas_path_def_concat:
+ * @list: a GSList of GnomeCanvasPathDefs to concatenate into one new
+ * path.
+ *
+ * This function concatenates a list of GnomeCanvasPathDefs into one
+ * newly created GnomeCanvasPathDef.
+ *
+ * Returns: a new GnomeCanvasPathDef
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_concat (const GSList * list)
+{
+	GnomeCanvasPathDef * c, * new;
+	ArtBpath * bp;
+	const GSList * l;
+	gint length;
+
+	g_return_val_if_fail (list != NULL, NULL);
+
+	length = 1;
+
+	for (l = list; l != NULL; l = l->next) {
+		c = (GnomeCanvasPathDef *) l->data;
+		length += c->end;
+	}
+
+	new = gnome_canvas_path_def_new_sized (length);
+
+	bp = new->bpath;
+
+	for (l = list; l != NULL; l = l->next) {
+		c = (GnomeCanvasPathDef *) l->data;
+		memcpy (bp, c->bpath, c->end * sizeof (ArtBpath));
+		bp += c->end;
+	}
+
+	bp->code = ART_END;
+
+	new->end = length - 1;
+
+	new->allclosed = sp_bpath_all_closed (new->bpath);
+	new->allopen = sp_bpath_all_open (new->bpath);
+
+	return new;
+}
+
+/**
+ * gnome_canvas_path_def_split:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function splits the passed @path into a list of
+ * GnomeCanvasPathDefs which represent each segment of the origional
+ * path.  The path is split when ever an ART_MOVETO or ART_MOVETO_OPEN
+ * is encountered. The closedness of resulting paths is set accordingly
+ * to closedness of corresponding segment.
+ *
+ * Returns: a list of GnomeCanvasPathDef(s).
+ */
+GSList *
+gnome_canvas_path_def_split (const GnomeCanvasPathDef * path)
+{
+	GnomeCanvasPathDef * new;
+	GSList * l;
+	gint p, i;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	p = 0;
+	l = NULL;
+
+	while (p < path->end) {
+		i = 1;
+		while ((path->bpath[p + i].code == ART_LINETO) || (path->bpath[p + i].code == ART_CURVETO)) i++;
+		new = gnome_canvas_path_def_new_sized (i + 1);
+		memcpy (new->bpath, path->bpath + p, i * sizeof (ArtBpath));
+		new->end = i;
+		new->bpath[i].code = ART_END;
+		new->allclosed = (new->bpath->code == ART_MOVETO);
+		new->allopen = (new->bpath->code == ART_MOVETO_OPEN);
+		l = g_slist_append (l, new);
+		p += i;
+	}
+
+	return l;
+}
+
+/**
+ * gnome_canvas_path_def_open_parts:
+ * @path: a GnomeCanvasPathDef
+ * 
+ * This function creates a new GnomeCanvasPathDef that contains all of
+ * the open segments on the passed @path.
+ *
+ * Returns: a new GnomeCanvasPathDef that contains all of the open segemtns in @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_open_parts (const GnomeCanvasPathDef * path)
+{
+	GnomeCanvasPathDef * new;
+	ArtBpath * p, * d;
+	gint len;
+	gboolean closed;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	closed = TRUE;
+	len = 0;
+
+	for (p = path->bpath; p->code != ART_END; p++) {
+		switch (p->code) {
+		case ART_MOVETO_OPEN:
+			closed = FALSE;
+			len++;
+			break;
+		case ART_MOVETO:
+			closed = TRUE;
+			break;
+		case ART_LINETO:
+		case ART_CURVETO:
+			if (!closed) len++;
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	new = gnome_canvas_path_def_new_sized (len + 1);
+
+	closed = TRUE;
+	d = new->bpath;
+
+	for (p = path->bpath; p->code != ART_END; p++) {
+		switch (p->code) {
+		case ART_MOVETO_OPEN:
+			closed = FALSE;
+			*d++ = *p;
+			break;
+		case ART_MOVETO:
+			closed = TRUE;
+			break;
+		case ART_LINETO:
+		case ART_CURVETO:
+			if (!closed) *d++ = *p;
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	d->code = ART_END;
+
+	new->end = len;
+	new->allclosed = FALSE;
+	new->allopen = TRUE;
+
+	return new;
+}
+
+/**
+ * gnome_canvas_path_def_closed_parts:
+ * @path: a GnomeCanvasPathDef
+ * 
+ * This function returns a new GnomeCanvasPathDef that contains the
+ * all of close parts of passed @path.
+ *
+ * Returns: a new GnomeCanvasPathDef that contains all of the closed
+ * parts of passed @path.
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_closed_parts (const GnomeCanvasPathDef * path)
+{
+	GnomeCanvasPathDef * new;
+	ArtBpath * p, * d;
+	gint len;
+	gboolean closed;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	closed = FALSE;
+	len = 0;
+
+	for (p = path->bpath; p->code != ART_END; p++) {
+		switch (p->code) {
+		case ART_MOVETO_OPEN:
+			closed = FALSE;
+			break;
+		case ART_MOVETO:
+			closed = TRUE;
+			len++;
+			break;
+		case ART_LINETO:
+		case ART_CURVETO:
+			if (closed) len++;
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	new = gnome_canvas_path_def_new_sized (len + 1);
+
+	closed = FALSE;
+	d = new->bpath;
+
+	for (p = path->bpath; p->code != ART_END; p++) {
+		switch (p->code) {
+		case ART_MOVETO_OPEN:
+			closed = FALSE;
+			break;
+		case ART_MOVETO:
+			closed = TRUE;
+			*d++ = *p;
+			break;
+		case ART_LINETO:
+		case ART_CURVETO:
+			if (closed) *d++ = *p;
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	d->code = ART_END;
+
+	new->end = len;
+	new->allclosed = TRUE;
+	new->allopen = FALSE;
+
+	return new;
+}
+
+/**
+ * gnome_canvas_path_def_close_all:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes all of the open segments in the passed path
+ * and returns a new GnomeCanvasPathDef.
+ *
+ * Returns: a GnomeCanvasPathDef that contains the contents of @path
+ * but has modified the path is fully closed
+ */
+GnomeCanvasPathDef *
+gnome_canvas_path_def_close_all (const GnomeCanvasPathDef * path)
+{
+	GnomeCanvasPathDef * new;
+	ArtBpath * p, * d, * start;
+	gint len;
+	gboolean closed;
+
+	g_return_val_if_fail (path != NULL, NULL);
+
+	if (path->allclosed) {
+		new = gnome_canvas_path_def_duplicate (path);
+		return new;
+	}
+
+	len = 1;
+
+	/* Count MOVETO_OPEN */
+
+	for (p = path->bpath; p->code != ART_END; p++) {
+		len += 1;
+		if (p->code == ART_MOVETO_OPEN) len += 2;
+	}
+
+	new = gnome_canvas_path_def_new_sized (len);
+
+	d = start = new->bpath;
+	closed = TRUE;
+
+	for (p = path->bpath; p->code != ART_END; p++) {
+		switch (p->code) {
+		case ART_MOVETO_OPEN:
+			start = p;
+			closed = FALSE;
+		case ART_MOVETO:
+			if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
+				d->code = ART_LINETO;
+				d->x3 = start->x3;
+				d->y3 = start->y3;
+				d++;
+			}
+			if (p->code == ART_MOVETO) closed = TRUE;
+			d->code = ART_MOVETO;
+			d->x3 = p->x3;
+			d->y3 = p->y3;
+			d++;
+			break;
+		case ART_LINETO:
+		case ART_CURVETO:
+			*d++ = *p;
+			break;
+		default:
+			g_assert_not_reached ();
+		}
+	}
+
+	if ((!closed) && ((start->x3 != p->x3) || (start->y3 != p->y3))) {
+		d->code = ART_LINETO;
+		d->x3 = start->x3;
+		d->y3 = start->y3;
+		d++;
+	}
+
+	d->code = ART_END;
+
+	new->end = d - new->bpath;
+	new->allclosed = TRUE;
+	new->allopen = FALSE;
+
+	return new;
+}
+
+/* Destructor */
+
+/**
+ * gnome_canvas_path_def_unref:
+ * @path: a GnomeCanvasPathDef
+ *
+ * Decrease the reference count of the passed @path.  If the reference
+ * count is < 1 the path is deallocated.
+ */
+void
+gnome_canvas_path_def_unref (GnomeCanvasPathDef * path)
+{
+	g_return_if_fail (path != NULL);
+
+	if (--path->refcount < 1) {
+		if ((!path->sbpath) && (path->bpath)) art_free (path->bpath);
+		g_free (path);
+	}
+}
+
+
+/* Methods */
+/**
+ * gnome_canvas_path_def_reset:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function clears the contents of the passed @path.
+ */
+void
+gnome_canvas_path_def_reset (GnomeCanvasPathDef * path)
+{
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (!path->sbpath);
+
+	path->bpath->code = ART_END;
+	path->end = 0;
+	path->hascpt = FALSE;
+	path->posset = FALSE;
+	path->moving = FALSE;
+	path->allclosed = TRUE;
+	path->allopen = TRUE;
+}
+
+/* Several consequtive movetos are ALLOWED */
+
+/**
+ * gnome_canvas_path_def_moveto:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This function adds starts new subpath on @path, and sets its
+ * starting point to @x and @y. If current subpath is empty, it
+ * simply changes its starting coordinates to new values.
+ */
+void
+gnome_canvas_path_def_moveto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (!path->sbpath);
+	g_return_if_fail (!path->moving);
+
+	path->substart = path->end;
+	path->hascpt = TRUE;
+	path->posset = TRUE;
+	path->x = x;
+	path->y = y;
+
+	path->allclosed = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_lineto:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This function add a line segment to the passed @path with the
+ * specified @x and @y coordinates.
+ */
+void
+gnome_canvas_path_def_lineto (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+	ArtBpath * bp;
+
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (!path->sbpath);
+	g_return_if_fail (path->hascpt);
+
+	if (path->moving) {
+		/* simply fix endpoint */
+		g_return_if_fail (!path->posset);
+		g_return_if_fail (path->end > 1);
+		bp = path->bpath + path->end - 1;
+		g_return_if_fail (bp->code == ART_LINETO);
+		bp->x3 = x;
+		bp->y3 = y;
+		path->moving = FALSE;
+		return;
+	}
+
+	if (path->posset) {
+		/* start a new segment */
+		gnome_canvas_path_def_ensure_space (path, 2);
+		bp = path->bpath + path->end;
+		bp->code = ART_MOVETO_OPEN;
+		bp->x3 = path->x;
+		bp->y3 = path->y;
+		bp++;
+		bp->code = ART_LINETO;
+		bp->x3 = x;
+		bp->y3 = y;
+		bp++;
+		bp->code = ART_END;
+		path->end += 2;
+		path->posset = FALSE;
+		path->allclosed = FALSE;
+		return;
+	}
+
+	/* Simply add line */
+
+	g_return_if_fail (path->end > 1);
+	gnome_canvas_path_def_ensure_space (path, 1);
+	bp = path->bpath + path->end;
+	bp->code = ART_LINETO;
+	bp->x3 = x;
+	bp->y3 = y;
+	bp++;
+	bp->code = ART_END;
+	path->end++;
+}
+
+
+/**
+ * gnome_canvas_path_def_lineto_moving:
+ * @path: a GnomeCanvasPathDef
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * This functions adds a new line segment with loose endpoint to the path, or
+ * if endpoint is already loose, changes its coordinates to @x, @y. You
+ * can change the coordinates of loose endpoint as many times as you want,
+ * the last ones set will be fixed, if you continue line. This is useful
+ * for handling drawing with mouse.
+ */ 
+void
+gnome_canvas_path_def_lineto_moving (GnomeCanvasPathDef * path, gdouble x, gdouble y)
+{
+	ArtBpath * bp;
+
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (!path->sbpath);
+	g_return_if_fail (path->hascpt);
+
+	if (path->moving) {
+		/* simply change endpoint */
+		g_return_if_fail (!path->posset);
+		g_return_if_fail (path->end > 1);
+		bp = path->bpath + path->end - 1;
+		g_return_if_fail (bp->code == ART_LINETO);
+		bp->x3 = x;
+		bp->y3 = y;
+		return;
+	}
+
+	if (path->posset) {
+		/* start a new segment */
+		gnome_canvas_path_def_ensure_space (path, 2);
+		bp = path->bpath + path->end;
+		bp->code = ART_MOVETO_OPEN;
+		bp->x3 = path->x;
+		bp->y3 = path->y;
+		bp++;
+		bp->code = ART_LINETO;
+		bp->x3 = x;
+		bp->y3 = y;
+		bp++;
+		bp->code = ART_END;
+		path->end += 2;
+		path->posset = FALSE;
+		path->moving = TRUE;
+		path->allclosed = FALSE;
+		return;
+	}
+
+	/* Simply add line */
+
+	g_return_if_fail (path->end > 1);
+	gnome_canvas_path_def_ensure_space (path, 1);
+	bp = path->bpath + path->end;
+	bp->code = ART_LINETO;
+	bp->x3 = x;
+	bp->y3 = y;
+	bp++;
+	bp->code = ART_END;
+	path->end++;
+	path->moving = TRUE;
+}
+
+/**
+ * gnome_canvas_path_def_curveto:
+ * @path: a GnomeCanvasPathDef
+ * @x0: first control point x coordinate
+ * @y0: first control point y coordinate
+ * @x1: second control point x coordinate
+ * @y1: second control point y coordinate
+ * @x2: end of curve x coordinate
+ * @y2: end of curve y coordinate
+ *
+ * This function adds a bezier curve segment to the path definition.
+ */
+ 
+void
+gnome_canvas_path_def_curveto (GnomeCanvasPathDef * path, gdouble x0, gdouble y0, gdouble x1, gdouble y1, gdouble x2, gdouble y2)
+{
+	ArtBpath * bp;
+
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (!path->sbpath);
+	g_return_if_fail (path->hascpt);
+	g_return_if_fail (!path->moving);
+
+	if (path->posset) {
+		/* start a new segment */
+		gnome_canvas_path_def_ensure_space (path, 2);
+		bp = path->bpath + path->end;
+		bp->code = ART_MOVETO_OPEN;
+		bp->x3 = path->x;
+		bp->y3 = path->y;
+		bp++;
+		bp->code = ART_CURVETO;
+		bp->x1 = x0;
+		bp->y1 = y0;
+		bp->x2 = x1;
+		bp->y2 = y1;
+		bp->x3 = x2;
+		bp->y3 = y2;
+		bp++;
+		bp->code = ART_END;
+		path->end += 2;
+		path->posset = FALSE;
+		path->allclosed = FALSE;
+		return;
+	}
+
+	/* Simply add path */
+
+	g_return_if_fail (path->end > 1);
+	gnome_canvas_path_def_ensure_space (path, 1);
+	bp = path->bpath + path->end;
+	bp->code = ART_CURVETO;
+	bp->x1 = x0;
+	bp->y1 = y0;
+	bp->x2 = x1;
+	bp->y2 = y1;
+	bp->x3 = x2;
+	bp->y3 = y2;
+	bp++;
+	bp->code = ART_END;
+	path->end++;
+}
+
+/**
+ * gnome_canvas_path_def_closepath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes the last subpath of @path, adding a ART_LINETO to
+ * subpath starting point, if needed and changing starting pathcode to
+ * ART_MOVETO
+ */
+void
+gnome_canvas_path_def_closepath (GnomeCanvasPathDef * path)
+{
+	ArtBpath * bs, * be;
+
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (!path->sbpath);
+	g_return_if_fail (path->hascpt);
+	g_return_if_fail (!path->posset);
+	g_return_if_fail (!path->moving);
+	g_return_if_fail (!path->allclosed);
+	/* We need at last M + L + L + E */
+	g_return_if_fail (path->end - path->substart > 2);
+
+	bs = path->bpath + path->substart;
+	be = path->bpath + path->end - 1;
+
+	if ((bs->x3 != be->x3) || (bs->y3 != be->y3)) {
+		gnome_canvas_path_def_lineto (path, bs->x3, bs->y3);
+	}
+
+	bs = path->bpath + path->substart; /* NB. def_lineto can
+					      realloc bpath */
+	bs->code = ART_MOVETO;
+
+	path->allclosed = sp_bpath_all_closed (path->bpath);
+	path->allopen = sp_bpath_all_open (path->bpath);
+
+	path->hascpt = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_closepath_current:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function closes the last subpath by setting the coordinates of
+ * the endpoint of the last segment (line or curve) to starting point.
+ */
+void
+gnome_canvas_path_def_closepath_current (GnomeCanvasPathDef * path)
+{
+	ArtBpath * bs, * be;
+
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (!path->sbpath);
+	g_return_if_fail (path->hascpt);
+	g_return_if_fail (!path->posset);
+	g_return_if_fail (!path->allclosed);
+	/* We need at last M + L + L + E */
+	g_return_if_fail (path->end - path->substart > 2);
+
+	bs = path->bpath + path->substart;
+	be = path->bpath + path->end - 1;
+
+	be->x3 = bs->x3;
+	be->y3 = bs->y3;
+
+	bs->code = ART_MOVETO;
+
+	path->allclosed = sp_bpath_all_closed (path->bpath);
+	path->allopen = sp_bpath_all_open (path->bpath);
+
+	path->hascpt = FALSE;
+	path->moving = FALSE;
+}
+
+/**
+ * gnome_canvas_path_def_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a ArtBpath that consists of the path
+ * definition.
+ *
+ * Returns: ArtBpath
+ */
+ArtBpath * gnome_canvas_path_def_bpath (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, NULL);
+
+	return path->bpath;
+}
+
+/**
+ * gnome_canvas_path_def_length:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns the length of the path definition.  Not
+ * Euclidian length of the path but rather the number of points on the
+ * path.
+ *
+ * Returns: integer, number of points on the path.
+ */
+gint gnome_canvas_path_def_length (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, -1);
+
+	return path->end + 1;
+}
+
+/**
+ * gnome_canvas_path_def_is_empty:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function is a boolean test to see if the path is empty,
+ * meaning containing no line segments.
+ *
+ * Returns: boolean, indicating if the path is empty.
+ */
+gboolean
+gnome_canvas_path_def_is_empty (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, TRUE);
+
+	return (path->bpath->code == ART_END);
+}
+
+/**
+ * gnome_canvas_path_def_has_currentpoint:
+ * @path: a GnomeCanvasPathdef
+ *
+ * This function is a boolean test checking to see if the path has a
+ * current point defined. Current point will be set by line operators,
+ * and cleared by closing subpath.
+ *
+ * Returns: boolean, indicating if the path has a current point defined.
+ */
+gboolean
+gnome_canvas_path_def_has_currentpoint (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	return (path->hascpt);
+}
+
+/**
+ * gnome_canvas_path_def_currentpoint:
+ * @path: a GnomeCanvasPathDef
+ * @p: a ArtPoint where to store the current point
+ *
+ * Stores the current point of the path definition in the passed ArtPoint @p.
+ */
+void
+gnome_canvas_path_def_currentpoint (const GnomeCanvasPathDef * path, ArtPoint * p)
+{
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (p != NULL);
+	g_return_if_fail (path->hascpt);
+
+	if (path->posset) {
+		p->x = path->x;
+		p->y = path->y;
+	} else {
+		p->x = (path->bpath + path->end - 1)->x3;
+		p->y = (path->bpath + path->end - 1)->y3;
+	}	
+}
+
+/**
+ * gnome_canvas_path_def_last_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns pointer to the last ArtBpath segment in the path
+ * definition.
+ *
+ * Returns: ArtBpath, being the last segment in the path definition or
+ * null if no line segments have been defined.
+ */
+ArtBpath *
+gnome_canvas_path_def_last_bpath (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, NULL);
+
+	if (path->end == 0) return NULL;
+
+	return path->bpath + path->end - 1;
+}
+
+/**
+ * gnome_canvas_path_def_first_bpath:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns the first ArtBpath point in the definition.
+ *
+ * Returns: ArtBpath being the first point in the path definition or
+ * null if no points are defined
+*/
+ArtBpath *
+gnome_canvas_path_def_first_bpath (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, NULL);
+
+	if (path->end == 0) return NULL;
+
+	return path->bpath;
+}
+
+/**
+ * gnome_canvas_path_def_any_open:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path has
+ * any open segments.
+ *
+ * Returns: boolean, indicating if the path has any open segments.
+ */
+gboolean
+gnome_canvas_path_def_any_open (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	return (!path->allclosed);
+}
+
+/**
+ * gnome_canvas_path_def_all_open:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path only
+ * contains open segments.
+ *
+ * Returns: boolean, indicating if the path has all open segments.
+ */
+gboolean
+gnome_canvas_path_def_all_open (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	return (path->allopen);
+}
+
+/**
+ * gnome_canvas_path_def_any_closed:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean valid indicating if the path has
+ * any closed segements.
+ *
+ * Returns: boolean, indicating if the path has any closed segments.
+ */
+gboolean
+gnome_canvas_path_def_any_closed (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	return (!path->allopen);
+}
+
+/**
+ * gnome_canvas_path_def_all_closed:
+ * @path: a GnomeCanvasPathDef
+ *
+ * This function returns a boolean value indicating if the path only
+ * contains closed segments.
+ *
+ * Returns: boolean, indicating if the path has all closed segments.
+ */
+gboolean
+gnome_canvas_path_def_all_closed (const GnomeCanvasPathDef * path)
+{
+	g_return_val_if_fail (path != NULL, FALSE);
+
+	return (path->allclosed);
+}
+
+/* Private methods */
+
+static
+gboolean sp_bpath_good (ArtBpath * bpath)
+{
+	ArtBpath * bp;
+
+	g_return_val_if_fail (bpath != NULL, FALSE);
+
+	if (bpath->code == ART_END)
+                return TRUE;
+
+	bp = bpath;
+
+	while (bp->code != ART_END) {
+		bp = sp_bpath_check_subpath (bp);
+		if (bp == NULL) return FALSE;
+	}
+
+	return TRUE;
+}
+
+static ArtBpath *
+sp_bpath_check_subpath (ArtBpath * bpath)
+{
+	gint i, len;
+	gboolean closed;
+
+	g_return_val_if_fail (bpath != NULL, NULL);
+
+	if (bpath->code == ART_MOVETO) {
+		closed = TRUE;
+	} else if (bpath->code == ART_MOVETO_OPEN) {
+		closed = FALSE;
+	} else {
+		return NULL;
+	}
+
+	len = 0;
+
+	for (i = 1; (bpath[i].code != ART_END) && (bpath[i].code != ART_MOVETO) && (bpath[i].code != ART_MOVETO_OPEN); i++) {
+		switch (bpath[i].code) {
+			case ART_LINETO:
+			case ART_CURVETO:
+				len++;
+				break;
+			default:
+				return NULL;
+		}
+	}
+
+	if (closed) {
+		if (len < 2) return NULL;
+		if ((bpath->x3 != bpath[i-1].x3) || (bpath->y3 != bpath[i-1].y3)) return NULL;
+	} else {
+		if (len < 1) return NULL;
+	}
+
+	return bpath + i;
+}
+
+static gint
+sp_bpath_length (const ArtBpath * bpath)
+{
+	gint l;
+
+	g_return_val_if_fail (bpath != NULL, FALSE);
+
+	for (l = 0; bpath[l].code != ART_END; l++) ;
+
+	l++;
+
+	return l;
+}
+
+static gboolean
+sp_bpath_all_closed (const ArtBpath * bpath)
+{
+	const ArtBpath * bp;
+
+	g_return_val_if_fail (bpath != NULL, FALSE);
+
+	for (bp = bpath; bp->code != ART_END; bp++)
+		if (bp->code == ART_MOVETO_OPEN) return FALSE;
+
+	return TRUE;
+}
+
+static gboolean
+sp_bpath_all_open (const ArtBpath * bpath)
+{
+	const ArtBpath * bp;
+
+	g_return_val_if_fail (bpath != NULL, FALSE);
+
+	for (bp = bpath; bp->code != ART_END; bp++)
+		if (bp->code == ART_MOVETO) return FALSE;
+
+	return TRUE;
+}
+
+
diff --git a/src/libgnomecanvas/gnome-canvas-path-def.h b/src/libgnomecanvas/gnome-canvas-path-def.h
new file mode 100644
index 0000000..c3f6b25
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-path-def.h
@@ -0,0 +1,96 @@
+#ifndef GNOME_CANVAS_PATH_DEF_H
+#define GNOME_CANVAS_PATH_DEF_H
+
+/*
+ * GnomeCanvasPathDef
+ *
+ * (C) 1999-2000 Lauris Kaplinski <lauris ximian com>
+ * Released under LGPL
+ *
+ * This is mostly like GnomeCanvasBpathDef, but with added functionality:
+ * - can be constructed from scratch, from existing bpath of from static bpath
+ * - Path is always terminated with ART_END
+ * - Has closed flag
+ * - has concat, split and copy methods
+ *
+ */
+
+#include <glib-object.h>
+#include <libart_lgpl/art_bpath.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasPathDef GnomeCanvasPathDef;
+
+#define GNOME_TYPE_CANVAS_PATH_DEF	(gnome_canvas_path_def_get_type ())
+GType gnome_canvas_path_def_get_type (void) G_GNUC_CONST;
+
+/* Constructors */
+
+GnomeCanvasPathDef * gnome_canvas_path_def_new (void);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_sized (gint length);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_bpath (ArtBpath * bpath);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_static_bpath (ArtBpath * bpath);
+GnomeCanvasPathDef * gnome_canvas_path_def_new_from_foreign_bpath (ArtBpath * bpath);
+
+void gnome_canvas_path_def_ref (GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_finish (GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_ensure_space (GnomeCanvasPathDef * path, gint space);
+
+/*
+ * Misc constructors
+ * All these return NEW path, not unrefing old
+ * Also copy and duplicate force bpath to be private (otherwise you
+ * would use ref :)
+ */
+
+void gnome_canvas_path_def_copy (GnomeCanvasPathDef * dst, const GnomeCanvasPathDef * src);
+GnomeCanvasPathDef * gnome_canvas_path_def_duplicate (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_concat (const GSList * list);
+GSList * gnome_canvas_path_def_split (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_open_parts (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_closed_parts (const GnomeCanvasPathDef * path);
+GnomeCanvasPathDef * gnome_canvas_path_def_close_all (const GnomeCanvasPathDef * path);
+
+/* Destructor */
+
+void gnome_canvas_path_def_unref (GnomeCanvasPathDef * path);
+
+/* Methods */
+
+/* Sets GnomeCanvasPathDef to zero length */
+
+void gnome_canvas_path_def_reset (GnomeCanvasPathDef * path);
+
+/* Drawing methods */
+
+void gnome_canvas_path_def_moveto (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+void gnome_canvas_path_def_lineto (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+
+/* Does not create new ArtBpath, but simply changes last lineto position */
+
+void gnome_canvas_path_def_lineto_moving (GnomeCanvasPathDef * path, gdouble x, gdouble y);
+void gnome_canvas_path_def_curveto (GnomeCanvasPathDef * path, gdouble x0, gdouble y0,gdouble x1, gdouble y1, gdouble x2, gdouble y2);
+void gnome_canvas_path_def_closepath (GnomeCanvasPathDef * path);
+
+/* Does not draw new line to startpoint, but moves last lineto */
+
+void gnome_canvas_path_def_closepath_current (GnomeCanvasPathDef * path);
+
+/* Various methods */
+
+ArtBpath * gnome_canvas_path_def_bpath (const GnomeCanvasPathDef * path);
+gint gnome_canvas_path_def_length (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_is_empty (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_has_currentpoint (const GnomeCanvasPathDef * path);
+void gnome_canvas_path_def_currentpoint (const GnomeCanvasPathDef * path, ArtPoint * p);
+ArtBpath * gnome_canvas_path_def_last_bpath (const GnomeCanvasPathDef * path);
+ArtBpath * gnome_canvas_path_def_first_bpath (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_any_open (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_all_open (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_any_closed (const GnomeCanvasPathDef * path);
+gboolean gnome_canvas_path_def_all_closed (const GnomeCanvasPathDef * path);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-pixbuf.c b/src/libgnomecanvas/gnome-canvas-pixbuf.c
new file mode 100644
index 0000000..e16e9e5
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-pixbuf.c
@@ -0,0 +1,1069 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico gimp org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <math.h>
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <libart_lgpl/art_rgb_affine.h>
+#include <libart_lgpl/art_rgb_rgba_affine.h>
+#include "gnome-canvas-pixbuf.h"
+
+/* Private part of the GnomeCanvasPixbuf structure */
+typedef struct {
+	/* Our gdk-pixbuf */
+	GdkPixbuf *pixbuf;
+
+	/* Width value */
+	double width;
+
+	/* Height value */
+	double height;
+
+	/* X translation */
+	double x;
+
+	/* Y translation */
+	double y;
+
+	/* Whether dimensions are set and whether they are in pixels or units */
+	guint width_set : 1;
+	guint width_in_pixels : 1;
+	guint height_set : 1;
+	guint height_in_pixels : 1;
+	guint x_in_pixels : 1;
+	guint y_in_pixels : 1;
+
+	/* Whether the pixbuf has changed */
+	guint need_pixbuf_update : 1;
+
+	/* Whether the transformation or size have changed */
+	guint need_xform_update : 1;
+
+	/* Anchor */
+	GtkAnchorType anchor;
+} PixbufPrivate;
+
+/* Object argument IDs */
+enum {
+	PROP_0,
+	PROP_PIXBUF,
+	PROP_WIDTH,
+	PROP_WIDTH_SET,
+	PROP_WIDTH_IN_PIXELS,
+	PROP_HEIGHT,
+	PROP_HEIGHT_SET,
+	PROP_HEIGHT_IN_PIXELS,
+	PROP_X,
+	PROP_X_IN_PIXELS,
+	PROP_Y,
+	PROP_Y_IN_PIXELS,
+	PROP_ANCHOR
+};
+
+static void gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class);
+static void gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *cpb);
+static void gnome_canvas_pixbuf_destroy (GtkObject *object);
+static void gnome_canvas_pixbuf_set_property (GObject *object,
+					      guint param_id,
+					      const GValue *value,
+					      GParamSpec *pspec);
+static void gnome_canvas_pixbuf_get_property (GObject *object,
+					      guint param_id,
+					      GValue *value,
+					      GParamSpec *pspec);
+
+static void gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine,
+					ArtSVP *clip_path, int flags);
+static void gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+				      int x, int y, int width, int height);
+static void gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static double gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+					 GnomeCanvasItem **actual_item);
+static void gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item,
+					double *x1, double *y1, double *x2, double *y2);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+
+/**
+ * gnome_canvas_pixbuf_get_type:
+ * @void:
+ *
+ * Registers the #GnomeCanvasPixbuf class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the #GnomeCanvasPixbuf class.
+ **/
+GtkType
+gnome_canvas_pixbuf_get_type (void)
+{
+	static GType pixbuf_type;
+
+	if (!pixbuf_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasPixbufClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_pixbuf_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasPixbuf),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_pixbuf_init,
+			NULL			/* value_table */
+		};
+
+		pixbuf_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasPixbuf",
+						     &object_info, 0);
+	}
+
+	return pixbuf_type;
+}
+
+/* Class initialization function for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class)
+{
+        GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+        gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_pixbuf_set_property;
+	gobject_class->get_property = gnome_canvas_pixbuf_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_PIXBUF,
+                 g_param_spec_object ("pixbuf", NULL, NULL,
+                                      GDK_TYPE_PIXBUF,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH,
+                 g_param_spec_double ("width", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_SET,
+                 g_param_spec_boolean ("width_set", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH_IN_PIXELS,
+                 g_param_spec_boolean ("width_in_pixels", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT,
+                 g_param_spec_double ("height", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT_SET,
+                 g_param_spec_boolean ("height_set", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT_IN_PIXELS,
+                 g_param_spec_boolean ("height_in_pixels", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X,
+                 g_param_spec_double ("x", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X_IN_PIXELS,
+                 g_param_spec_boolean ("x_in_pixels", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y,
+                 g_param_spec_double ("y", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y_IN_PIXELS,
+                 g_param_spec_boolean ("y_in_pixels", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ANCHOR,
+                 g_param_spec_enum ("anchor", NULL, NULL,
+                                    GTK_TYPE_ANCHOR_TYPE,
+                                    GTK_ANCHOR_NW,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_pixbuf_destroy;
+
+	item_class->update = gnome_canvas_pixbuf_update;
+	item_class->draw = gnome_canvas_pixbuf_draw;
+	item_class->render = gnome_canvas_pixbuf_render;
+	item_class->point = gnome_canvas_pixbuf_point;
+	item_class->bounds = gnome_canvas_pixbuf_bounds;
+}
+
+/* Object initialization function for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_init (GnomeCanvasPixbuf *gcp)
+{
+	PixbufPrivate *priv;
+
+	priv = g_new0 (PixbufPrivate, 1);
+	gcp->priv = priv;
+
+	priv->width = 0.0;
+	priv->height = 0.0;
+	priv->x = 0.0;
+	priv->y = 0.0;
+	priv->anchor = GTK_ANCHOR_NW;
+}
+
+/* Destroy handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_destroy (GtkObject *object)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	gcp = (GNOME_CANVAS_PIXBUF (object));
+	priv = gcp->priv;
+
+	/* remember, destroy can be run multiple times! */
+
+	if (priv) {
+	    gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+
+	    if (priv->pixbuf)
+		gdk_pixbuf_unref (priv->pixbuf);
+
+	    g_free (priv);
+	    gcp->priv = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+
+
+/* Set_property handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_set_property (GObject            *object,
+				  guint               param_id,
+				  const GValue       *value,
+				  GParamSpec         *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	GdkPixbuf *pixbuf;
+	double val;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	gcp = GNOME_CANVAS_PIXBUF (object);
+	priv = gcp->priv;
+
+	switch (param_id) {
+	case PROP_PIXBUF:
+		if (g_value_get_object (value))
+			pixbuf = GDK_PIXBUF (g_value_get_object (value));
+		else
+			pixbuf = NULL;
+		if (pixbuf != priv->pixbuf) {
+			if (pixbuf) {
+				g_return_if_fail
+				    (gdk_pixbuf_get_colorspace (pixbuf) == GDK_COLORSPACE_RGB);
+				g_return_if_fail
+				    (gdk_pixbuf_get_n_channels (pixbuf) == 3
+				     || gdk_pixbuf_get_n_channels (pixbuf) == 4);
+				g_return_if_fail
+				    (gdk_pixbuf_get_bits_per_sample (pixbuf) == 8);
+
+				gdk_pixbuf_ref (pixbuf);
+			}
+
+			if (priv->pixbuf)
+				gdk_pixbuf_unref (priv->pixbuf);
+
+			priv->pixbuf = pixbuf;
+		}
+
+		priv->need_pixbuf_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH:
+		val = g_value_get_double (value);
+		g_return_if_fail (val >= 0.0);
+		priv->width = val;
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_SET:
+		priv->width_set = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_IN_PIXELS:
+		priv->width_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_HEIGHT:
+		val = g_value_get_double (value);
+		g_return_if_fail (val >= 0.0);
+		priv->height = val;
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_HEIGHT_SET:
+		priv->height_set = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_HEIGHT_IN_PIXELS:
+		priv->height_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_X:
+		priv->x = g_value_get_double (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_X_IN_PIXELS:
+		priv->x_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y:
+		priv->y = g_value_get_double (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y_IN_PIXELS:
+		priv->y_in_pixels = g_value_get_boolean (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_ANCHOR:
+		priv->anchor = g_value_get_enum (value);
+		priv->need_xform_update = TRUE;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* Get_property handler for the pixbuf canvasi item */
+static void
+gnome_canvas_pixbuf_get_property (GObject            *object,
+				  guint               param_id,
+				  GValue             *value,
+				  GParamSpec         *pspec)
+{
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_PIXBUF (object));
+
+	gcp = GNOME_CANVAS_PIXBUF (object);
+	priv = gcp->priv;
+
+	switch (param_id) {
+	case PROP_PIXBUF:
+		g_value_set_object (value, G_OBJECT (priv->pixbuf));
+		break;
+
+	case PROP_WIDTH:
+		g_value_set_double (value, priv->width);
+		break;
+
+	case PROP_WIDTH_SET:
+		g_value_set_boolean (value, priv->width_set);
+		break;
+
+	case PROP_WIDTH_IN_PIXELS:
+		g_value_set_boolean (value, priv->width_in_pixels);
+		break;
+
+	case PROP_HEIGHT:
+		g_value_set_double (value, priv->height);
+		break;
+
+	case PROP_HEIGHT_SET:
+		g_value_set_boolean (value, priv->height_set);
+		break;
+
+	case PROP_HEIGHT_IN_PIXELS:
+		g_value_set_boolean (value, priv->height_in_pixels);
+		break;
+
+	case PROP_X:
+		g_value_set_double (value, priv->x);
+		break;
+
+	case PROP_X_IN_PIXELS:
+		g_value_set_boolean (value, priv->x_in_pixels);
+		break;
+
+	case PROP_Y:
+		g_value_set_double (value, priv->y);
+		break;
+
+	case PROP_Y_IN_PIXELS:
+		g_value_set_boolean (value, priv->y_in_pixels);
+		break;
+
+	case PROP_ANCHOR:
+		g_value_set_enum (value, priv->anchor);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+
+
+/* Bounds and utilities */
+
+/* Computes the amount by which the unit horizontal and vertical vectors will be
+ * scaled by an affine transformation.
+ */
+static void
+compute_xform_scaling (double *affine, ArtPoint *i_c, ArtPoint *j_c)
+{
+	ArtPoint orig, orig_c;
+	ArtPoint i, j;
+
+	/* Origin */
+
+	orig.x = 0.0;
+	orig.y = 0.0;
+	art_affine_point (&orig_c, &orig, affine);
+
+	/* Horizontal and vertical vectors */
+
+	i.x = 1.0;
+	i.y = 0.0;
+	art_affine_point (i_c, &i, affine);
+	i_c->x -= orig_c.x;
+	i_c->y -= orig_c.y;
+
+	j.x = 0.0;
+	j.y = 1.0;
+	art_affine_point (j_c, &j, affine);
+	j_c->x -= orig_c.x;
+	j_c->y -= orig_c.y;
+}
+
+/* computes the addtional resolution dependent affine needed to
+ * fit the image within its viewport defined by x,y,width and height
+ * args
+ */
+static void
+compute_viewport_affine (GnomeCanvasPixbuf *gcp, double *viewport_affine, double *i2c)
+{
+	PixbufPrivate *priv;
+	ArtPoint i_c, j_c;
+	double i_len, j_len;
+	double si_len, sj_len;
+	double ti_len, tj_len;
+	double scale[6], translate[6];
+	double w, h;
+	double x, y;
+
+	priv = gcp->priv;
+
+	/* Compute scaling vectors and required width/height */
+
+	compute_xform_scaling (i2c, &i_c, &j_c);
+
+	i_len = sqrt (i_c.x * i_c.x + i_c.y * i_c.y);
+	j_len = sqrt (j_c.x * j_c.x + j_c.y * j_c.y);
+
+	if (priv->width_set)
+		w = priv->width;
+	else
+		w = gdk_pixbuf_get_width (priv->pixbuf);
+
+	if (priv->height_set)
+		h = priv->height;
+	else
+		h = gdk_pixbuf_get_height (priv->pixbuf);
+
+	x = priv->x;
+	y = priv->y;
+
+	/* Convert i_len and j_len into scaling factors */
+
+	if (priv->width_in_pixels) {
+		if (i_len > GNOME_CANVAS_EPSILON)
+			si_len = 1.0 / i_len;
+		else
+			si_len = 0.0;
+	} else
+		si_len = 1.0;
+
+	si_len *= w / gdk_pixbuf_get_width (priv->pixbuf);
+
+	if (priv->height_in_pixels) {
+		if (j_len > GNOME_CANVAS_EPSILON)
+			sj_len = 1.0 / j_len;
+		else
+			sj_len = 0.0;
+	} else
+		sj_len = 1.0;
+
+	sj_len *= h / gdk_pixbuf_get_height (priv->pixbuf);
+
+	/* Calculate translation offsets */
+
+	if (priv->x_in_pixels) {
+		if (i_len > GNOME_CANVAS_EPSILON)
+			ti_len = 1.0 / i_len;
+		else
+			ti_len = 0.0;
+	} else
+		ti_len = 1.0;
+
+	switch (priv->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		ti_len *= x;
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		ti_len *= x - w * si_len / 2;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		ti_len *= x - w * si_len;
+		break;
+
+        default:
+                break;
+	}
+
+	if (priv->y_in_pixels) {
+		if (j_len > GNOME_CANVAS_EPSILON)
+			tj_len = 1.0 / j_len;
+		else
+			tj_len = 0.0;
+	} else
+		tj_len = 1.0;
+
+	switch (priv->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		tj_len *= y;
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		tj_len *= y - h * sj_len / 2;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		tj_len *= y - h * sj_len;
+		break;
+
+        default:
+                break;
+	}
+
+	/* Compute the final affine */
+
+	art_affine_scale (scale, si_len, sj_len);
+	art_affine_translate (translate, ti_len, tj_len);
+  	art_affine_multiply (viewport_affine, scale, translate);
+}
+
+/* Computes the affine transformation with which the pixbuf needs to be
+ * transformed to render it on the canvas.  This is not the same as the
+ * item_to_canvas transformation because we may need to scale the pixbuf
+ * by some other amount.
+ */
+static void
+compute_render_affine (GnomeCanvasPixbuf *gcp, double *ra, double *i2c)
+{
+	double va[6];
+
+	compute_viewport_affine (gcp, va, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+	g_print ("va %g %g %g %g %g %g\n", va[0], va[1], va[2], va[3], va[4], va[5]);
+#endif
+	art_affine_multiply (ra, va, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+	g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
+#endif
+}
+
+/* Recomputes the bounding box of a pixbuf canvas item.  The horizontal and
+ * vertical dimensions may be specified in units or pixels, separately, so we
+ * have to compute the components individually for each dimension.
+ */
+static void
+recompute_bounding_box (GnomeCanvasPixbuf *gcp, gdouble *i2c)
+{
+	GnomeCanvasItem *item;
+	PixbufPrivate *priv;
+	double ra[6];
+	ArtDRect rect;
+
+	item = GNOME_CANVAS_ITEM (gcp);
+	priv = gcp->priv;
+
+	if (!priv->pixbuf) {
+		item->x1 = item->y1 = item->x2 = item->y2 = 0.0;
+		return;
+	}
+
+	rect.x0 = 0.0;
+	rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
+
+	rect.y0 = 0.0;
+	rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
+
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+	g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
+#endif
+	gnome_canvas_item_i2c_affine (item, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+	g_print ("i2c %g %g %g %g %g %g\n", i2c[0], i2c[1], i2c[2], i2c[3], i2c[4], i2c[5]);
+#endif
+	compute_render_affine (gcp, ra, i2c);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+	g_print ("ra %g %g %g %g %g %g\n", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]);
+#endif
+	art_drect_affine_transform (&rect, &rect, ra);
+
+	item->x1 = floor (rect.x0);
+	item->y1 = floor (rect.y0);
+	item->x2 = ceil (rect.x1);
+	item->y2 = ceil (rect.y1);
+}
+
+
+
+/* Update sequence */
+
+/* Update handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+
+	gcp = GNOME_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (parent_class->update)
+		(* parent_class->update) (item, affine, clip_path, flags);
+
+        /* the optimzations below cause rarely triggered redrawing bugs and
+	 * don't seem to actually save much performance. so it's probably
+	 * better to turn them off, than to chase subtle optimization bugs
+	 * throughgout all of gnome-canvas-pixbuf.c - TIMJ
+	 */
+#ifdef USE_BROKEN_OPTIMIZATIONS
+	if (((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
+	     && !(GTK_OBJECT_FLAGS (item) & GNOME_CANVAS_ITEM_VISIBLE))
+	    || (flags & GNOME_CANVAS_UPDATE_AFFINE)
+	    || priv->need_pixbuf_update
+	    || priv->need_xform_update) {
+		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+	}
+
+	/* If we need a pixbuf update, or if the item changed visibility to
+	 * shown, recompute the bounding box.
+	 */
+	if (priv->need_pixbuf_update
+	    || priv->need_xform_update
+	    || ((flags & GNOME_CANVAS_UPDATE_VISIBILITY)
+		&& (GTK_OBJECT_FLAGS (gcp) & GNOME_CANVAS_ITEM_VISIBLE))
+	    || (flags & GNOME_CANVAS_UPDATE_AFFINE)) {
+		recompute_bounding_box (gcp, affine);
+#ifdef GNOME_CANVAS_PIXBUF_VERBOSE
+		g_print ("BBox is %g %g %g %g\n", item->x1, item->y1, item->x2, item->y2);
+#endif
+		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+		priv->need_pixbuf_update = FALSE;
+		priv->need_xform_update = FALSE;
+	}
+#else   /* ordinary update logic */
+        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+        recompute_bounding_box (gcp, affine);
+        gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+        priv->need_pixbuf_update = FALSE;
+        priv->need_xform_update = FALSE;
+#endif
+}
+
+
+
+/* Rendering */
+
+/* This is private to libart, but we need it.  Sigh. */
+extern void art_rgb_affine_run (int *p_x0, int *p_x1, int y, int src_width, int src_height,
+				const double affine[6]);
+
+/* Fills the specified buffer with the transformed version of a pixbuf */
+static void
+transform_pixbuf (guchar *dest, int x, int y, int width, int height, int rowstride,
+		  GdkPixbuf *pixbuf, double *affine)
+{
+	int xx, yy;
+	double inv[6];
+	guchar *src, *d;
+	ArtPoint src_p, dest_p;
+	int run_x1, run_x2;
+	int src_x, src_y;
+	int i;
+
+	art_affine_invert (inv, affine);
+
+	for (yy = 0; yy < height; yy++) {
+		dest_p.y = y + yy + 0.5;
+
+		run_x1 = x;
+		run_x2 = x + width;
+		art_rgb_affine_run (&run_x1, &run_x2, yy + y,
+				    gdk_pixbuf_get_width (pixbuf),
+				    gdk_pixbuf_get_height (pixbuf),
+				    inv);
+
+		d = dest + yy * rowstride + (run_x1 - x) * 4;
+
+		for (xx = run_x1; xx < run_x2; xx++) {
+			dest_p.x = xx + 0.5;
+			art_affine_point (&src_p, &dest_p, inv);
+			src_x = floor (src_p.x);
+			src_y = floor (src_p.y);
+
+			src =
+			    gdk_pixbuf_get_pixels (pixbuf) + src_y *
+			    gdk_pixbuf_get_rowstride (pixbuf) + src_x *
+			    gdk_pixbuf_get_n_channels (pixbuf);
+
+			for (i = 0; i < gdk_pixbuf_get_n_channels (pixbuf); i++)
+				*d++ = *src++;
+
+			if (!gdk_pixbuf_get_has_alpha(pixbuf))
+				*d++ = 255; /* opaque */
+		}
+	}
+}
+
+/* Draw handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+			  int x, int y, int width, int height)
+{
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	double i2c[6], render_affine[6];
+	guchar *buf;
+	GdkPixbuf *pixbuf;
+	ArtIRect p_rect, a_rect, d_rect;
+	int w, h;
+
+	gcp = GNOME_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (!priv->pixbuf)
+		return;
+
+	gnome_canvas_item_i2c_affine (item, i2c);
+	compute_render_affine (gcp, render_affine, i2c);
+
+	/* Compute the area we need to repaint */
+
+	p_rect.x0 = item->x1;
+	p_rect.y0 = item->y1;
+	p_rect.x1 = item->x2;
+	p_rect.y1 = item->y2;
+
+	a_rect.x0 = x;
+	a_rect.y0 = y;
+	a_rect.x1 = x + width;
+	a_rect.y1 = y + height;
+
+	art_irect_intersect (&d_rect, &p_rect, &a_rect);
+	if (art_irect_empty (&d_rect))
+		return;
+
+	/* Create a temporary buffer and transform the pixbuf there */
+
+	w = d_rect.x1 - d_rect.x0;
+	h = d_rect.y1 - d_rect.y0;
+
+	buf = g_new0 (guchar, w * h * 4);
+	transform_pixbuf (buf,
+			  d_rect.x0, d_rect.y0,
+			  w, h,
+			  w * 4,
+			  priv->pixbuf, render_affine);
+
+	pixbuf = gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB,
+					   TRUE,
+					   8, w, h,
+					   w * 4,
+					   NULL, NULL);
+
+	gdk_pixbuf_render_to_drawable_alpha (pixbuf, drawable,
+					     0, 0,
+					     d_rect.x0 - x, d_rect.y0 - y,
+					     w, h,
+					     GDK_PIXBUF_ALPHA_FULL,
+					     0,
+					     GDK_RGB_DITHER_MAX,
+					     d_rect.x0, d_rect.y0);
+
+	gdk_pixbuf_unref (pixbuf);
+	g_free (buf);
+}
+
+/* Render handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	double i2c[6], render_affine[6];
+
+	gcp = GNOME_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (!priv->pixbuf)
+		return;
+
+	gnome_canvas_item_i2c_affine (item, i2c);
+	compute_render_affine (gcp, render_affine, i2c);
+        gnome_canvas_buf_ensure_buf (buf);
+
+
+	if ((fabs (render_affine[1]) < GNOME_CANVAS_EPSILON) &&
+	    (fabs (render_affine[2]) < GNOME_CANVAS_EPSILON) &&
+	    render_affine[0] > 0.0 &&
+	    render_affine[3] > 0.0)
+	  {
+	    GdkPixbuf *dest_pixbuf;
+	    int x0, y0, x1, y1;
+
+	    dest_pixbuf = gdk_pixbuf_new_from_data (buf->buf,
+						    GDK_COLORSPACE_RGB,
+						    FALSE,
+						    8,
+						    buf->rect.x1 - buf->rect.x0,
+						    buf->rect.y1 - buf->rect.y0,
+						    buf->buf_rowstride,
+						    NULL, NULL);
+
+
+	    x0 = floor (render_affine[4] - buf->rect.x0 + 0.5);
+	    y0 = floor (render_affine[5] - buf->rect.y0 + 0.5);
+
+	    x1 = x0 + floor (gdk_pixbuf_get_width (priv->pixbuf) * render_affine[0] + 0.5);
+	    y1 = y0 + floor (gdk_pixbuf_get_height (priv->pixbuf) * render_affine[3] + 0.5);
+
+	    x0 = MAX (x0, 0);
+	    x0 = MIN (x0, buf->rect.x1 - buf->rect.x0);
+	    y0 = MAX (y0, 0);
+	    y0 = MIN (y0, buf->rect.y1 - buf->rect.y0);
+
+	    x1 = MAX (x1, 0);
+	    x1 = MIN (x1, buf->rect.x1 - buf->rect.x0);
+	    y1 = MAX (y1, 0);
+	    y1 = MIN (y1, buf->rect.y1 - buf->rect.y0);
+
+	    gdk_pixbuf_composite  (priv->pixbuf,
+				   dest_pixbuf,
+				   x0, y0,
+				   x1 - x0, y1 - y0,
+				   render_affine[4] - buf->rect.x0,
+				   render_affine[5] - buf->rect.y0,
+				   render_affine[0],
+				   render_affine[3],
+				   GDK_INTERP_BILINEAR,
+				   255);
+
+	    gdk_pixbuf_unref (dest_pixbuf);
+	  }
+	else if (gdk_pixbuf_get_has_alpha(priv->pixbuf))
+		art_rgb_rgba_affine (buf->buf,
+				     buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+				     buf->buf_rowstride,
+				     gdk_pixbuf_get_pixels(priv->pixbuf),
+				     gdk_pixbuf_get_width(priv->pixbuf),
+				     gdk_pixbuf_get_height(priv->pixbuf),
+				     gdk_pixbuf_get_rowstride(priv->pixbuf),
+				     render_affine,
+				     ART_FILTER_NEAREST,
+				     NULL);
+	else
+		art_rgb_affine (buf->buf,
+				buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+				buf->buf_rowstride,
+				gdk_pixbuf_get_pixels(priv->pixbuf),
+				gdk_pixbuf_get_width(priv->pixbuf),
+				gdk_pixbuf_get_height(priv->pixbuf),
+				gdk_pixbuf_get_rowstride(priv->pixbuf),
+				render_affine,
+				ART_FILTER_NEAREST,
+				NULL);
+
+	buf->is_bg = 0;
+}
+
+
+
+/* Point handler for the pixbuf canvas item */
+static double
+gnome_canvas_pixbuf_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+			   GnomeCanvasItem **actual_item)
+{
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	double i2c[6], render_affine[6], inv[6];
+	ArtPoint c, p;
+	int px, py;
+	double no_hit;
+	GdkPixbuf *pixbuf;
+
+	gcp = GNOME_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+	pixbuf = priv->pixbuf;
+
+	*actual_item = item;
+
+	no_hit = item->canvas->pixels_per_unit * 2 + 10;
+
+	if (!priv->pixbuf)
+		return no_hit;
+
+	gnome_canvas_item_i2c_affine (item, i2c);
+	compute_render_affine (gcp, render_affine, i2c);
+	art_affine_invert (inv, render_affine);
+
+	c.x = cx;
+	c.y = cy;
+	art_affine_point (&p, &c, inv);
+	px = p.x;
+	py = p.y;
+
+	if (px < 0 || px >= gdk_pixbuf_get_width (pixbuf) ||
+	    py < 0 || py >= gdk_pixbuf_get_height (pixbuf))
+		return no_hit;
+
+	if (!gdk_pixbuf_get_has_alpha (pixbuf))
+		return 0.0;
+
+	return 0.0;
+}
+
+
+
+/* Bounds handler for the pixbuf canvas item */
+static void
+gnome_canvas_pixbuf_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	GnomeCanvasPixbuf *gcp;
+	PixbufPrivate *priv;
+	double i2c[6], viewport_affine[6];
+	ArtDRect rect;
+
+	gcp = GNOME_CANVAS_PIXBUF (item);
+	priv = gcp->priv;
+
+	if (!priv->pixbuf) {
+		*x1 = *y1 = *x2 = *y2 = 0.0;
+		return;
+	}
+
+	rect.x0 = 0.0;
+	rect.x1 = gdk_pixbuf_get_width (priv->pixbuf);
+
+	rect.y0 = 0.0;
+	rect.y1 = gdk_pixbuf_get_height (priv->pixbuf);
+
+	gnome_canvas_item_i2c_affine (item, i2c);
+	compute_viewport_affine (gcp, viewport_affine, i2c);
+	art_drect_affine_transform (&rect, &rect, viewport_affine);
+
+	*x1 = rect.x0;
+	*y1 = rect.y0;
+	*x2 = rect.x1;
+	*y2 = rect.y1;
+}
diff --git a/src/libgnomecanvas/gnome-canvas-pixbuf.h b/src/libgnomecanvas/gnome-canvas-pixbuf.h
new file mode 100644
index 0000000..c271924
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-pixbuf.h
@@ -0,0 +1,60 @@
+/* GNOME libraries - GdkPixbuf item for the GNOME canvas
+ *
+ * Copyright (C) 1999 The Free Software Foundation
+ *
+ * Author: Federico Mena-Quintero <federico gimp org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GNOME_CANVAS_PIXBUF_H
+#define GNOME_CANVAS_PIXBUF_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+G_BEGIN_DECLS
+
+
+
+#define GNOME_TYPE_CANVAS_PIXBUF            (gnome_canvas_pixbuf_get_type ())
+#define GNOME_CANVAS_PIXBUF(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbuf))
+#define GNOME_CANVAS_PIXBUF_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbufClass))
+#define GNOME_IS_CANVAS_PIXBUF(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_PIXBUF))
+#define GNOME_IS_CANVAS_PIXBUF_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_PIXBUF))
+#define GNOME_CANVAS_PIXBUF_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_PIXBUF, GnomeCanvasPixbufClass))
+
+
+typedef struct _GnomeCanvasPixbuf GnomeCanvasPixbuf;
+typedef struct _GnomeCanvasPixbufClass GnomeCanvasPixbufClass;
+
+struct _GnomeCanvasPixbuf {
+	GnomeCanvasItem item;
+
+	/* Private data */
+	gpointer priv;
+};
+
+struct _GnomeCanvasPixbufClass {
+	GnomeCanvasItemClass parent_class;
+};
+
+
+GType gnome_canvas_pixbuf_get_type (void) G_GNUC_CONST;
+
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-polygon.c b/src/libgnomecanvas/gnome-canvas-polygon.c
new file mode 100644
index 0000000..20c6c0f
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-polygon.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Polygon item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ *         Rusty Conover <rconover bangtail net>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "libart_lgpl/art_vpath.h"
+#include "libart_lgpl/art_svp.h"
+#include "libart_lgpl/art_svp_vpath.h"
+#include "libart_lgpl/art_svp_vpath_stroke.h"
+#include "libgnomecanvas.h"
+
+#include "gnome-canvas-shape.h"
+
+
+#define NUM_STATIC_POINTS 256	/* Number of static points to use to avoid allocating arrays */
+
+
+enum {
+	PROP_0,
+	PROP_POINTS
+};
+
+
+static void gnome_canvas_polygon_class_init (GnomeCanvasPolygonClass *class);
+static void gnome_canvas_polygon_init       (GnomeCanvasPolygon      *poly);
+static void gnome_canvas_polygon_destroy    (GtkObject               *object);
+static void gnome_canvas_polygon_set_property (GObject              *object,
+					       guint                 param_id,
+					       const GValue         *value,
+					       GParamSpec           *pspec);
+static void gnome_canvas_polygon_get_property (GObject              *object,
+					       guint                 param_id,
+					       GValue               *value,
+					       GParamSpec           *pspec);
+
+static void gnome_canvas_polygon_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+static GnomeCanvasItemClass *parent_class;
+
+GType
+gnome_canvas_polygon_get_type (void)
+{
+	static GType polygon_type;
+
+	if (!polygon_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasPolygonClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_polygon_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasPolygon),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_polygon_init,
+			NULL			/* value_table */
+		};
+
+		polygon_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasPolygon",
+						       &object_info, 0);
+	}
+
+	return polygon_type;
+}
+
+static void
+gnome_canvas_polygon_class_init (GnomeCanvasPolygonClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_polygon_set_property;
+	gobject_class->get_property = gnome_canvas_polygon_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_POINTS,
+                 g_param_spec_boxed ("points", NULL, NULL,
+				     GNOME_TYPE_CANVAS_POINTS,
+				     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_polygon_destroy;
+
+	item_class->update = gnome_canvas_polygon_update;
+}
+
+static void
+gnome_canvas_polygon_init (GnomeCanvasPolygon *poly)
+{
+	poly->path_def = NULL;
+}
+
+static void
+gnome_canvas_polygon_destroy (GtkObject *object)
+{
+	GnomeCanvasPolygon *poly;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+	poly = GNOME_CANVAS_POLYGON (object);
+
+	/* remember, destroy can be run multiple times! */
+
+	if(poly->path_def)
+		gnome_canvas_path_def_unref(poly->path_def);
+
+	poly->path_def = NULL;
+
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+set_points (GnomeCanvasPolygon *poly, GnomeCanvasPoints *points)
+{
+	int i;
+
+
+	if (poly->path_def)
+		gnome_canvas_path_def_unref(poly->path_def);
+
+	if (!points) {
+		poly->path_def = gnome_canvas_path_def_new();
+		gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (poly), poly->path_def);
+		return;
+	}
+
+
+	/* Optomize the path def to the number of points */
+	poly->path_def = gnome_canvas_path_def_new_sized(points->num_points+1);
+
+#if 0
+	/* No need for explicit duplicate, as closepaths does it for us (Lauris) */
+	/* See if we need to duplicate the first point */
+	duplicate = ((points->coords[0] != points->coords[2 * points->num_points - 2])
+		     || (points->coords[1] != points->coords[2 * points->num_points - 1]));
+#endif
+
+	
+	gnome_canvas_path_def_moveto (poly->path_def, points->coords[0], points->coords[1]);
+	
+	for (i = 1; i < points->num_points; i++) {
+		gnome_canvas_path_def_lineto(poly->path_def, points->coords[i * 2], points->coords[(i * 2) + 1]);
+	}
+
+	gnome_canvas_path_def_closepath (poly->path_def);
+
+	gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (poly), poly->path_def);
+}
+
+
+static void
+gnome_canvas_polygon_set_property (GObject              *object,
+				   guint                 param_id,
+				   const GValue         *value,
+				   GParamSpec           *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasPolygon *poly;
+	GnomeCanvasPoints *points;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	poly = GNOME_CANVAS_POLYGON (object);
+
+	switch (param_id) {
+	case PROP_POINTS:
+		points = g_value_get_boxed (value);
+
+		set_points (poly, points);
+
+		gnome_canvas_item_request_update (item);
+		break;
+ 	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+
+static void
+gnome_canvas_polygon_get_property (GObject              *object,
+				   guint                 param_id,
+				   GValue               *value,
+				   GParamSpec           *pspec)
+{
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_POLYGON (object));
+
+	switch (param_id) {
+	case PROP_POINTS:
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+
+static void
+gnome_canvas_polygon_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	/* Since the path has already been defined just pass the update up. */
+
+	if (parent_class->update)
+		(* parent_class->update) (item, affine, clip_path, flags);
+}
diff --git a/src/libgnomecanvas/gnome-canvas-polygon.h b/src/libgnomecanvas/gnome-canvas-polygon.h
new file mode 100644
index 0000000..3fb8267
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-polygon.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Polygon item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ *         Rusty Conover <rconover bangtail net>
+ */
+
+#ifndef GNOME_CANVAS_POLYGON_H
+#define GNOME_CANVAS_POLYGON_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-shape.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Polygon item for the canvas.  A polygon is a bit different from rectangles and ellipses in that
+ * points inside it will always be considered "inside", even if the fill color is not set.  If you
+ * want to have a hollow polygon, use a line item instead.
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * points		GnomeCanvasPoints*	RW		Pointer to a GnomeCanvasPoints structure.
+ *								This can be created by a call to
+ *								gnome_canvas_points_new() (in gnome-canvas-util.h).
+ *								X coordinates are in the even indices of the
+ *								points->coords array, Y coordinates are in
+ *								the odd indices.
+ */
+
+#define GNOME_TYPE_CANVAS_POLYGON            (gnome_canvas_polygon_get_type ())
+#define GNOME_CANVAS_POLYGON(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygon))
+#define GNOME_CANVAS_POLYGON_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygonClass))
+#define GNOME_IS_CANVAS_POLYGON(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_POLYGON))
+#define GNOME_IS_CANVAS_POLYGON_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_POLYGON))
+#define GNOME_CANVAS_POLYGON_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_POLYGON, GnomeCanvasPolygonClass))
+
+
+typedef struct _GnomeCanvasPolygon GnomeCanvasPolygon;
+typedef struct _GnomeCanvasPolygonClass GnomeCanvasPolygonClass;
+
+struct _GnomeCanvasPolygon {
+	GnomeCanvasShape item;
+
+	GnomeCanvasPathDef *path_def;
+};
+
+struct _GnomeCanvasPolygonClass {
+	GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_polygon_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-rect-ellipse.c b/src/libgnomecanvas/gnome-canvas-rect-ellipse.c
new file mode 100644
index 0000000..0513e83
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-rect-ellipse.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Rectangle and ellipse item types for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Rusty Conover <rconover bangtail net>
+ */
+
+#include <config.h>
+#include <math.h>
+#include "gnome-canvas-rect-ellipse.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-shape.h"
+
+
+#include "libart_lgpl/art_vpath.h"
+#include "libart_lgpl/art_svp.h"
+#include "libart_lgpl/art_svp_vpath.h"
+#include "libart_lgpl/art_rgb_svp.h"
+
+/* Base class for rectangle and ellipse item types */
+
+#define noVERBOSE
+
+enum {
+	PROP_0,
+	PROP_X1,
+	PROP_Y1,
+	PROP_X2,
+	PROP_Y2
+};
+
+
+static void gnome_canvas_re_class_init (GnomeCanvasREClass *class);
+static void gnome_canvas_re_init       (GnomeCanvasRE      *re);
+static void gnome_canvas_re_destroy    (GtkObject          *object);
+static void gnome_canvas_re_set_property (GObject              *object,
+					  guint                 param_id,
+					  const GValue         *value,
+					  GParamSpec           *pspec);
+static void gnome_canvas_re_get_property (GObject              *object,
+					  guint                 param_id,
+					  GValue               *value,
+					  GParamSpec           *pspec);
+
+static void gnome_canvas_rect_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void gnome_canvas_ellipse_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+static GnomeCanvasItemClass *re_parent_class;
+
+
+GType
+gnome_canvas_re_get_type (void)
+{
+	static GType re_type;
+
+	if (!re_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasREClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_re_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasRE),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_re_init,
+			NULL			/* value_table */
+		};
+
+		re_type = g_type_register_static (GNOME_TYPE_CANVAS_SHAPE, "GnomeCanvasRE",
+						  &object_info, 0);
+	}
+
+	return re_type;
+}
+
+static void
+gnome_canvas_re_class_init (GnomeCanvasREClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+
+	re_parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_re_set_property;
+	gobject_class->get_property = gnome_canvas_re_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X1,
+                 g_param_spec_double ("x1", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y1,
+                 g_param_spec_double ("y1", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X2,
+                 g_param_spec_double ("x2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y2,
+                 g_param_spec_double ("y2", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_re_destroy;
+}
+
+static void
+gnome_canvas_re_init (GnomeCanvasRE *re)
+{
+	re->x1 = 0.0;
+	re->y1 = 0.0;
+	re->x2 = 0.0;
+	re->y2 = 0.0;
+	re->path_dirty = 0;
+}
+
+static void
+gnome_canvas_re_destroy (GtkObject *object)
+{
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+	if (GTK_OBJECT_CLASS (re_parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (re_parent_class)->destroy) (object);
+}
+
+static void
+gnome_canvas_re_set_property (GObject              *object,
+			      guint                 param_id,
+			      const GValue         *value,
+			      GParamSpec           *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasRE *re;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	re = GNOME_CANVAS_RE (object);
+
+	switch (param_id) {
+	case PROP_X1:
+		re->x1 = g_value_get_double (value);
+		re->path_dirty = 1;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y1:
+		re->y1 = g_value_get_double (value);
+		re->path_dirty = 1;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_X2:
+		re->x2 = g_value_get_double (value);
+		re->path_dirty = 1;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_Y2:
+		re->y2 = g_value_get_double (value);
+		re->path_dirty = 1;
+		gnome_canvas_item_request_update (item);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_re_get_property (GObject              *object,
+			      guint                 param_id,
+			      GValue               *value,
+			      GParamSpec           *pspec)
+{
+	GnomeCanvasRE *re;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_RE (object));
+
+	re = GNOME_CANVAS_RE (object);
+
+	switch (param_id) {
+	case PROP_X1:
+		g_value_set_double (value,  re->x1);
+		break;
+
+	case PROP_Y1:
+		g_value_set_double (value,  re->y1);
+		break;
+
+	case PROP_X2:
+		g_value_set_double (value,  re->x2);
+		break;
+
+	case PROP_Y2:
+		g_value_set_double (value,  re->y2);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* Rectangle item */
+static void gnome_canvas_rect_class_init (GnomeCanvasRectClass *class);
+
+
+
+GType
+gnome_canvas_rect_get_type (void)
+{
+	static GType rect_type;
+
+	if (!rect_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasRectClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_rect_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasRect),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) NULL,
+			NULL			/* value_table */
+		};
+
+		rect_type = g_type_register_static (GNOME_TYPE_CANVAS_RE, "GnomeCanvasRect",
+						    &object_info, 0);
+	}
+
+	return rect_type;
+}
+
+static void
+gnome_canvas_rect_class_init (GnomeCanvasRectClass *class)
+{
+	GnomeCanvasItemClass *item_class;
+
+	item_class = (GnomeCanvasItemClass *) class;
+
+	item_class->update = gnome_canvas_rect_update;
+}
+
+static void
+gnome_canvas_rect_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags)
+{	GnomeCanvasRE *re;	
+
+	GnomeCanvasPathDef *path_def;
+
+	re = GNOME_CANVAS_RE(item);
+
+	if (re->path_dirty) {		
+		path_def = gnome_canvas_path_def_new ();
+		
+		gnome_canvas_path_def_moveto(path_def, re->x1, re->y1);
+		gnome_canvas_path_def_lineto(path_def, re->x2, re->y1);
+		gnome_canvas_path_def_lineto(path_def, re->x2, re->y2);
+		gnome_canvas_path_def_lineto(path_def, re->x1, re->y2);
+		gnome_canvas_path_def_lineto(path_def, re->x1, re->y1);		
+		gnome_canvas_path_def_closepath_current(path_def);		
+		gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (item), path_def);
+		gnome_canvas_path_def_unref(path_def);
+		re->path_dirty = 0;
+	}
+
+	if (re_parent_class->update)
+		(* re_parent_class->update) (item, affine, clip_path, flags);
+}
+
+/* Ellipse item */
+
+
+static void gnome_canvas_ellipse_class_init (GnomeCanvasEllipseClass *class);
+
+
+GType
+gnome_canvas_ellipse_get_type (void)
+{
+	static GType ellipse_type;
+
+	if (!ellipse_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasEllipseClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_ellipse_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasEllipse),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) NULL,
+			NULL			/* value_table */
+		};
+
+		ellipse_type = g_type_register_static (GNOME_TYPE_CANVAS_RE, "GnomeCanvasEllipse",
+						       &object_info, 0);
+	}
+
+	return ellipse_type;
+}
+
+static void
+gnome_canvas_ellipse_class_init (GnomeCanvasEllipseClass *class)
+{
+	GnomeCanvasItemClass *item_class;
+
+	item_class = (GnomeCanvasItemClass *) class;
+
+	item_class->update = gnome_canvas_ellipse_update;
+}
+
+#define N_PTS 90
+
+static void
+gnome_canvas_ellipse_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags) {
+	GnomeCanvasPathDef *path_def;
+	GnomeCanvasRE *re;
+
+	re = GNOME_CANVAS_RE(item);
+
+	if (re->path_dirty) {
+		gdouble cx, cy, rx, ry;
+		gdouble beta = 0.26521648983954400922; /* 4*(1-cos(pi/8))/(3*sin(pi/8)) */
+		gdouble sincosA = 0.70710678118654752440; /* sin (pi/4), cos (pi/4) */
+		gdouble dx1, dy1, dx2, dy2;
+		gdouble mx, my;
+
+		path_def = gnome_canvas_path_def_new();
+
+		cx = (re->x2 + re->x1) * 0.5;
+		cy = (re->y2 + re->y1) * 0.5;
+		rx = re->x2 - cx;
+		ry = re->y2 - cy;
+
+		dx1 = beta * rx;
+		dy1 = beta * ry;
+		dx2 = beta * rx * sincosA;
+		dy2 = beta * ry * sincosA;
+		mx = rx * sincosA;
+		my = ry * sincosA;
+
+		gnome_canvas_path_def_moveto (path_def, cx + rx, cy);
+		gnome_canvas_path_def_curveto (path_def,
+					       cx + rx, cy - dy1,
+					       cx + mx + dx2, cy - my + dy2,
+					       cx + mx, cy - my);
+		gnome_canvas_path_def_curveto (path_def,
+					       cx + mx - dx2, cy - my - dy2,
+					       cx + dx1, cy - ry,
+					       cx, cy - ry);
+		gnome_canvas_path_def_curveto (path_def,
+					       cx - dx1, cy - ry,
+					       cx - mx + dx2, cy - my - dy2,
+					       cx - mx, cy - my);
+		gnome_canvas_path_def_curveto (path_def,
+					       cx - mx - dx2, cy - my + dy2,
+					       cx - rx, cy - dy1,
+					       cx - rx, cy);
+		
+		gnome_canvas_path_def_curveto (path_def,
+					       cx - rx, cy + dy1,
+					       cx - mx - dx2, cy + my - dy2,
+					       cx - mx, cy + my);
+		gnome_canvas_path_def_curveto (path_def,
+					       cx - mx + dx2, cy + my + dy2,
+					       cx - dx1, cy + ry,
+					       cx, cy + ry);
+		gnome_canvas_path_def_curveto (path_def,
+					       cx + dx1, cy + ry,
+					       cx + mx - dx2, cy + my + dy2,
+					       cx + mx, cy + my);
+		gnome_canvas_path_def_curveto (path_def,
+					       cx + mx + dx2, cy + my - dy2,
+					       cx + rx, cy + dy1,
+					       cx + rx, cy);
+		
+		gnome_canvas_path_def_closepath_current(path_def);
+		
+		gnome_canvas_shape_set_path_def (GNOME_CANVAS_SHAPE (item), path_def);
+		gnome_canvas_path_def_unref(path_def);
+		re->path_dirty = 0;
+	}
+
+	if (re_parent_class->update)
+		(* re_parent_class->update) (item, affine, clip_path, flags);
+}
diff --git a/src/libgnomecanvas/gnome-canvas-rect-ellipse.h b/src/libgnomecanvas/gnome-canvas-rect-ellipse.h
new file mode 100644
index 0000000..a3a98dd
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-rect-ellipse.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Rectangle and ellipse item types for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef GNOME_CANVAS_RECT_ELLIPSE_H
+#define GNOME_CANVAS_RECT_ELLIPSE_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libgnomecanvas/gnome-canvas-shape.h>
+
+#include <libart_lgpl/art_svp.h>
+
+G_BEGIN_DECLS
+
+
+/* Base class for rectangle and ellipse item types.  These are defined by their top-left and
+ * bottom-right corners.  Rectangles and ellipses share the following arguments:
+ *
+ * name			type		read/write	description
+ * ------------------------------------------------------------------------------------------
+ * x1			double		RW		Leftmost coordinate of rectangle or ellipse
+ * y1			double		RW		Topmost coordinate of rectangle or ellipse
+ * x2			double		RW		Rightmost coordinate of rectangle or ellipse
+ * y2			double		RW		Bottommost coordinate of rectangle or ellipse
+ * fill_color		string		W		X color specification for fill color,
+ *							or NULL pointer for no color (transparent)
+ * fill_color_gdk	GdkColor*	RW		Allocated GdkColor for fill
+ * outline_color	string		W		X color specification for outline color,
+ *							or NULL pointer for no color (transparent)
+ * outline_color_gdk	GdkColor*	RW		Allocated GdkColor for outline
+ * fill_stipple		GdkBitmap*	RW		Stipple pattern for fill
+ * outline_stipple	GdkBitmap*	RW		Stipple pattern for outline
+ * width_pixels		uint		RW		Width of the outline in pixels.  The outline will
+ *							not be scaled when the canvas zoom factor is changed.
+ * width_units		double		RW		Width of the outline in canvas units.  The outline
+ *							will be scaled when the canvas zoom factor is changed.
+ */
+
+
+#define GNOME_TYPE_CANVAS_RE            (gnome_canvas_re_get_type ())
+#define GNOME_CANVAS_RE(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_RE, GnomeCanvasRE))
+#define GNOME_CANVAS_RE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RE, GnomeCanvasREClass))
+#define GNOME_IS_CANVAS_RE(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_RE))
+#define GNOME_IS_CANVAS_RE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RE))
+#define GNOME_CANVAS_RE_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RE, GnomeCanvasREClass))
+
+
+typedef struct _GnomeCanvasRE      GnomeCanvasRE;
+typedef struct _GnomeCanvasREClass GnomeCanvasREClass;
+
+struct _GnomeCanvasRE {
+	GnomeCanvasShape item;
+
+	double x1, y1, x2, y2;		/* Corners of item */
+
+	unsigned int path_dirty : 1;
+};
+
+struct _GnomeCanvasREClass {
+	GnomeCanvasShapeClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_re_get_type (void) G_GNUC_CONST;
+
+
+/* Rectangle item.  No configurable or queryable arguments are available (use those in
+ * GnomeCanvasRE).
+ */
+
+
+#define GNOME_TYPE_CANVAS_RECT            (gnome_canvas_rect_get_type ())
+#define GNOME_CANVAS_RECT(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRect))
+#define GNOME_CANVAS_RECT_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRectClass))
+#define GNOME_IS_CANVAS_RECT(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_RECT))
+#define GNOME_IS_CANVAS_RECT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RECT))
+#define GNOME_CANVAS_RECT_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RECT, GnomeCanvasRectClass))
+
+
+typedef struct _GnomeCanvasRect GnomeCanvasRect;
+typedef struct _GnomeCanvasRectClass GnomeCanvasRectClass;
+
+struct _GnomeCanvasRect {
+	GnomeCanvasRE re;
+};
+
+struct _GnomeCanvasRectClass {
+	GnomeCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_rect_get_type (void) G_GNUC_CONST;
+
+
+/* Ellipse item.  No configurable or queryable arguments are available (use those in
+ * GnomeCanvasRE).
+ */
+
+
+#define GNOME_TYPE_CANVAS_ELLIPSE            (gnome_canvas_ellipse_get_type ())
+#define GNOME_CANVAS_ELLIPSE(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipse))
+#define GNOME_CANVAS_ELLIPSE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipseClass))
+#define GNOME_IS_CANVAS_ELLIPSE(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_ELLIPSE))
+#define GNOME_IS_CANVAS_ELLIPSE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ELLIPSE))
+#define GNOME_CANVAS_ELLIPSE_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_ELLIPSE, GnomeCanvasEllipseClass))
+
+
+typedef struct _GnomeCanvasEllipse GnomeCanvasEllipse;
+typedef struct _GnomeCanvasEllipseClass GnomeCanvasEllipseClass;
+
+struct _GnomeCanvasEllipse {
+	GnomeCanvasRE re;
+};
+
+struct _GnomeCanvasEllipseClass {
+	GnomeCanvasREClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_ellipse_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-rich-text.c b/src/libgnomecanvas/gnome-canvas-rich-text.c
new file mode 100644
index 0000000..b166bce
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-rich-text.c
@@ -0,0 +1,2201 @@
+/* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
+ * from GtkTextView.
+ *
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Copyright (c) 2001 Joe Shaw
+ *
+ * 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 3 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 <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtktextdisplay.h>
+#include <gtk/gtkmain.h>
+
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-rich-text.h"
+#include "gnome-canvas-i18n.h"
+
+struct _GnomeCanvasRichTextPrivate {
+	GtkTextLayout *layout;
+	GtkTextBuffer *buffer;
+
+	char *text;
+
+	/* Position at anchor */
+	double x, y;
+	/* Dimensions */
+	double width, height;
+	/* Top-left canvas coordinates for text */
+	int cx, cy;
+
+	gboolean cursor_visible;
+	gboolean cursor_blink;
+	gboolean editable;
+	gboolean visible;
+	gboolean grow_height;
+	GtkWrapMode wrap_mode;
+	GtkJustification justification;
+	GtkTextDirection direction;
+	GtkAnchorType anchor;
+	int pixels_above_lines;
+	int pixels_below_lines;
+	int pixels_inside_wrap;
+	int left_margin;
+	int right_margin;
+	int indent;
+
+	guint preblink_timeout;
+	guint blink_timeout;
+
+	guint selection_drag_handler;
+
+	gint drag_start_x;
+	gint drag_start_y;
+
+	gboolean just_selected_element;
+
+	int clicks;
+	guint click_timeout;
+};
+
+enum {
+	PROP_0,
+	PROP_TEXT,
+	PROP_X,
+	PROP_Y,
+	PROP_WIDTH,
+	PROP_HEIGHT,
+	PROP_EDITABLE,
+	PROP_VISIBLE,
+	PROP_CURSOR_VISIBLE,
+	PROP_CURSOR_BLINK,
+	PROP_GROW_HEIGHT,
+	PROP_WRAP_MODE,
+	PROP_JUSTIFICATION,
+	PROP_DIRECTION,
+	PROP_ANCHOR,
+	PROP_PIXELS_ABOVE_LINES,
+	PROP_PIXELS_BELOW_LINES,
+	PROP_PIXELS_INSIDE_WRAP,
+	PROP_LEFT_MARGIN,
+	PROP_RIGHT_MARGIN,
+	PROP_INDENT
+};
+
+enum {
+	TAG_CHANGED,
+	LAST_SIGNAL
+};
+
+static GnomeCanvasItemClass *parent_class;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass);
+static void gnome_canvas_rich_text_init(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_set_property(GObject *object, guint property_id,
+						const GValue *value, GParamSpec *pspec);
+static void gnome_canvas_rich_text_get_property(GObject *object, guint property_id,
+						GValue *value, GParamSpec *pspec);
+static void gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
+					  ArtSVP *clip_path, int flags);
+static void gnome_canvas_rich_text_realize(GnomeCanvasItem *item);
+static void gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item);
+static double gnome_canvas_rich_text_point(GnomeCanvasItem *item,
+					   double x, double y,
+					   int cx, int cy,
+					   GnomeCanvasItem **actual_item);
+static void gnome_canvas_rich_text_draw(GnomeCanvasItem *item,
+					GdkDrawable *drawable,
+					int x, int y, int width, int height);
+static void gnome_canvas_rich_text_render(GnomeCanvasItem *item,
+					  GnomeCanvasBuf *buf);
+static gint gnome_canvas_rich_text_event(GnomeCanvasItem *item,
+					 GdkEvent *event);
+static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *text, double *px1, double *py1,
+	   double *px2, double *py2);
+
+static void gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text, gboolean delay);
+static void gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text);
+static void gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
+					       GtkMovementStep step,
+					       gint count,
+					       gboolean extend_selection);
+
+
+
+static GtkTextBuffer *get_buffer(GnomeCanvasRichText *text);
+static gint blink_cb(gpointer data);
+
+#define PREBLINK_TIME 300
+#define CURSOR_ON_TIME 800
+#define CURSOR_OFF_TIME 400
+
+GType
+gnome_canvas_rich_text_get_type(void)
+{
+	static GType rich_text_type;
+
+	if (!rich_text_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasRichTextClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_rich_text_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasRichText),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_rich_text_init,
+			NULL			/* value_table */
+		};
+
+		rich_text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasRichText",
+							 &object_info, 0);
+	}
+
+	return rich_text_type;
+}
+
+static void
+gnome_canvas_rich_text_finalize(GObject *object)
+{
+	GnomeCanvasRichText *text;
+
+	text = GNOME_CANVAS_RICH_TEXT(object);
+
+	g_free (text->_priv);
+	text->_priv = NULL;
+
+	if (G_OBJECT_CLASS (parent_class)->finalize)
+		G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gnome_canvas_rich_text_class_init(GnomeCanvasRichTextClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+	GtkObjectClass *object_class = GTK_OBJECT_CLASS(klass);
+	GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS(klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = gnome_canvas_rich_text_set_property;
+	gobject_class->get_property = gnome_canvas_rich_text_get_property;
+
+	g_object_class_install_property (
+		gobject_class,
+		PROP_TEXT,
+		g_param_spec_string ("text",
+				     "Text",
+				     "Text to display",
+				     NULL,
+				     G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_X,
+		g_param_spec_double ("x",
+				     "X",
+				     "X position",
+				     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				     G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_Y,
+		g_param_spec_double ("y",
+				     "Y",
+				     "Y position",
+				     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				     G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_WIDTH,
+		g_param_spec_double ("width",
+				     "Width",
+				     "Width for text box",
+				     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				     G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_HEIGHT,
+		g_param_spec_double ("height",
+				     "Height",
+				     "Height for text box",
+				     -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				     G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_EDITABLE,
+		g_param_spec_boolean ("editable",
+				      "Editable",
+				      "Is this rich text item editable?",
+				      TRUE,
+				      G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_VISIBLE,
+		g_param_spec_boolean ("visible",
+				      "Visible",
+				      "Is this rich text item visible?",
+				      TRUE,
+				      G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_CURSOR_VISIBLE,
+		g_param_spec_boolean ("cursor_visible",
+				      "Cursor Visible",
+				      "Is the cursor visible in this rich text item?",
+				      TRUE,
+				      G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_CURSOR_BLINK,
+		g_param_spec_boolean ("cursor_blink",
+				      "Cursor Blink",
+				      "Does the cursor blink in this rich text item?",
+				      TRUE,
+				      G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_GROW_HEIGHT,
+		g_param_spec_boolean ("grow_height",
+				      "Grow Height",
+				      "Should the text box height grow if the text does not fit?",
+				      FALSE,
+				      G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_WRAP_MODE,
+		g_param_spec_enum ("wrap_mode",
+				   "Wrap Mode",
+				   "Wrap mode for multiline text",
+				   GTK_TYPE_WRAP_MODE,
+				   GTK_WRAP_WORD,
+				   G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_JUSTIFICATION,
+		g_param_spec_enum ("justification",
+				   "Justification",
+				   "Justification mode",
+				   GTK_TYPE_JUSTIFICATION,
+				   GTK_JUSTIFY_LEFT,
+				   G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_DIRECTION,
+		g_param_spec_enum ("direction",
+				   "Direction",
+				   "Text direction",
+				   GTK_TYPE_DIRECTION_TYPE,
+				   gtk_widget_get_default_direction (),
+				   G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_ANCHOR,
+		g_param_spec_enum ("anchor",
+				   "Anchor",
+				   "Anchor point for text",
+				   GTK_TYPE_ANCHOR_TYPE,
+				   GTK_ANCHOR_NW,
+				   G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_PIXELS_ABOVE_LINES,
+		g_param_spec_int ("pixels_above_lines",
+				  "Pixels Above Lines",
+				  "Number of pixels to put above lines",
+				  G_MININT, G_MAXINT,
+				  0,
+				  G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_PIXELS_BELOW_LINES,
+		g_param_spec_int ("pixels_below_lines",
+				  "Pixels Below Lines",
+				  "Number of pixels to put below lines",
+				  G_MININT, G_MAXINT,
+				  0,
+				  G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_PIXELS_INSIDE_WRAP,
+		g_param_spec_int ("pixels_inside_wrap",
+				  "Pixels Inside Wrap",
+				  "Number of pixels to put inside the wrap",
+				  G_MININT, G_MAXINT,
+				  0,
+				  G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_LEFT_MARGIN,
+		g_param_spec_int ("left_margin",
+				  "Left Margin",
+				  "Number of pixels in the left margin",
+				  G_MININT, G_MAXINT,
+				  0,
+				  G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_RIGHT_MARGIN,
+		g_param_spec_int ("right_margin",
+				  "Right Margin",
+				  "Number of pixels in the right margin",
+				  G_MININT, G_MAXINT,
+				  0,
+				  G_PARAM_READWRITE));
+	g_object_class_install_property (
+		gobject_class,
+		PROP_INDENT,
+		g_param_spec_int ("indent",
+				  "Indentation",
+				  "Number of pixels for indentation",
+				  G_MININT, G_MAXINT,
+				  0,
+				  G_PARAM_READWRITE));
+
+	/* Signals */
+	signals[TAG_CHANGED] = g_signal_new(
+		"tag_changed",
+		G_TYPE_FROM_CLASS(object_class),
+		G_SIGNAL_RUN_LAST,
+		G_STRUCT_OFFSET(GnomeCanvasRichTextClass, tag_changed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__OBJECT,
+		G_TYPE_NONE, 1,
+		G_TYPE_OBJECT);
+
+	gobject_class->finalize = gnome_canvas_rich_text_finalize;
+
+	item_class->update = gnome_canvas_rich_text_update;
+	item_class->realize = gnome_canvas_rich_text_realize;
+	item_class->unrealize = gnome_canvas_rich_text_unrealize;
+	item_class->draw = gnome_canvas_rich_text_draw;
+	item_class->point = gnome_canvas_rich_text_point;
+	item_class->render = gnome_canvas_rich_text_render;
+	item_class->event = gnome_canvas_rich_text_event;
+	item_class->bounds = gnome_canvas_rich_text_get_bounds;
+} /* gnome_canvas_rich_text_class_init */
+
+static void
+gnome_canvas_rich_text_init(GnomeCanvasRichText *text)
+{
+#if 0
+	GtkObject *object = GTK_OBJECT(text);
+
+	object->flags |= GNOME_CANVAS_ITEM_ALWAYS_REDRAW;
+#endif
+	text->_priv = g_new0(GnomeCanvasRichTextPrivate, 1);
+
+	/* Try to set some sane defaults */
+	text->_priv->cursor_visible = TRUE;
+	text->_priv->cursor_blink = TRUE;
+	text->_priv->editable = TRUE;
+	text->_priv->visible = TRUE;
+	text->_priv->grow_height = FALSE;
+	text->_priv->wrap_mode = GTK_WRAP_WORD;
+	text->_priv->justification = GTK_JUSTIFY_LEFT;
+	text->_priv->direction = gtk_widget_get_default_direction();
+	text->_priv->anchor = GTK_ANCHOR_NW;
+
+	text->_priv->blink_timeout = 0;
+	text->_priv->preblink_timeout = 0;
+
+	text->_priv->clicks = 0;
+	text->_priv->click_timeout = 0;
+} /* gnome_canvas_rich_text_init */
+
+static void
+gnome_canvas_rich_text_set_property (GObject *object, guint property_id,
+				     const GValue *value, GParamSpec *pspec)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
+
+	switch (property_id) {
+	case PROP_TEXT:
+		if (text->_priv->text)
+			g_free(text->_priv->text);
+
+		text->_priv->text = g_value_dup_string (value);
+
+		gtk_text_buffer_set_text(
+			get_buffer(text), text->_priv->text, strlen(text->_priv->text));
+
+		break;
+	case PROP_X:
+		text->_priv->x = g_value_get_double (value);
+		break;
+	case PROP_Y:
+		text->_priv->y = g_value_get_double (value);
+		break;
+	case PROP_WIDTH:
+		text->_priv->width = g_value_get_double (value);
+		break;
+	case PROP_HEIGHT:
+		text->_priv->height = g_value_get_double (value);
+		break;
+	case PROP_EDITABLE:
+		text->_priv->editable = g_value_get_boolean (value);
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->editable =
+				text->_priv->editable;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_VISIBLE:
+		text->_priv->visible = g_value_get_boolean (value);
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->invisible =
+				!text->_priv->visible;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_CURSOR_VISIBLE:
+		text->_priv->cursor_visible = g_value_get_boolean (value);
+		if (text->_priv->layout) {
+			gtk_text_layout_set_cursor_visible(
+				text->_priv->layout, text->_priv->cursor_visible);
+
+			if (text->_priv->cursor_visible && text->_priv->cursor_blink) {
+				gnome_canvas_rich_text_start_cursor_blink(
+					text, FALSE);
+			}
+			else
+				gnome_canvas_rich_text_stop_cursor_blink(text);
+		}
+		break;
+	case PROP_CURSOR_BLINK:
+		text->_priv->cursor_blink = g_value_get_boolean (value);
+		if (text->_priv->layout && text->_priv->cursor_visible) {
+			if (text->_priv->cursor_blink && !text->_priv->blink_timeout) {
+				gnome_canvas_rich_text_start_cursor_blink(
+					text, FALSE);
+			}
+			else if (!text->_priv->cursor_blink && text->_priv->blink_timeout) {
+				gnome_canvas_rich_text_stop_cursor_blink(text);
+				gtk_text_layout_set_cursor_visible(
+					text->_priv->layout, TRUE);
+			}
+		}
+		break;
+	case PROP_GROW_HEIGHT:
+		text->_priv->grow_height = g_value_get_boolean (value);
+		/* FIXME: Recalc here */
+		break;
+	case PROP_WRAP_MODE:
+		text->_priv->wrap_mode = g_value_get_enum (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->wrap_mode =
+				text->_priv->wrap_mode;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_JUSTIFICATION:
+		text->_priv->justification = g_value_get_enum (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->justification =
+				text->_priv->justification;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_DIRECTION:
+		text->_priv->direction = g_value_get_enum (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->direction =
+				text->_priv->direction;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_ANCHOR:
+		text->_priv->anchor = g_value_get_enum (value);
+		break;
+	case PROP_PIXELS_ABOVE_LINES:
+		text->_priv->pixels_above_lines = g_value_get_int (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->pixels_above_lines =
+				text->_priv->pixels_above_lines;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_PIXELS_BELOW_LINES:
+		text->_priv->pixels_below_lines = g_value_get_int (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->pixels_below_lines =
+				text->_priv->pixels_below_lines;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_PIXELS_INSIDE_WRAP:
+		text->_priv->pixels_inside_wrap = g_value_get_int (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->pixels_inside_wrap =
+				text->_priv->pixels_inside_wrap;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_LEFT_MARGIN:
+		text->_priv->left_margin = g_value_get_int (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->left_margin =
+				text->_priv->left_margin;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_RIGHT_MARGIN:
+		text->_priv->right_margin = g_value_get_int (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->right_margin =
+				text->_priv->right_margin;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+	case PROP_INDENT:
+		text->_priv->pixels_above_lines = g_value_get_int (value);
+
+		if (text->_priv->layout) {
+			text->_priv->layout->default_style->indent = text->_priv->indent;
+			gtk_text_layout_default_style_changed(text->_priv->layout);
+		}
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+		break;
+	}
+
+	gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+}
+
+static void
+gnome_canvas_rich_text_get_property (GObject *object, guint property_id,
+				     GValue *value, GParamSpec *pspec)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(object);
+
+	switch (property_id) {
+	case PROP_TEXT:
+		g_value_set_string (value, text->_priv->text);
+		break;
+	case PROP_X:
+		g_value_set_double (value, text->_priv->x);
+		break;
+	case PROP_Y:
+		g_value_set_double (value, text->_priv->y);
+		break;
+	case PROP_HEIGHT:
+		g_value_set_double (value, text->_priv->height);
+		break;
+	case PROP_WIDTH:
+		g_value_set_double (value, text->_priv->width);
+		break;
+	case PROP_EDITABLE:
+		g_value_set_boolean (value, text->_priv->editable);
+		break;
+	case PROP_CURSOR_VISIBLE:
+		g_value_set_boolean (value, text->_priv->cursor_visible);
+		break;
+	case PROP_CURSOR_BLINK:
+		g_value_set_boolean (value, text->_priv->cursor_blink);
+		break;
+	case PROP_GROW_HEIGHT:
+		g_value_set_boolean (value, text->_priv->grow_height);
+		break;
+	case PROP_WRAP_MODE:
+		g_value_set_enum (value, text->_priv->wrap_mode);
+		break;
+	case PROP_JUSTIFICATION:
+		g_value_set_enum (value, text->_priv->justification);
+		break;
+	case PROP_DIRECTION:
+		g_value_set_enum (value, text->_priv->direction);
+		break;
+	case PROP_ANCHOR:
+		g_value_set_enum (value, text->_priv->anchor);
+		break;
+	case PROP_PIXELS_ABOVE_LINES:
+		g_value_set_enum (value, text->_priv->pixels_above_lines);
+		break;
+	case PROP_PIXELS_BELOW_LINES:
+		g_value_set_int (value, text->_priv->pixels_below_lines);
+		break;
+	case PROP_PIXELS_INSIDE_WRAP:
+		g_value_set_int (value, text->_priv->pixels_inside_wrap);
+		break;
+	case PROP_LEFT_MARGIN:
+		g_value_set_int (value, text->_priv->left_margin);
+		break;
+	case PROP_RIGHT_MARGIN:
+		g_value_set_int (value, text->_priv->right_margin);
+		break;
+	case PROP_INDENT:
+		g_value_set_int (value, text->_priv->indent);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_rich_text_realize(GnomeCanvasItem *item)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+	(* GNOME_CANVAS_ITEM_CLASS(parent_class)->realize)(item);
+
+	gnome_canvas_rich_text_ensure_layout(text);
+} /* gnome_canvas_rich_text_realize */
+
+static void
+gnome_canvas_rich_text_unrealize(GnomeCanvasItem *item)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+	gnome_canvas_rich_text_destroy_layout(text);
+
+	(* GNOME_CANVAS_ITEM_CLASS(parent_class)->unrealize)(item);
+} /* gnome_canvas_rich_text_unrealize */
+
+static void
+gnome_canvas_rich_text_move_iter_by_lines(GnomeCanvasRichText *text,
+					  GtkTextIter *newplace, gint count)
+{
+	while (count < 0) {
+		gtk_text_layout_move_iter_to_previous_line(
+			text->_priv->layout, newplace);
+		count++;
+	}
+
+	while (count > 0) {
+		gtk_text_layout_move_iter_to_next_line(
+			text->_priv->layout, newplace);
+		count--;
+	}
+} /* gnome_canvas_rich_text_move_iter_by_lines */
+
+static gint
+gnome_canvas_rich_text_get_cursor_x_position(GnomeCanvasRichText *text)
+{
+	GtkTextIter insert;
+	GdkRectangle rect;
+
+	gtk_text_buffer_get_iter_at_mark(
+		get_buffer(text), &insert,
+		gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+	gtk_text_layout_get_cursor_locations(
+		text->_priv->layout, &insert, &rect, NULL);
+
+	return rect.x;
+} /* gnome_canvas_rich_text_get_cursor_x_position */
+
+static void
+gnome_canvas_rich_text_move_cursor(GnomeCanvasRichText *text,
+				   GtkMovementStep step,
+				   gint count, gboolean extend_selection)
+{
+	GtkTextIter insert, newplace;
+
+	gtk_text_buffer_get_iter_at_mark(
+		get_buffer(text), &insert,
+		gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+
+	newplace = insert;
+
+	switch (step) {
+	case GTK_MOVEMENT_LOGICAL_POSITIONS:
+		gtk_text_iter_forward_cursor_positions(&newplace, count);
+		break;
+	case GTK_MOVEMENT_VISUAL_POSITIONS:
+		gtk_text_layout_move_iter_visually(
+			text->_priv->layout, &newplace, count);
+		break;
+	case GTK_MOVEMENT_WORDS:
+		if (count < 0)
+			gtk_text_iter_backward_word_starts(&newplace, -count);
+		else if (count > 0)
+			gtk_text_iter_forward_word_ends(&newplace, count);
+		break;
+	case GTK_MOVEMENT_DISPLAY_LINES:
+		gnome_canvas_rich_text_move_iter_by_lines(
+			text, &newplace, count);
+		gtk_text_layout_move_iter_to_x(
+			text->_priv->layout, &newplace,
+			gnome_canvas_rich_text_get_cursor_x_position(text));
+		break;
+	case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+		if (count > 1) {
+			gnome_canvas_rich_text_move_iter_by_lines(
+				text, &newplace, --count);
+		}
+		else if (count < -1) {
+			gnome_canvas_rich_text_move_iter_by_lines(
+				text, &newplace, ++count);
+		}
+
+		if (count != 0) {
+			gtk_text_layout_move_iter_to_line_end(
+				text->_priv->layout, &newplace, count);
+		}
+		break;
+	case GTK_MOVEMENT_PARAGRAPHS:
+		/* FIXME: Busted in gtktextview.c too */
+		break;
+	case GTK_MOVEMENT_PARAGRAPH_ENDS:
+		if (count > 0)
+			gtk_text_iter_forward_to_line_end(&newplace);
+		else if (count < 0)
+			gtk_text_iter_set_line_offset(&newplace, 0);
+		break;
+	case GTK_MOVEMENT_BUFFER_ENDS:
+		if (count > 0) {
+			gtk_text_buffer_get_end_iter(
+				get_buffer(text), &newplace);
+		}
+		else if (count < 0) {
+			gtk_text_buffer_get_iter_at_offset(
+				get_buffer(text), &newplace, 0);
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (!gtk_text_iter_equal(&insert, &newplace)) {
+		if (extend_selection) {
+			gtk_text_buffer_move_mark(
+				get_buffer(text),
+				gtk_text_buffer_get_mark(
+					get_buffer(text), "insert"),
+				&newplace);
+		}
+		else {
+			gtk_text_buffer_place_cursor(
+				get_buffer(text), &newplace);
+		}
+	}
+
+	gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
+} /* gnome_canvas_rich_text_move_cursor */
+
+static gboolean
+whitespace(gunichar ch, gpointer user_data)
+{
+	return (ch == ' ' || ch == '\t');
+} /* whitespace */
+
+static gboolean
+not_whitespace(gunichar ch, gpointer user_data)
+{
+	return !whitespace(ch, user_data);
+} /* not_whitespace */
+
+static gboolean
+find_whitespace_region(const GtkTextIter *center,
+		       GtkTextIter *start, GtkTextIter *end)
+{
+	*start = *center;
+	*end = *center;
+
+	if (gtk_text_iter_backward_find_char(start, not_whitespace, NULL, NULL))
+		gtk_text_iter_forward_char(start);
+	if (whitespace(gtk_text_iter_get_char(end), NULL))
+		gtk_text_iter_forward_find_char(end, not_whitespace, NULL, NULL);
+
+	return !gtk_text_iter_equal(start, end);
+} /* find_whitespace_region */
+
+static void
+gnome_canvas_rich_text_delete_from_cursor(GnomeCanvasRichText *text,
+					  GtkDeleteType type,
+					  gint count)
+{
+	GtkTextIter insert, start, end;
+
+	/* Special case: If the user wants to delete a character and there is
+	   a selection, then delete the selection and return */
+	if (type == GTK_DELETE_CHARS) {
+		if (gtk_text_buffer_delete_selection(get_buffer(text), TRUE,
+						     text->_priv->editable))
+			return;
+	}
+
+	gtk_text_buffer_get_iter_at_mark(
+		get_buffer(text), &insert,
+		gtk_text_buffer_get_mark(get_buffer(text), "insert"));
+
+	start = insert;
+	end = insert;
+
+	switch (type) {
+	case GTK_DELETE_CHARS:
+		gtk_text_iter_forward_cursor_positions(&end, count);
+		break;
+	case GTK_DELETE_WORD_ENDS:
+		if (count > 0)
+			gtk_text_iter_forward_word_ends(&end, count);
+		else if (count < 0)
+			gtk_text_iter_backward_word_starts(&start, -count);
+		break;
+	case GTK_DELETE_WORDS:
+		break;
+	case GTK_DELETE_DISPLAY_LINE_ENDS:
+		break;
+	case GTK_DELETE_PARAGRAPH_ENDS:
+		if (gtk_text_iter_ends_line(&end)) {
+			gtk_text_iter_forward_line(&end);
+			--count;
+		}
+
+		while (count > 0) {
+			if (!gtk_text_iter_forward_to_line_end(&end))
+				break;
+
+			--count;
+		}
+		break;
+	case GTK_DELETE_PARAGRAPHS:
+		if (count > 0) {
+			gtk_text_iter_set_line_offset(&start, 0);
+			gtk_text_iter_forward_to_line_end(&end);
+
+			/* Do the lines beyond the first. */
+			while (count > 1) {
+				gtk_text_iter_forward_to_line_end(&end);
+				--count;
+			}
+		}
+		break;
+	case GTK_DELETE_WHITESPACE:
+		find_whitespace_region(&insert, &start, &end);
+		break;
+
+	default:
+		break;
+	}
+
+	if (!gtk_text_iter_equal(&start, &end)) {
+		gtk_text_buffer_begin_user_action(get_buffer(text));
+		gtk_text_buffer_delete_interactive(
+			get_buffer(text), &start, &end, text->_priv->editable);
+		gtk_text_buffer_end_user_action(get_buffer(text));
+	}
+} /* gnome_canvas_rich_text_delete_from_cursor */
+
+static gint
+selection_motion_event_handler(GnomeCanvasRichText *text, GdkEvent *event,
+			       gpointer data)
+{
+	GtkTextIter newplace;
+	GtkTextMark *mark;
+	double newx, newy;
+
+	/* We only want to handle motion events... */
+	if (event->type != GDK_MOTION_NOTIFY)
+		return FALSE;
+
+	newx = (event->motion.x - text->_priv->x) *
+		GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+	newy = (event->motion.y - text->_priv->y) *
+		GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+
+	gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &newplace, newx, newy);
+	mark = gtk_text_buffer_get_mark(get_buffer(text), "insert");
+	gtk_text_buffer_move_mark(get_buffer(text), mark, &newplace);
+
+	return TRUE;
+} /* selection_motion_event_handler */
+
+static void
+gnome_canvas_rich_text_start_selection_drag(GnomeCanvasRichText *text,
+					    const GtkTextIter *iter,
+					    GdkEventButton *button)
+{
+	GtkTextIter newplace;
+
+	g_return_if_fail(text->_priv->selection_drag_handler == 0);
+
+#if 0
+	gnome_canvas_item_grab_focus(GNOME_CANVAS_ITEM(text));
+#endif
+
+	newplace = *iter;
+
+	gtk_text_buffer_place_cursor(get_buffer(text), &newplace);
+
+	text->_priv->selection_drag_handler = g_signal_connect(
+		text, "event",
+		G_CALLBACK (selection_motion_event_handler),
+		NULL);
+} /* gnome_canvas_rich_text_start_selection_drag */
+
+static gboolean
+gnome_canvas_rich_text_end_selection_drag(GnomeCanvasRichText *text,
+					  GdkEventButton *event)
+{
+	if (text->_priv->selection_drag_handler == 0)
+		return FALSE;
+
+	g_signal_handler_disconnect (text, text->_priv->selection_drag_handler);
+	text->_priv->selection_drag_handler = 0;
+
+#if 0
+	gnome_canvas_item_grab(NULL);
+#endif
+
+	return TRUE;
+} /* gnome_canvas_rich_text_end_selection_drag */
+
+static void
+gnome_canvas_rich_text_emit_tag_changed(GnomeCanvasRichText *text,
+					GtkTextTag *tag)
+{
+	g_signal_emit(G_OBJECT(text), signals[TAG_CHANGED], 0, tag);
+} /* gnome_canvas_rich_text_emit_tag_changed */
+
+static gint
+gnome_canvas_rich_text_key_press_event(GnomeCanvasItem *item,
+				       GdkEventKey *event)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	gboolean extend_selection = FALSE;
+	gboolean handled = FALSE;
+
+#if 0
+	printf("Key press event\n");
+#endif
+
+	if (!text->_priv->layout || !text->_priv->buffer)
+		return FALSE;
+
+	if (event->state & GDK_SHIFT_MASK)
+		extend_selection = TRUE;
+
+	switch (event->keyval) {
+	case GDK_Return:
+	case GDK_KP_Enter:
+		gtk_text_buffer_delete_selection(
+			get_buffer(text), TRUE, text->_priv->editable);
+		gtk_text_buffer_insert_interactive_at_cursor(
+			get_buffer(text), "\n", 1, text->_priv->editable);
+		handled = TRUE;
+		break;
+
+	case GDK_Tab:
+		gtk_text_buffer_insert_interactive_at_cursor(
+			get_buffer(text), "\t", 1, text->_priv->editable);
+		handled = TRUE;
+		break;
+
+	/* MOVEMENT */
+	case GDK_Right:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_WORDS, 1,
+				extend_selection);
+			handled = TRUE;
+		}
+		else {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_VISUAL_POSITIONS, 1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+	case GDK_Left:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_WORDS, -1,
+				extend_selection);
+			handled = TRUE;
+		}
+		else {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_VISUAL_POSITIONS, -1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+	case GDK_f:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_LOGICAL_POSITIONS, 1,
+				extend_selection);
+			handled = TRUE;
+		}
+		else if (event->state & GDK_MOD1_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_WORDS, 1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+	case GDK_b:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_LOGICAL_POSITIONS, -1,
+				extend_selection);
+			handled = TRUE;
+		}
+		else if (event->state & GDK_MOD1_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_WORDS, -1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+	case GDK_Up:
+		gnome_canvas_rich_text_move_cursor(
+			text, GTK_MOVEMENT_DISPLAY_LINES, -1,
+			extend_selection);
+		handled = TRUE;
+		break;
+	case GDK_Down:
+		gnome_canvas_rich_text_move_cursor(
+			text, GTK_MOVEMENT_DISPLAY_LINES, 1,
+			extend_selection);
+		handled = TRUE;
+		break;
+	case GDK_p:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_DISPLAY_LINES, -1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+	case GDK_n:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_DISPLAY_LINES, 1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+	case GDK_Home:
+		gnome_canvas_rich_text_move_cursor(
+			text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
+			extend_selection);
+		handled = TRUE;
+		break;
+	case GDK_End:
+		gnome_canvas_rich_text_move_cursor(
+			text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
+			extend_selection);
+		handled = TRUE;
+		break;
+	case GDK_a:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_PARAGRAPH_ENDS, -1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+	case GDK_e:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_move_cursor(
+				text, GTK_MOVEMENT_PARAGRAPH_ENDS, 1,
+				extend_selection);
+			handled = TRUE;
+		}
+		break;
+
+	/* DELETING TEXT */
+	case GDK_Delete:
+	case GDK_KP_Delete:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_WORD_ENDS, 1);
+			handled = TRUE;
+		}
+		else {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_CHARS, 1);
+			handled = TRUE;
+		}
+		break;
+	case GDK_d:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_CHARS, 1);
+			handled = TRUE;
+		}
+		else if (event->state & GDK_MOD1_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_WORD_ENDS, 1);
+			handled = TRUE;
+		}
+		break;
+	case GDK_BackSpace:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_WORD_ENDS, -1);
+			handled = TRUE;
+		}
+		else {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_CHARS, -1);
+		}
+		handled = TRUE;
+		break;
+	case GDK_k:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_PARAGRAPH_ENDS, 1);
+			handled = TRUE;
+		}
+		break;
+	case GDK_u:
+		if (event->state & GDK_CONTROL_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_PARAGRAPHS, 1);
+			handled = TRUE;
+		}
+		break;
+	case GDK_space:
+		if (event->state & GDK_MOD1_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_WHITESPACE, 1);
+			handled = TRUE;
+		}
+		break;
+	case GDK_backslash:
+		if (event->state & GDK_MOD1_MASK) {
+			gnome_canvas_rich_text_delete_from_cursor(
+				text, GTK_DELETE_WHITESPACE, 1);
+			handled = TRUE;
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* An empty string, click just pressing "Alt" by itself or whatever. */
+	if (!event->length)
+		return FALSE;
+
+	if (!handled) {
+		gtk_text_buffer_delete_selection(
+			get_buffer(text), TRUE, text->_priv->editable);
+		gtk_text_buffer_insert_interactive_at_cursor(
+			get_buffer(text), event->string, event->length,
+			text->_priv->editable);
+	}
+
+	gnome_canvas_rich_text_start_cursor_blink(text, TRUE);
+
+	return TRUE;
+} /* gnome_canvas_rich_text_key_press_event */
+
+static gint
+gnome_canvas_rich_text_key_release_event(GnomeCanvasItem *item,
+					 GdkEventKey *event)
+{
+	return FALSE;
+} /* gnome_canvas_rich_text_key_release_event */
+
+static gboolean
+_click(gpointer data)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+	text->_priv->clicks = 0;
+	text->_priv->click_timeout = 0;
+
+	return FALSE;
+} /* _click */
+
+static gint
+gnome_canvas_rich_text_button_press_event(GnomeCanvasItem *item,
+					  GdkEventButton *event)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	GtkTextIter iter;
+	GdkEventType event_type;
+	double newx, newy;
+
+	newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
+	newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
+
+	gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, newx, newy);
+
+	/* The canvas doesn't give us double- or triple-click events, so
+	   we have to synthesize them ourselves. Yay. */
+	event_type = event->type;
+	if (event_type == GDK_BUTTON_PRESS) {
+		text->_priv->clicks++;
+		text->_priv->click_timeout = gtk_timeout_add(400, _click, text);
+
+		if (text->_priv->clicks > 3)
+			text->_priv->clicks = text->_priv->clicks % 3;
+
+		if (text->_priv->clicks == 1)
+			event_type = GDK_BUTTON_PRESS;
+		else if (text->_priv->clicks == 2)
+			event_type = GDK_2BUTTON_PRESS;
+		else if (text->_priv->clicks == 3)
+			event_type = GDK_3BUTTON_PRESS;
+		else
+			printf("ZERO CLICKS!\n");
+	}
+
+	if (event->button == 1 && event_type == GDK_BUTTON_PRESS) {
+		GtkTextIter start, end;
+
+		if (gtk_text_buffer_get_selection_bounds(
+			    get_buffer(text), &start, &end) &&
+		    gtk_text_iter_in_range(&iter, &start, &end)) {
+			text->_priv->drag_start_x = event->x;
+			text->_priv->drag_start_y = event->y;
+		}
+		else {
+			gnome_canvas_rich_text_start_selection_drag(
+				text, &iter, event);
+		}
+
+		return TRUE;
+	}
+	else if (event->button == 1 && event_type == GDK_2BUTTON_PRESS) {
+		GtkTextIter start, end;
+
+#if 0
+		printf("double-click\n");
+#endif
+
+		gnome_canvas_rich_text_end_selection_drag(text, event);
+
+		start = iter;
+		end = start;
+
+		if (gtk_text_iter_inside_word(&start)) {
+			if (!gtk_text_iter_starts_word(&start))
+				gtk_text_iter_backward_word_start(&start);
+
+			if (!gtk_text_iter_ends_word(&end))
+				gtk_text_iter_forward_word_end(&end);
+		}
+
+		gtk_text_buffer_move_mark(
+			get_buffer(text),
+			gtk_text_buffer_get_selection_bound(get_buffer(text)),
+			&start);
+		gtk_text_buffer_move_mark(
+			get_buffer(text),
+			gtk_text_buffer_get_insert(get_buffer(text)), &end);
+
+		text->_priv->just_selected_element = TRUE;
+
+		return TRUE;
+	}
+	else if (event->button == 1 && event_type == GDK_3BUTTON_PRESS) {
+		GtkTextIter start, end;
+
+#if 0
+		printf("triple-click\n");
+#endif
+
+		gnome_canvas_rich_text_end_selection_drag(text, event);
+
+		start = iter;
+		end = start;
+
+		if (gtk_text_layout_iter_starts_line(text->_priv->layout, &start)) {
+			gtk_text_layout_move_iter_to_line_end(
+				text->_priv->layout, &start, -1);
+		}
+		else {
+			gtk_text_layout_move_iter_to_line_end(
+				text->_priv->layout, &start, -1);
+
+			if (!gtk_text_layout_iter_starts_line(
+				    text->_priv->layout, &end)) {
+				gtk_text_layout_move_iter_to_line_end(
+					text->_priv->layout, &end, 1);
+			}
+		}
+
+		gtk_text_buffer_move_mark(
+			get_buffer(text),
+			gtk_text_buffer_get_selection_bound(get_buffer(text)),
+			&start);
+		gtk_text_buffer_move_mark(
+			get_buffer(text),
+			gtk_text_buffer_get_insert(get_buffer(text)), &end);
+
+		text->_priv->just_selected_element = TRUE;
+
+		return TRUE;
+	}
+	else if (event->button == 2 && event_type == GDK_BUTTON_PRESS) {
+		gtk_text_buffer_paste_clipboard(
+			get_buffer(text),
+			gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+			&iter, text->_priv->editable);
+	}
+
+	return FALSE;
+} /* gnome_canvas_rich_text_button_press_event */
+
+static gint
+gnome_canvas_rich_text_button_release_event(GnomeCanvasItem *item,
+					    GdkEventButton *event)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	double newx, newy;
+
+	newx = (event->x - text->_priv->x) * item->canvas->pixels_per_unit;
+	newy = (event->y - text->_priv->y) * item->canvas->pixels_per_unit;
+
+	if (event->button == 1) {
+		if (text->_priv->drag_start_x >= 0) {
+			text->_priv->drag_start_x = -1;
+			text->_priv->drag_start_y = -1;
+		}
+
+		if (gnome_canvas_rich_text_end_selection_drag(text, event))
+			return TRUE;
+		else if (text->_priv->just_selected_element) {
+			text->_priv->just_selected_element = FALSE;
+			return FALSE;
+		}
+		else {
+			GtkTextIter iter;
+
+			gtk_text_layout_get_iter_at_pixel(
+				text->_priv->layout, &iter, newx, newy);
+
+			gtk_text_buffer_place_cursor(get_buffer(text), &iter);
+
+			return FALSE;
+		}
+	}
+
+	return FALSE;
+} /* gnome_canvas_rich_text_button_release_event */
+
+static gint
+gnome_canvas_rich_text_focus_in_event(GnomeCanvasItem *item,
+				      GdkEventFocus *event)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+	if (text->_priv->cursor_visible && text->_priv->layout) {
+		gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+		gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+	}
+
+	return FALSE;
+} /* gnome_canvas_rich_text_focus_in_event */
+
+static gint
+gnome_canvas_rich_text_focus_out_event(GnomeCanvasItem *item,
+				       GdkEventFocus *event)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+
+	if (text->_priv->cursor_visible && text->_priv->layout) {
+		gtk_text_layout_set_cursor_visible(text->_priv->layout, FALSE);
+		gnome_canvas_rich_text_stop_cursor_blink(text);
+	}
+
+	return FALSE;
+} /* gnome_canvas_rich_text_focus_out_event */
+
+static gboolean
+get_event_coordinates(GdkEvent *event, gint *x, gint *y)
+{
+	g_return_val_if_fail(event, FALSE);
+
+	switch (event->type) {
+	case GDK_MOTION_NOTIFY:
+		*x = event->motion.x;
+		*y = event->motion.y;
+		return TRUE;
+	case GDK_BUTTON_PRESS:
+	case GDK_2BUTTON_PRESS:
+	case GDK_3BUTTON_PRESS:
+	case GDK_BUTTON_RELEASE:
+		*x = event->button.x;
+		*y = event->button.y;
+		return TRUE;
+
+	default:
+		return FALSE;
+	}
+} /* get_event_coordinates */
+
+static void
+emit_event_on_tags(GnomeCanvasRichText *text, GdkEvent *event,
+		   GtkTextIter *iter)
+{
+	GSList *tags;
+	GSList *i;
+
+	tags = gtk_text_iter_get_tags(iter);
+
+	i = tags;
+	while (i) {
+		GtkTextTag *tag = i->data;
+
+		gtk_text_tag_event(tag, G_OBJECT(text), event, iter);
+
+		/* The cursor has been moved to within this tag. Emit the
+		   tag_changed signal */
+		if (event->type == GDK_BUTTON_RELEASE ||
+		    event->type == GDK_KEY_PRESS ||
+		    event->type == GDK_KEY_RELEASE) {
+			gnome_canvas_rich_text_emit_tag_changed(
+				text, tag);
+		}
+
+		i = g_slist_next(i);
+	}
+
+	g_slist_free(tags);
+} /* emit_event_on_tags */
+
+static gint
+gnome_canvas_rich_text_event(GnomeCanvasItem *item, GdkEvent *event)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	int x, y;
+
+	if (get_event_coordinates(event, &x, &y)) {
+		GtkTextIter iter;
+
+		x -= text->_priv->x;
+		y -= text->_priv->y;
+
+		gtk_text_layout_get_iter_at_pixel(text->_priv->layout, &iter, x, y);
+		emit_event_on_tags(text, event, &iter);
+	}
+	else if (event->type == GDK_KEY_PRESS ||
+		 event->type == GDK_KEY_RELEASE) {
+		GtkTextMark *insert;
+		GtkTextIter iter;
+
+		insert = gtk_text_buffer_get_mark(get_buffer(text), "insert");
+		gtk_text_buffer_get_iter_at_mark(
+			get_buffer(text), &iter, insert);
+		emit_event_on_tags(text, event, &iter);
+	}
+
+	switch (event->type) {
+	case GDK_KEY_PRESS:
+		return gnome_canvas_rich_text_key_press_event(
+			item, (GdkEventKey *) event);
+	case GDK_KEY_RELEASE:
+		return gnome_canvas_rich_text_key_release_event(
+			item, (GdkEventKey *) event);
+	case GDK_BUTTON_PRESS:
+		return gnome_canvas_rich_text_button_press_event(
+			item, (GdkEventButton *) event);
+	case GDK_BUTTON_RELEASE:
+		return gnome_canvas_rich_text_button_release_event(
+			item, (GdkEventButton *) event);
+	case GDK_FOCUS_CHANGE:
+		if (((GdkEventFocus *) event)->window !=
+		    item->canvas->layout.bin_window)
+			return FALSE;
+
+		if (((GdkEventFocus *) event)->in)
+			return gnome_canvas_rich_text_focus_in_event(
+				item, (GdkEventFocus *) event);
+		else
+			return gnome_canvas_rich_text_focus_out_event(
+				item, (GdkEventFocus *) event);
+	default:
+		return FALSE;
+	}
+} /* gnome_canvas_rich_text_event */
+
+/* Cut/Copy/Paste */
+
+/**
+ * gnome_canvas_rich_text_cut_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Copies the currently selected @text to clipboard, then deletes said text
+ * if it's editable.
+ **/
+void
+gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text)
+{
+	g_return_if_fail(text);
+	g_return_if_fail(get_buffer(text));
+
+	gtk_text_buffer_cut_clipboard(get_buffer(text),
+				      gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+				      text->_priv->editable);
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+
+/**
+ * gnome_canvas_rich_text_copy_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Copies the currently selected @text to clipboard.
+ **/
+void
+gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text)
+{
+	g_return_if_fail(text);
+	g_return_if_fail(get_buffer(text));
+
+	gtk_text_buffer_copy_clipboard(get_buffer(text),
+				       gtk_clipboard_get (GDK_SELECTION_PRIMARY));
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+
+/**
+ * gnome_canvas_rich_text_paste_clipboard:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Pastes the contents of the clipboard at the insertion point.
+ **/
+void
+gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text)
+{
+	g_return_if_fail(text);
+	g_return_if_fail(get_buffer(text));
+
+	gtk_text_buffer_paste_clipboard(get_buffer(text),
+					gtk_clipboard_get (GDK_SELECTION_PRIMARY),
+					NULL,
+					text->_priv->editable);
+} /* gnome_canvas_rich_text_cut_clipboard */
+
+static gint
+preblink_cb(gpointer data)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+	text->_priv->preblink_timeout = 0;
+	gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+
+	/* Remove ourselves */
+	return FALSE;
+} /* preblink_cb */
+
+static gint
+blink_cb(gpointer data)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+	gboolean visible;
+
+	g_assert(text->_priv->layout);
+	g_assert(text->_priv->cursor_visible);
+
+	visible = gtk_text_layout_get_cursor_visible(text->_priv->layout);
+	if (visible)
+		text->_priv->blink_timeout = gtk_timeout_add(
+			CURSOR_OFF_TIME, blink_cb, text);
+	else
+		text->_priv->blink_timeout = gtk_timeout_add(
+			CURSOR_ON_TIME, blink_cb, text);
+
+	gtk_text_layout_set_cursor_visible(text->_priv->layout, !visible);
+
+	/* Remove ourself */
+	return FALSE;
+} /* blink_cb */
+
+static void
+gnome_canvas_rich_text_start_cursor_blink(GnomeCanvasRichText *text,
+					  gboolean with_delay)
+{
+	if (!text->_priv->layout)
+		return;
+
+	if (!text->_priv->cursor_visible || !text->_priv->cursor_blink)
+		return;
+
+	if (text->_priv->preblink_timeout != 0) {
+		gtk_timeout_remove(text->_priv->preblink_timeout);
+		text->_priv->preblink_timeout = 0;
+	}
+
+	if (with_delay) {
+		if (text->_priv->blink_timeout != 0) {
+			gtk_timeout_remove(text->_priv->blink_timeout);
+			text->_priv->blink_timeout = 0;
+		}
+
+		gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+
+		text->_priv->preblink_timeout = gtk_timeout_add(
+			PREBLINK_TIME, preblink_cb, text);
+	}
+	else {
+		if (text->_priv->blink_timeout == 0) {
+			gtk_text_layout_set_cursor_visible(text->_priv->layout, TRUE);
+			text->_priv->blink_timeout = gtk_timeout_add(
+				CURSOR_ON_TIME, blink_cb, text);
+		}
+	}
+} /* gnome_canvas_rich_text_start_cursor_blink */
+
+static void
+gnome_canvas_rich_text_stop_cursor_blink(GnomeCanvasRichText *text)
+{
+	if (text->_priv->blink_timeout) {
+		gtk_timeout_remove(text->_priv->blink_timeout);
+		text->_priv->blink_timeout = 0;
+	}
+} /* gnome_canvas_rich_text_stop_cursor_blink */
+
+/* We have to request updates this way because the update cycle is not
+   re-entrant. This will fire off a request in an idle loop. */
+static gboolean
+request_update(gpointer data)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+	gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+
+	return FALSE;
+} /* request_update */
+
+static void
+invalidated_handler(GtkTextLayout *layout, gpointer data)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+#if 0
+	printf("Text is being invalidated.\n");
+#endif
+
+	gtk_text_layout_validate(text->_priv->layout, 2000);
+
+	/* We are called from the update cycle; gotta put this in an idle
+	   loop. */
+	gtk_idle_add(request_update, text);
+} /* invalidated_handler */
+
+static void
+scale_fonts(GtkTextTag *tag, gpointer data)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+	if (!tag->values)
+		return;
+
+	g_object_set(
+		G_OBJECT(tag), "scale",
+		text->_priv->layout->default_style->font_scale, NULL);
+} /* scale_fonts */
+
+static void
+changed_handler(GtkTextLayout *layout, gint start_y,
+		gint old_height, gint new_height, gpointer data)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(data);
+
+#if 0
+	printf("Layout %p is being changed.\n", text->_priv->layout);
+#endif
+
+	if (text->_priv->layout->default_style->font_scale !=
+	    GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit) {
+		GtkTextTagTable *tag_table;
+
+		text->_priv->layout->default_style->font_scale =
+			GNOME_CANVAS_ITEM(text)->canvas->pixels_per_unit;
+
+		tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
+		gtk_text_tag_table_foreach(tag_table, scale_fonts, text);
+
+		gtk_text_layout_default_style_changed(text->_priv->layout);
+	}
+
+	if (text->_priv->grow_height) {
+		int width, height;
+
+		gtk_text_layout_get_size(text->_priv->layout, &width, &height);
+
+		if (height > text->_priv->height)
+			text->_priv->height = height;
+	}
+
+	/* We are called from the update cycle; gotta put this in an idle
+	   loop. */
+	gtk_idle_add(request_update, text);
+} /* changed_handler */
+
+
+/**
+ * gnome_canvas_rich_text_set_buffer:
+ * @text: a #GnomeCanvasRichText.
+ * @buffer: a #GtkTextBuffer.
+ *
+ * Sets the buffer field of the @text to @buffer.
+ **/
+void
+gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text,
+				  GtkTextBuffer *buffer)
+{
+	g_return_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text));
+	g_return_if_fail(buffer == NULL || GTK_IS_TEXT_BUFFER(buffer));
+
+	if (text->_priv->buffer == buffer)
+		return;
+
+	if (text->_priv->buffer != NULL) {
+		g_object_unref(G_OBJECT(text->_priv->buffer));
+	}
+
+	text->_priv->buffer = buffer;
+
+	if (buffer) {
+		g_object_ref(G_OBJECT(buffer));
+
+		if (text->_priv->layout)
+			gtk_text_layout_set_buffer(text->_priv->layout, buffer);
+	}
+
+	gnome_canvas_item_request_update(GNOME_CANVAS_ITEM(text));
+} /* gnome_canvas_rich_text_set_buffer */
+
+static GtkTextBuffer *
+get_buffer(GnomeCanvasRichText *text)
+{
+	if (!text->_priv->buffer) {
+		GtkTextBuffer *b;
+
+		b = gtk_text_buffer_new(NULL);
+		gnome_canvas_rich_text_set_buffer(text, b);
+		g_object_unref(G_OBJECT(b));
+	}
+
+	return text->_priv->buffer;
+} /* get_buffer */
+
+
+/**
+ * gnome_canvas_rich_text_get_buffer:
+ * @text: a #GnomeCanvasRichText.
+ *
+ * Returns a #GtkTextBuffer associated with the #GnomeCanvasRichText.
+ * This function creates a new #GtkTextBuffer if the text buffer is NULL.
+ *
+ * Return value: the #GtkTextBuffer.
+ **/
+GtkTextBuffer *
+gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text)
+{
+	g_return_val_if_fail(GNOME_IS_CANVAS_RICH_TEXT(text), NULL);
+
+	return get_buffer(text);
+} /* gnome_canvas_rich_text_get_buffer */
+
+
+/**
+ * gnome_canvas_rich_text_get_iter_location:
+ * @text: a #GnomeCanvasRichText.
+ * @iter: a #GtkTextIter.
+ * @location: a #GdkRectangle containing the bounds of the character at @iter.
+ *
+ * Gets a rectangle which roughly contains the character at @iter.
+ **/
+void
+gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
+					  const GtkTextIter *iter,
+					  GdkRectangle      *location)
+{
+  g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
+  g_return_if_fail (gtk_text_iter_get_buffer (iter) == text->_priv->buffer);
+
+  gtk_text_layout_get_iter_location (text->_priv->layout, iter, location);
+}
+
+
+/**
+ * gnome_canvas_rich_text_get_iter_at_location:
+ * @text: a #GnomeCanvasRichText.
+ * @iter: a #GtkTextIter.
+ * @x: x position, in buffer coordinates.
+ * @y: y position, in buffer coordinates.
+ *
+ * Retrieves the iterator at the buffer coordinates x and y.
+ **/
+void
+gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
+                                    GtkTextIter *iter,
+                                    gint         x,
+                                    gint         y)
+{
+  g_return_if_fail (GNOME_IS_CANVAS_RICH_TEXT (text));
+  g_return_if_fail (iter != NULL);
+  g_return_if_fail (text->_priv->layout != NULL);
+
+  gtk_text_layout_get_iter_at_pixel (text->_priv->layout,
+                                     iter,
+                                     x,
+                                     y);
+}
+
+
+static void
+gnome_canvas_rich_text_set_attributes_from_style(GnomeCanvasRichText *text,
+						 GtkTextAttributes *values,
+						 GtkStyle *style)
+{
+	values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
+	values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
+
+	if (values->font)
+		pango_font_description_free (values->font);
+
+	values->font = pango_font_description_copy (style->font_desc);
+
+} /* gnome_canvas_rich_text_set_attributes_from_style */
+
+static void
+gnome_canvas_rich_text_ensure_layout(GnomeCanvasRichText *text)
+{
+	if (!text->_priv->layout) {
+		GtkWidget *canvas;
+		GtkTextAttributes *style;
+		PangoContext *ltr_context, *rtl_context;
+
+		text->_priv->layout = gtk_text_layout_new();
+
+		gtk_text_layout_set_screen_width(text->_priv->layout, text->_priv->width);
+
+		if (get_buffer(text)) {
+			gtk_text_layout_set_buffer(
+				text->_priv->layout, get_buffer(text));
+		}
+
+		/* Setup the cursor stuff */
+		gtk_text_layout_set_cursor_visible(
+			text->_priv->layout, text->_priv->cursor_visible);
+		if (text->_priv->cursor_visible && text->_priv->cursor_blink)
+			gnome_canvas_rich_text_start_cursor_blink(text, FALSE);
+		else
+			gnome_canvas_rich_text_stop_cursor_blink(text);
+
+		canvas = GTK_WIDGET(GNOME_CANVAS_ITEM(text)->canvas);
+
+		ltr_context = gtk_widget_create_pango_context(canvas);
+		pango_context_set_base_dir(ltr_context, PANGO_DIRECTION_LTR);
+		rtl_context = gtk_widget_create_pango_context(canvas);
+		pango_context_set_base_dir(rtl_context, PANGO_DIRECTION_RTL);
+
+		gtk_text_layout_set_contexts(
+			text->_priv->layout, ltr_context, rtl_context);
+
+		g_object_unref(G_OBJECT(ltr_context));
+		g_object_unref(G_OBJECT(rtl_context));
+
+		style = gtk_text_attributes_new();
+
+		gnome_canvas_rich_text_set_attributes_from_style(
+			text, style, canvas->style);
+
+		style->pixels_above_lines = text->_priv->pixels_above_lines;
+		style->pixels_below_lines = text->_priv->pixels_below_lines;
+		style->pixels_inside_wrap = text->_priv->pixels_inside_wrap;
+		style->left_margin = text->_priv->left_margin;
+		style->right_margin = text->_priv->right_margin;
+		style->indent = text->_priv->indent;
+		style->tabs = NULL;
+		style->wrap_mode = text->_priv->wrap_mode;
+		style->justification = text->_priv->justification;
+		style->direction = text->_priv->direction;
+		style->editable = text->_priv->editable;
+		style->invisible = !text->_priv->visible;
+
+		gtk_text_layout_set_default_style(text->_priv->layout, style);
+
+		gtk_text_attributes_unref(style);
+
+		g_signal_connect(
+			G_OBJECT(text->_priv->layout), "invalidated",
+			G_CALLBACK (invalidated_handler), text);
+
+		g_signal_connect(
+			G_OBJECT(text->_priv->layout), "changed",
+			G_CALLBACK (changed_handler), text);
+	}
+} /* gnome_canvas_rich_text_ensure_layout */
+
+static void
+gnome_canvas_rich_text_destroy_layout(GnomeCanvasRichText *text)
+{
+	if (text->_priv->layout) {
+		g_signal_handlers_disconnect_by_func(
+			G_OBJECT(text->_priv->layout), invalidated_handler, text);
+		g_signal_handlers_disconnect_by_func(
+			G_OBJECT(text->_priv->layout), changed_handler, text);
+		g_object_unref(G_OBJECT(text->_priv->layout));
+		text->_priv->layout = NULL;
+	}
+} /* gnome_canvas_rich_text_destroy_layout */
+
+static void
+adjust_for_anchors(GnomeCanvasRichText *text, double *ax, double *ay)
+{
+	double x, y;
+
+	x = text->_priv->x;
+	y = text->_priv->y;
+
+	/* Anchor text */
+	/* X coordinates */
+	switch (text->_priv->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		x -= text->_priv->width / 2;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		x -= text->_priv->width;
+		break;
+	default:
+		break;
+	}
+
+	/* Y coordinates */
+	switch (text->_priv->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		y -= text->_priv->height / 2;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		y -= text->_priv->height;
+		break;
+	default:
+		break;
+	}
+
+	if (ax)
+		*ax = x;
+	if (ay)
+		*ay = y;
+} /* adjust_for_anchors */
+
+static void
+get_bounds(GnomeCanvasRichText *text, double *px1, double *py1,
+	   double *px2, double *py2)
+{
+	GnomeCanvasItem *item = GNOME_CANVAS_ITEM(text);
+	double x, y;
+	double x1, x2, y1, y2;
+	int cx1, cx2, cy1, cy2;
+
+	adjust_for_anchors(text, &x, &y);
+
+	x1 = x;
+	y1 = y;
+	x2 = x + text->_priv->width;
+	y2 = y + text->_priv->height;
+
+	gnome_canvas_item_i2w(item, &x1, &y1);
+	gnome_canvas_item_i2w(item, &x2, &y2);
+	gnome_canvas_w2c(item->canvas, x1, y1, &cx1, &cy1);
+	gnome_canvas_w2c(item->canvas, x2, y2, &cx2, &cy2);
+
+	*px1 = cx1;
+	*py1 = cy1;
+	*px2 = cx2;
+	*py2 = cy2;
+} /* get_bounds */
+
+static void gnome_canvas_rich_text_get_bounds(GnomeCanvasItem *item, double *px1, double *py1,
+	   double *px2, double *py2)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	get_bounds (text, px1, py1, px2, py2);
+}
+
+static void
+gnome_canvas_rich_text_update(GnomeCanvasItem *item, double *affine,
+			      ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	double x1, y1, x2, y2;
+	GtkTextIter start;
+
+	(* GNOME_CANVAS_ITEM_CLASS(parent_class)->update)(
+		item, affine, clip_path, flags);
+
+	get_bounds(text, &x1, &y1, &x2, &y2);
+
+	gtk_text_buffer_get_iter_at_offset(text->_priv->buffer, &start, 0);
+	if (text->_priv->layout)
+		gtk_text_layout_validate_yrange(
+			text->_priv->layout, &start, 0, y2 - y1);
+
+	gnome_canvas_update_bbox(item, x1, y1, x2, y2);
+} /* gnome_canvas_rich_text_update */
+
+static double
+gnome_canvas_rich_text_point(GnomeCanvasItem *item, double x, double y,
+			     int cx, int cy, GnomeCanvasItem **actual_item)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	double ax, ay;
+	double x1, x2, y1, y2;
+	double dx, dy;
+
+	*actual_item = item;
+
+	/* This is a lame cop-out. Anywhere inside of the bounding box. */
+
+	adjust_for_anchors(text, &ax, &ay);
+
+	x1 = ax;
+	y1 = ay;
+	x2 = ax + text->_priv->width;
+	y2 = ay + text->_priv->height;
+
+	if ((x > x1) && (y > y1) && (x < x2) && (y < y2))
+		return 0.0;
+
+	if (x < x1)
+		dx = x1 - x;
+	else if (x > x2)
+		dx = x - x2;
+	else
+		dx = 0.0;
+
+	if (y < y1)
+		dy = y1 - y;
+	else if (y > y2)
+		dy = y - y2;
+	else
+		dy = 0.0;
+
+	return sqrt(dx * dx + dy * dy);
+} /* gnome_canvas_rich_text_point */
+
+static void
+gnome_canvas_rich_text_draw(GnomeCanvasItem *item, GdkDrawable *drawable,
+			    int x, int y, int width, int height)
+{
+	GnomeCanvasRichText *text = GNOME_CANVAS_RICH_TEXT(item);
+	double i2w[6], w2c[6], i2c[6];
+	double ax, ay;
+	int x1, y1, x2, y2;
+	ArtPoint i1, i2;
+	ArtPoint c1, c2;
+
+	gnome_canvas_item_i2w_affine(item, i2w);
+	gnome_canvas_w2c_affine(item->canvas, w2c);
+	art_affine_multiply(i2c, i2w, w2c);
+
+	adjust_for_anchors(text, &ax, &ay);
+
+	i1.x = ax;
+	i1.y = ay;
+	i2.x = ax + text->_priv->width;
+	i2.y = ay + text->_priv->height;
+	art_affine_point(&c1, &i1, i2c);
+	art_affine_point(&c2, &i2, i2c);
+
+	x1 = c1.x;
+	y1 = c1.y;
+	x2 = c2.x;
+	y2 = c2.y;
+
+	gtk_text_layout_set_screen_width(text->_priv->layout, x2 - x1);
+
+        /* FIXME: should last arg be NULL? */
+	gtk_text_layout_draw(
+		text->_priv->layout,
+		GTK_WIDGET(item->canvas),
+		drawable,
+		GTK_WIDGET (item->canvas)->style->text_gc[GTK_STATE_NORMAL],
+		x - x1, y - y1,
+		0, 0, (x2 - x1) - (x - x1), (y2 - y1) - (y - y1),
+		NULL);
+} /* gnome_canvas_rich_text_draw */
+
+static void
+gnome_canvas_rich_text_render(GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+	g_warning ("rich text item not implemented for anti-aliased canvas");
+} /* gnome_canvas_rich_text_render */
+
+#if 0
+static GtkTextTag *
+gnome_canvas_rich_text_add_tag(GnomeCanvasRichText *text, char *tag_name,
+			       int start_offset, int end_offset,
+			       const char *first_property_name, ...)
+{
+	GtkTextTag *tag;
+	GtkTextIter start, end;
+	va_list var_args;
+
+	g_return_val_if_fail(text, NULL);
+	g_return_val_if_fail(start_offset >= 0, NULL);
+	g_return_val_if_fail(end_offset >= 0, NULL);
+
+	if (tag_name) {
+		GtkTextTagTable *tag_table;
+
+		tag_table = gtk_text_buffer_get_tag_table(get_buffer(text));
+		g_return_val_if_fail(gtk_text_tag_table_lookup(tag_table, tag_name) == NULL, NULL);
+	}
+
+	tag = gtk_text_buffer_create_tag(
+		get_buffer(text), tag_name, NULL);
+
+	va_start(var_args, first_property_name);
+	g_object_set_valist(G_OBJECT(tag), first_property_name, var_args);
+	va_end(var_args);
+
+	gtk_text_buffer_get_iter_at_offset(
+		get_buffer(text), &start, start_offset);
+	gtk_text_buffer_get_iter_at_offset(
+		get_buffer(text), &end, end_offset);
+	gtk_text_buffer_apply_tag(get_buffer(text), tag, &start, &end);
+
+	return tag;
+} /* gnome_canvas_rich_text_add_tag */
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-rich-text.h b/src/libgnomecanvas/gnome-canvas-rich-text.h
new file mode 100644
index 0000000..23773af
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-rich-text.h
@@ -0,0 +1,77 @@
+/* Editable GnomeCanvas text item based on GtkTextLayout, borrowed heavily
+ * from GtkTextView.
+ *
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Copyright (c) 2001 Joe Shaw
+ *
+ * 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 3 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/>.
+ */
+
+#ifndef GNOME_CANVAS_RICH_TEXT_H
+#define GNOME_CANVAS_RICH_TEXT_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <gtk/gtktextbuffer.h>
+
+G_BEGIN_DECLS
+
+#define GNOME_TYPE_CANVAS_RICH_TEXT             (gnome_canvas_rich_text_get_type ())
+#define GNOME_CANVAS_RICH_TEXT(obj)             (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichText))
+#define GNOME_CANVAS_RICH_TEXT_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichTextClass))
+#define GNOME_IS_CANVAS_RICH_TEXT(obj)          (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_RICH_TEXT))
+#define GNOME_IS_CANVAS_RICH_TEXT_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_RICH_TEXT))
+#define GNOME_CANVAS_RICH_TEXT_GET_CLASS(obj)   (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_RICH_TEXT, GnomeCanvasRichTextClass))
+
+typedef struct _GnomeCanvasRichText             GnomeCanvasRichText;
+typedef struct _GnomeCanvasRichTextPrivate      GnomeCanvasRichTextPrivate;
+typedef struct _GnomeCanvasRichTextClass        GnomeCanvasRichTextClass;
+
+struct _GnomeCanvasRichText {
+	GnomeCanvasItem item;
+
+    GnomeCanvasRichTextPrivate *_priv;
+};
+
+struct _GnomeCanvasRichTextClass {
+	GnomeCanvasItemClass parent_class;
+
+	void (* tag_changed)(GnomeCanvasRichText *text,
+			     GtkTextTag *tag);
+};
+
+GType gnome_canvas_rich_text_get_type(void) G_GNUC_CONST;
+
+void gnome_canvas_rich_text_cut_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_copy_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_paste_clipboard(GnomeCanvasRichText *text);
+
+void gnome_canvas_rich_text_set_buffer(GnomeCanvasRichText *text,
+				       GtkTextBuffer *buffer);
+
+GtkTextBuffer *gnome_canvas_rich_text_get_buffer(GnomeCanvasRichText *text);
+void
+gnome_canvas_rich_text_get_iter_location (GnomeCanvasRichText *text,
+					  const GtkTextIter *iter,
+					  GdkRectangle      *location);
+void
+gnome_canvas_rich_text_get_iter_at_location (GnomeCanvasRichText *text,
+                                    GtkTextIter *iter,
+                                    gint         x,
+					     gint         y);
+
+G_END_DECLS
+
+#endif /* GNOME_CANVAS_RICH_TEXT_H */
diff --git a/src/libgnomecanvas/gnome-canvas-shape-private.h b/src/libgnomecanvas/gnome-canvas-shape-private.h
new file mode 100644
index 0000000..ffdd3ce
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-shape-private.h
@@ -0,0 +1,103 @@
+#ifndef GNOME_CANVAS_SHAPE_PRIVATE_H
+#define GNOME_CANVAS_SHAPE_PRIVATE_H
+
+/* Bpath item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph acm org>
+ *          Lauris Kaplinski <lauris ariman ee>
+ */
+
+#include <gdk/gdk.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasShapePrivGdk GnomeCanvasShapePrivGdk;
+typedef struct _GCBPDrawCtx GCBPDrawCtx;
+
+/* Per canvas private structure, holding necessary data for rendering
+ * temporary masks, which are needed for drawing multipart bpaths.
+ * As canvas cannot multithread, we can be sure, that masks are used
+ * serially, also one set of masks per canvas is sufficent to guarantee,
+ * that masks are created on needed X server. Masks grow as needed.
+ * Full structure is refcounted in Bpath implementation
+ */
+
+struct _GCBPDrawCtx {
+	gint refcount;
+
+	GnomeCanvas * canvas;
+
+	gint width;
+	gint height;
+
+	GdkBitmap * mask;
+	GdkBitmap * clip;
+
+	GdkGC * clear_gc;
+	GdkGC * xor_gc;
+};
+
+/* Per Bpath private structure, holding Gdk specific data */
+
+struct _GnomeCanvasShapePrivGdk {
+	gulong fill_pixel;		/* Color for fill */
+	gulong outline_pixel;		/* Color for outline */
+
+	GdkBitmap *fill_stipple;	/* Stipple for fill */
+	GdkBitmap *outline_stipple;	/* Stipple for outline */
+
+	GdkGC * fill_gc;		/* GC for filling */
+	GdkGC * outline_gc;		/* GC for outline */
+
+	gint len_points;		/* Size of allocated points array */
+	gint num_points;		/* Gdk points in canvas coords */
+	GdkPoint * points;		/* Ivariant: closed paths are before open ones */
+	GSList * closed_paths;		/* List of lengths */
+	GSList * open_paths;		/* List of lengths */
+
+	GCBPDrawCtx * ctx;		/* Pointer to per-canvas drawing context */
+};
+
+struct _GnomeCanvasShapePriv {
+	GnomeCanvasPathDef * path;      /* Our bezier path representation */
+
+	gdouble scale;			/* CTM scaling (for pen) */
+
+	guint fill_set : 1;		/* Is fill color set? */
+	guint outline_set : 1;		/* Is outline color set? */
+	guint width_pixels : 1;		/* Is outline width specified in pixels or units? */
+
+	double width;			/* Width of outline, in user coords */
+
+	guint32 fill_rgba;		/* Fill color, RGBA */
+	guint32 outline_rgba;		/* Outline color, RGBA */
+
+	GdkCapStyle cap;		/* Cap style for line */
+	GdkJoinStyle join;		/* Join style for line */
+	ArtWindRule wind;		/* Winding rule */
+	double miterlimit;		/* Miter limit */
+
+	ArtVpathDash dash;		/* Dashing pattern */
+
+	ArtSVP * fill_svp;		/* The SVP for the filled shape */
+	ArtSVP * outline_svp;		/* The SVP for the outline shape */
+
+	GnomeCanvasShapePrivGdk * gdk;	/* Gdk specific things */
+};
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-shape.c b/src/libgnomecanvas/gnome-canvas-shape.c
new file mode 100644
index 0000000..f32ea29
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-shape.c
@@ -0,0 +1,1559 @@
+/* Generic bezier shape item for GnomeCanvasWidget.  Most code taken
+ * from gnome-canvas-bpath but made into a shape item.
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent
+ * canvas widget.  Tk is copyrighted by the Regents of the University
+ * of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph acm org>
+ *          Lauris Kaplinski <lauris ximian com>
+ *          Miguel de Icaza <miguel kernel org>
+ *          Cody Russell <bratsche gnome org>
+ *          Rusty Conover <rconover bangtail net>
+ */
+
+/* These includes are set up for standalone compile. If/when this codebase
+   is integrated into libgnomeui, the includes will need to change. */
+
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtkobject.h>
+#include <gtk/gtkwidget.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+
+#include "gnome-canvas-shape.h"
+#include "gnome-canvas-shape-private.h"
+#include "gnome-canvas-path-def.h"
+
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_point.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_vpath_dash.h>
+#include <libart_lgpl/art_svp_wind.h>
+#include <libart_lgpl/art_svp_intersect.h>
+#include <libart_lgpl/art_rect_svp.h>
+
+enum {
+	PROP_0,
+	PROP_FILL_COLOR,
+	PROP_FILL_COLOR_GDK,
+	PROP_FILL_COLOR_RGBA,
+	PROP_OUTLINE_COLOR,
+	PROP_OUTLINE_COLOR_GDK,
+	PROP_OUTLINE_COLOR_RGBA,
+	PROP_FILL_STIPPLE,
+	PROP_OUTLINE_STIPPLE,
+	PROP_WIDTH_PIXELS,
+	PROP_WIDTH_UNITS,
+	PROP_CAP_STYLE,
+	PROP_JOIN_STYLE,
+	PROP_WIND,
+	PROP_MITERLIMIT,
+	PROP_DASH
+};
+
+static void gnome_canvas_shape_class_init   (GnomeCanvasShapeClass *class);
+static void gnome_canvas_shape_init         (GnomeCanvasShape      *bpath);
+static void gnome_canvas_shape_destroy      (GtkObject               *object);
+static void gnome_canvas_shape_set_property (GObject               *object,
+					     guint                  param_id,
+					     const GValue          *value,
+                                             GParamSpec            *pspec);
+static void gnome_canvas_shape_get_property (GObject               *object,
+					     guint                  param_id,
+					     GValue                *value,
+                                             GParamSpec            *pspec);
+
+static void   gnome_canvas_shape_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static void   gnome_canvas_shape_realize     (GnomeCanvasItem *item);
+static void   gnome_canvas_shape_unrealize   (GnomeCanvasItem *item);
+static void   gnome_canvas_shape_draw        (GnomeCanvasItem *item, GdkDrawable *drawable,
+                                              int x, int y, int width, int height);
+static double gnome_canvas_shape_point       (GnomeCanvasItem *item, double x, double y,
+                                              int cx, int cy, GnomeCanvasItem **actual_item);
+static void   gnome_canvas_shape_render      (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static void   gnome_canvas_shape_bounds      (GnomeCanvasItem *item,
+					      double *x1, double *y1, double *x2, double *y2);
+
+static gulong get_pixel_from_rgba (GnomeCanvasItem *item, guint32 rgba_color);
+static guint32 get_rgba_from_color (GdkColor * color);
+static void set_gc_foreground (GdkGC *gc, gulong pixel);
+static void gcbp_ensure_gdk (GnomeCanvasShape * bpath);
+static void gcbp_destroy_gdk (GnomeCanvasShape * bpath);
+static void set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure);
+static void gcbp_ensure_mask (GnomeCanvasShape * bpath, gint width, gint height);
+static void gcbp_draw_ctx_unref (GCBPDrawCtx * ctx);
+
+static GnomeCanvasItemClass *parent_class;
+
+GType
+gnome_canvas_shape_get_type (void)
+{
+	static GType shape_type;
+
+	if (!shape_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasShapeClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_shape_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasShape),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_shape_init,
+			NULL			/* value_table */
+		};
+
+		shape_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasShape",
+						     &object_info, 0);
+	}
+
+	return shape_type;
+}
+
+static void
+gnome_canvas_shape_class_init (GnomeCanvasShapeClass *class)
+{
+	GObjectClass         *gobject_class;
+	GtkObjectClass       *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	/* when this gets checked into libgnomeui, change the
+           GTK_TYPE_POINTER to GTK_TYPE_GNOME_CANVAS_SHAPE, and add an
+           entry to gnome-boxed.defs */
+
+	gobject_class->set_property = gnome_canvas_shape_set_property;
+	gobject_class->get_property = gnome_canvas_shape_get_property;
+
+
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_FILL_COLOR,
+                                         g_param_spec_string ("fill_color", NULL, NULL,
+                                                              NULL,
+                                                              (G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_FILL_COLOR_GDK,
+                                         g_param_spec_boxed ("fill_color_gdk", NULL, NULL,
+                                                             GDK_TYPE_COLOR,
+                                                             (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_FILL_COLOR_RGBA,
+                                         g_param_spec_uint ("fill_color_rgba", NULL, NULL,
+                                                            0, G_MAXUINT, 0,
+                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_OUTLINE_COLOR,
+                                         g_param_spec_string ("outline_color", NULL, NULL,
+                                                              NULL,
+                                                              (G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_OUTLINE_COLOR_GDK,
+                                         g_param_spec_boxed ("outline_color_gdk", NULL, NULL,
+                                                             GDK_TYPE_COLOR,
+                                                             (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_OUTLINE_COLOR_RGBA,
+                                         g_param_spec_uint ("outline_color_rgba", NULL, NULL,
+                                                            0, G_MAXUINT, 0,
+                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_FILL_STIPPLE,
+                                         g_param_spec_object ("fill_stipple", NULL, NULL,
+                                                              GDK_TYPE_DRAWABLE,
+                                                              (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_OUTLINE_STIPPLE,
+                                         g_param_spec_object ("outline_stipple", NULL, NULL,
+                                                              GDK_TYPE_DRAWABLE,
+                                                              (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_WIDTH_PIXELS,
+                                         g_param_spec_uint ("width_pixels", NULL, NULL,
+                                                            0, G_MAXUINT, 0,
+                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_WIDTH_UNITS,
+                                         g_param_spec_double ("width_units", NULL, NULL,
+                                                              0.0, G_MAXDOUBLE, 0.0,
+                                                              (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_CAP_STYLE,
+                                         g_param_spec_enum ("cap_style", NULL, NULL,
+                                                            GDK_TYPE_CAP_STYLE,
+                                                            GDK_CAP_BUTT,
+                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_JOIN_STYLE,
+                                         g_param_spec_enum ("join_style", NULL, NULL,
+                                                            GDK_TYPE_JOIN_STYLE,
+                                                            GDK_JOIN_MITER,
+                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_WIND,
+                                         g_param_spec_uint ("wind", NULL, NULL,
+                                                            0, G_MAXUINT, 0,
+                                                            (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_MITERLIMIT,
+                                         g_param_spec_double ("miterlimit", NULL, NULL,
+                                                              0.0, G_MAXDOUBLE, 0.0,
+                                                              (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property (gobject_class,
+                                         PROP_DASH,
+                                         g_param_spec_pointer ("dash", NULL, NULL,
+                                                               (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_shape_destroy;
+
+	item_class->update = gnome_canvas_shape_update;
+	item_class->realize = gnome_canvas_shape_realize;
+	item_class->unrealize = gnome_canvas_shape_unrealize;
+	item_class->draw = gnome_canvas_shape_draw;
+	item_class->point = gnome_canvas_shape_point;
+	item_class->render = gnome_canvas_shape_render;
+	item_class->bounds = gnome_canvas_shape_bounds;
+}
+
+static void
+gnome_canvas_shape_init (GnomeCanvasShape *shape)
+{
+	shape->priv = g_new (GnomeCanvasShapePriv, 1);
+
+	shape->priv->path = NULL;
+
+	shape->priv->scale = 1.0;
+
+	shape->priv->fill_set = FALSE;
+	shape->priv->outline_set = FALSE;
+	shape->priv->width_pixels = FALSE;
+
+	shape->priv->width = 1.0;
+
+	shape->priv->fill_rgba = 0x0000003f;
+	shape->priv->outline_rgba = 0x0000007f;
+
+	shape->priv->cap = GDK_CAP_BUTT;
+	shape->priv->join = GDK_JOIN_MITER;
+	shape->priv->wind = ART_WIND_RULE_ODDEVEN;
+	shape->priv->miterlimit = 10.43;	   /* X11 default */
+
+	shape->priv->dash.n_dash = 0;
+	shape->priv->dash.dash = NULL;
+
+	shape->priv->fill_svp = NULL;
+	shape->priv->outline_svp = NULL;
+
+	shape->priv->gdk = NULL;
+}
+
+static void
+gnome_canvas_shape_destroy (GtkObject *object)
+{
+	GnomeCanvasShape *shape;
+	GnomeCanvasShapePriv *priv;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_SHAPE (object));
+
+        shape = GNOME_CANVAS_SHAPE (object);
+
+	if (shape->priv) {
+		priv = shape->priv;
+		if (priv->gdk) gcbp_destroy_gdk (shape);
+
+		if (priv->path) gnome_canvas_path_def_unref (priv->path);
+
+		if (priv->dash.dash) g_free (priv->dash.dash);
+		if (priv->fill_svp) art_svp_free (priv->fill_svp);
+		if (priv->outline_svp) art_svp_free (priv->outline_svp);
+		
+		g_free (shape->priv);
+	        shape->priv = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+/**
+ * gnome_canvas_shape_set_path_def:
+ * @shape: a GnomeCanvasShape
+ * @def: a GnomeCanvasPathDef 
+ *
+ * This function sets the the GnomeCanvasPathDef used by the
+ * GnomeCanvasShape. Notice, that it does not request updates, as
+ * it is meant to be used from item implementations, from inside
+ * update queue.
+ */
+ 
+void
+gnome_canvas_shape_set_path_def (GnomeCanvasShape *shape, GnomeCanvasPathDef *def) 
+{
+	GnomeCanvasShapePriv *priv;
+
+	g_return_if_fail (shape != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_SHAPE (shape));
+
+	priv = shape->priv;
+
+	if (priv->path) {
+		gnome_canvas_path_def_unref (priv->path);
+		priv->path = NULL;
+	}
+
+	if (def) {
+		priv->path = gnome_canvas_path_def_duplicate (def);
+	}
+}
+
+static void
+gnome_canvas_shape_set_property (GObject      *object,
+                                 guint         param_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+	GnomeCanvasItem         *item;
+	GnomeCanvasShape        *shape;
+	GnomeCanvasShapePriv    *priv;
+	GnomeCanvasShapePrivGdk *gdk;
+	GdkColor                 color;
+	GdkColor                *colorptr;
+	ArtVpathDash            *dash;
+
+	item = GNOME_CANVAS_ITEM (object);
+	shape = GNOME_CANVAS_SHAPE (object);
+	priv = shape->priv;
+
+	if (!item->canvas->aa) {
+		gcbp_ensure_gdk (shape);
+		gdk = priv->gdk;
+	} else {
+		gdk = NULL;
+	}
+
+	switch (param_id) {
+	case PROP_FILL_COLOR:
+		if (gnome_canvas_get_color (item->canvas, g_value_get_string (value), &color)) {
+			priv->fill_set = TRUE;
+			priv->fill_rgba = get_rgba_from_color (&color);
+			if (gdk) gdk->fill_pixel = color.pixel;
+		} else if (priv->fill_set)
+			priv->fill_set = FALSE;
+		else
+			break;
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_FILL_COLOR_GDK:
+		colorptr = g_value_get_boxed (value);
+		if (colorptr != NULL) {
+			priv->fill_set = TRUE;
+			priv->fill_rgba = get_rgba_from_color (colorptr);
+			if (gdk) {
+				GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+				GdkColor tmp = *colorptr;
+				gdk_rgb_find_color (colormap, &tmp);
+				gdk->fill_pixel = tmp.pixel;
+			}
+		} else if (priv->fill_set)
+			priv->fill_set = FALSE;
+		else
+			break;
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_FILL_COLOR_RGBA:
+		priv->fill_set = TRUE;
+		priv->fill_rgba = g_value_get_uint (value);
+		if (gdk) gdk->fill_pixel = get_pixel_from_rgba (item, priv->fill_rgba);
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_OUTLINE_COLOR:
+		if (gnome_canvas_get_color (item->canvas, g_value_get_string (value), &color)) {
+			priv->outline_set = TRUE;
+			priv->outline_rgba = get_rgba_from_color (&color);
+			if (gdk) gdk->outline_pixel = color.pixel;
+		} else if (priv->outline_set)
+			priv->outline_set = FALSE;
+		else
+			break;
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_OUTLINE_COLOR_GDK:
+		colorptr = g_value_get_boxed (value);
+		if (colorptr != NULL) {
+			priv->outline_set = TRUE;
+			priv->outline_rgba = get_rgba_from_color (colorptr);
+			if (gdk) {
+				GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+				GdkColor tmp = *colorptr;
+				gdk_rgb_find_color (colormap, &tmp);
+				gdk->outline_pixel = tmp.pixel;
+			}
+		} else if (priv->outline_set)
+			priv->outline_set = FALSE;
+		else
+			break;
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_OUTLINE_COLOR_RGBA:
+		priv->outline_set = TRUE;
+		priv->outline_rgba = g_value_get_uint (value);
+		if (gdk) gdk->outline_pixel = get_pixel_from_rgba (item, priv->outline_rgba);
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		if (gdk) {
+			set_stipple (gdk->fill_gc, &gdk->fill_stipple, (GdkBitmap*) g_value_get_object (value), FALSE);
+			gnome_canvas_item_request_update (item);
+		}
+		break;
+
+	case PROP_OUTLINE_STIPPLE:
+		if (gdk) {
+			set_stipple (gdk->outline_gc, &gdk->outline_stipple, (GdkBitmap*) g_value_get_object (value), FALSE);
+			gnome_canvas_item_request_update (item);
+		}
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		priv->width = g_value_get_uint (value);
+		priv->width_pixels = TRUE;
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIDTH_UNITS:
+		priv->width = fabs (g_value_get_double (value));
+		priv->width_pixels = FALSE;
+
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_WIND:
+		priv->wind = g_value_get_uint (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_CAP_STYLE:
+		priv->cap = g_value_get_enum (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_JOIN_STYLE:
+		priv->join = g_value_get_enum (value);
+		gnome_canvas_item_request_update (item);
+		break;
+	
+	case PROP_MITERLIMIT:
+		priv->miterlimit = g_value_get_double (value);
+		gnome_canvas_item_request_update (item);
+		break;
+
+	case PROP_DASH:
+		dash = g_value_get_pointer (value);
+		if (priv->dash.dash) g_free (priv->dash.dash);
+		priv->dash.dash = NULL;
+
+		if (dash) {
+			priv->dash.offset = dash->offset;
+			priv->dash.n_dash = dash->n_dash;
+			if (dash->dash != NULL) {
+				priv->dash.dash = g_new (double, dash->n_dash);
+				memcpy (priv->dash.dash, dash->dash, dash->n_dash * sizeof (double));
+			}
+		}
+		gnome_canvas_item_request_update (item);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* Allocates a GdkColor structure filled with the specified pixel, and
+ * puts it into the specified value for returning it in the get_property 
+ * method.
+ */
+
+static void
+get_color_value (GnomeCanvasShape *shape, gulong pixel, GValue *value)
+{
+	GnomeCanvas *canvas = GNOME_CANVAS_ITEM (shape)->canvas;
+	GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+	GdkColor color;
+
+	gdk_colormap_query_color (colormap, pixel, &color);
+	g_value_set_boxed (value, &color);
+}
+
+/**
+ * gnome_canvas_shape_get_path_def:
+ * @shape: a GnomeCanvasShape
+ *
+ * This function returns the #GnomeCanvasPathDef that the shape
+ * currently uses.  It adds a reference to the #GnomeCanvasPathDef and
+ * returns it, if there is not a #GnomeCanvasPathDef set for the shape
+ * it returns NULL.
+ *
+ * Returns: a #GnomeCanvasPathDef or NULL if none is set for the shape.
+ */
+ 
+GnomeCanvasPathDef *
+gnome_canvas_shape_get_path_def (GnomeCanvasShape *shape)
+{
+	GnomeCanvasShapePriv *priv;
+	
+	g_return_val_if_fail (shape != NULL, NULL);
+	g_return_val_if_fail (GNOME_IS_CANVAS_SHAPE (shape), NULL);
+
+	priv = shape->priv;
+
+	if (priv->path) {
+		gnome_canvas_path_def_ref (priv->path);
+		return priv->path;
+	}
+	
+	return NULL;
+}
+
+static void
+gnome_canvas_shape_get_property (GObject     *object,
+                                 guint        param_id,
+                                 GValue      *value,
+                                 GParamSpec  *pspec)
+{
+	GnomeCanvasItem         *item = GNOME_CANVAS_ITEM (object);
+	GnomeCanvasShape        *shape = GNOME_CANVAS_SHAPE (object);
+	GnomeCanvasShapePriv    *priv = shape->priv;
+	GnomeCanvasShapePrivGdk *gdk;
+
+	if (!item->canvas->aa) {
+		gcbp_ensure_gdk (shape);
+		gdk = priv->gdk;
+	}
+	else {
+		gdk = NULL;
+	}
+
+	switch (param_id) {
+	case PROP_FILL_COLOR_GDK:
+		if (gdk) {
+			get_color_value (shape, gdk->fill_pixel, value);
+		} else {
+			get_color_value (shape, 0, value);
+		}
+		break;
+		
+	case PROP_OUTLINE_COLOR_GDK:
+		if (gdk) {
+			get_color_value (shape, gdk->outline_pixel, value);
+		} else {
+			get_color_value (shape, 0, value);
+		}
+		break;
+
+	case PROP_FILL_COLOR_RGBA:
+		g_value_set_uint (value, priv->fill_rgba);
+		break;
+
+	case PROP_OUTLINE_COLOR_RGBA:
+		g_value_set_uint (value, priv->outline_rgba);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		if (gdk) {
+			g_value_set_object (value, gdk->fill_stipple);
+		} else {
+			g_value_set_object (value, NULL);
+		}
+		break;
+
+	case PROP_OUTLINE_STIPPLE:
+		if (gdk) {
+			g_value_set_object (value, gdk->outline_stipple);
+		} else {
+			g_value_set_object (value, NULL);
+		}
+		break;
+
+	case PROP_WIND:
+		g_value_set_uint (value, priv->wind);
+		break;
+
+	case PROP_CAP_STYLE:
+		g_value_set_enum (value, priv->cap);
+		break;
+
+	case PROP_JOIN_STYLE:
+		g_value_set_enum (value, priv->join);
+		break;
+
+	case PROP_WIDTH_PIXELS:
+		g_value_set_uint (value, priv->width);
+		break;
+
+	case PROP_WIDTH_UNITS:
+		g_value_set_double (value, priv->width);
+		break;
+
+	case PROP_MITERLIMIT:
+		g_value_set_double (value, priv->miterlimit);
+		break;
+
+	case PROP_DASH:
+		g_value_set_pointer (value, &priv->dash);
+		break;
+		
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_shape_realize (GnomeCanvasItem *item)
+{
+	GnomeCanvasShape *shape;
+
+	shape = GNOME_CANVAS_SHAPE (item);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (item);
+
+	if (!item->canvas->aa) {
+		gcbp_ensure_gdk (shape);
+
+		g_assert(item->canvas->layout.bin_window != NULL);
+
+		shape->priv->gdk->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+		shape->priv->gdk->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+	}
+}
+
+static void
+gnome_canvas_shape_unrealize (GnomeCanvasItem *item)
+{
+	GnomeCanvasShape *shape;
+
+	shape = GNOME_CANVAS_SHAPE (item);
+
+	if (!item->canvas->aa) {
+		g_assert (shape->priv->gdk != NULL);
+
+		gdk_gc_unref (shape->priv->gdk->fill_gc);
+		shape->priv->gdk->fill_gc = NULL;
+
+		gdk_gc_unref (shape->priv->gdk->outline_gc);
+		shape->priv->gdk->outline_gc = NULL;
+	}
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (item);
+}
+
+static void
+gnome_canvas_shape_render (GnomeCanvasItem *item,
+			     GnomeCanvasBuf *buf)
+{
+	GnomeCanvasShape *shape;
+
+	shape = GNOME_CANVAS_SHAPE (item);
+
+	if (shape->priv->fill_svp != NULL)
+		gnome_canvas_render_svp (buf,
+			shape->priv->fill_svp,
+			shape->priv->fill_rgba);
+
+	if (shape->priv->outline_svp != NULL)
+		gnome_canvas_render_svp (buf,
+			shape->priv->outline_svp,
+			shape->priv->outline_rgba);
+}
+
+static void
+gnome_canvas_shape_draw (GnomeCanvasItem *item,
+	GdkDrawable *drawable,
+	int x,
+	int y,
+	int width,
+	int height)
+{
+	static GdkPoint * dpoints = NULL;
+	static gint num_dpoints = 0;
+
+	GnomeCanvasShape * shape;
+	GnomeCanvasShapePriv * priv;
+	GnomeCanvasShapePrivGdk * gdk;
+	gint i, pos, len;
+	GSList * l;
+
+	shape = GNOME_CANVAS_SHAPE (item);
+	priv = shape->priv;
+
+	/* We have to be realized, so gdk struct should exist! */
+
+	gdk = shape->priv->gdk;
+	g_assert (gdk != NULL);
+
+	/* Build temporary point list, translated by -x, -y */
+
+	if (dpoints == NULL) {
+		dpoints = g_new (GdkPoint, gdk->num_points);
+		num_dpoints = gdk->num_points;
+	} else if (num_dpoints < gdk->num_points) {
+		dpoints = g_renew (GdkPoint, dpoints, gdk->num_points);
+		num_dpoints = gdk->num_points;
+	}
+
+	for (i = 0; i < gdk->num_points; i++) {
+		dpoints[i].x = gdk->points[i].x - x;
+		dpoints[i].y = gdk->points[i].y - y;
+	}
+
+	if (priv->fill_set) {
+
+		/* Ensure, that we have mask and it is big enough */
+
+		gcbp_ensure_mask (shape, width, height);
+
+		/* Clear mask */
+
+		gdk_draw_rectangle (gdk->ctx->mask,
+			gdk->ctx->clear_gc,
+			TRUE,
+			0, 0,
+			width, height);
+
+		/* Draw subpaths, using XOR gc */
+
+		pos = 0;
+
+		for (l = gdk->closed_paths; l != NULL; l = l->next) {
+			len = GPOINTER_TO_INT (l->data);
+
+			gdk_draw_polygon (gdk->ctx->mask,
+				gdk->ctx->xor_gc,
+				TRUE,
+				&dpoints[pos],
+				len);
+
+			pos += len;
+		}
+
+		/* Set bitmap to clipping mask */
+
+		gdk_gc_set_clip_mask (gdk->fill_gc, gdk->ctx->mask);
+
+		/* Stipple offset */
+
+		if (gdk->fill_stipple) gnome_canvas_set_stipple_origin (item->canvas, gdk->fill_gc);
+
+		/* Draw clipped rect to drawable */
+
+		gdk_draw_rectangle (drawable,
+			gdk->fill_gc,
+			TRUE,
+			0, 0,
+			width, height);
+	}
+
+	if (priv->outline_set) {
+
+		/* Stipple offset */
+
+		if (gdk->outline_stipple) gnome_canvas_set_stipple_origin (item->canvas, gdk->outline_gc);
+		/* Draw subpaths */
+
+		pos = 0;
+
+		for (l = gdk->closed_paths; l != NULL; l = l->next) {
+			len = GPOINTER_TO_INT (l->data);
+
+			gdk_draw_polygon (drawable,
+					  gdk->outline_gc,
+					  FALSE,
+					  &dpoints[pos],
+					  len);
+
+			pos += len;
+		}
+
+		for (l = gdk->open_paths; l != NULL; l = l->next) {
+			len = GPOINTER_TO_INT (l->data);
+
+			gdk_draw_lines (drawable,
+					gdk->outline_gc,
+					&dpoints[pos],
+					len);
+
+			pos += len;
+		}
+	}
+}
+
+#define GDK_POINTS_BLOCK 32
+
+static void
+gnome_canvas_shape_ensure_gdk_points (GnomeCanvasShapePrivGdk *gdk, gint num)
+{
+	if (gdk->len_points < gdk->num_points + num) {
+		gdk->len_points = MAX (gdk->len_points + GDK_POINTS_BLOCK, gdk->len_points + num);
+		gdk->points = g_renew (GdkPoint, gdk->points, gdk->len_points);
+	}
+}
+
+static void
+gnome_canvas_shape_update_gdk (GnomeCanvasShape * shape, double * affine, ArtSVP * clip, int flags)
+{
+	GnomeCanvasShapePriv * priv;
+	GnomeCanvasShapePrivGdk * gdk;
+	int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+	gboolean bbox_set = FALSE;
+	gint width = 0; /* silence gcc */
+	
+	g_assert (!((GnomeCanvasItem *) shape)->canvas->aa);
+
+	priv = shape->priv;
+	gdk = priv->gdk;
+	g_assert (gdk != NULL);
+
+	if (priv->outline_set) {
+		GdkLineStyle style;
+
+		if (priv->width_pixels) {
+			width = (int) floor (priv->width + 0.5);
+			/* Never select 0 pixels unless the user asked for it,
+			 * since that is the X11 zero width lines are non-specified */
+			if (priv->width_pixels != 0 && width == 0) {
+				width = 1;
+			}
+		} else {
+			width = (int) floor ((priv->width * priv->scale) + 0.5);
+			/* Never select 0 pixels unless the user asked for it,
+			 * since that is the X11 zero width lines are non-speciifed */
+			if (priv->width != 0 && width == 0) {
+				width = 1;
+			}
+		}
+
+		/* If dashed, set it in GdkGC */
+
+		if ((shape->priv->dash.dash != NULL) && (shape->priv->dash.n_dash > 0)) {
+			gint8 * dash_list;
+			gint i;
+
+			dash_list = g_new (gint8, shape->priv->dash.n_dash);
+
+			for (i = 0; i < priv->dash.n_dash; i++) {
+				dash_list[i] = (gint8) shape->priv->dash.dash[i];
+			}
+
+			gdk_gc_set_dashes (gdk->outline_gc,
+				(gint) priv->dash.offset,
+				dash_list,
+				priv->dash.n_dash);
+
+			g_free (dash_list);
+
+			style = GDK_LINE_ON_OFF_DASH;
+		} else {
+			style = GDK_LINE_SOLID;
+		}
+
+		/* Set line width, cap, join */
+		if(gdk->outline_gc) {
+			
+			gdk_gc_set_line_attributes (gdk->outline_gc,
+						    width,
+						    style,
+						    priv->cap,
+						    priv->join);
+			
+			/* Colors and stipples */
+			set_gc_foreground (gdk->outline_gc, gdk->outline_pixel);
+			set_stipple (gdk->outline_gc, &gdk->outline_stipple, gdk->outline_stipple, TRUE);
+		}
+	}
+
+	if (priv->fill_set) {
+
+		/* Colors and stipples */
+		if(gdk->fill_gc) {
+			set_gc_foreground (gdk->fill_gc, gdk->fill_pixel);
+			set_stipple (gdk->fill_gc, &gdk->fill_stipple, gdk->fill_stipple, TRUE);
+		}
+	}
+
+	/* Now the crazy part */
+
+	/* Free existing GdkPoint array */
+
+	if (gdk->points) {
+		g_free (gdk->points);
+		gdk->points = NULL;
+		gdk->len_points = 0;
+		gdk->num_points = 0;
+	}
+
+	/* Free subpath lists */
+
+	while (gdk->closed_paths) gdk->closed_paths = g_slist_remove (gdk->closed_paths, gdk->closed_paths->data);
+	while (gdk->open_paths) gdk->open_paths = g_slist_remove (gdk->open_paths, gdk->open_paths->data);
+
+	/* Calcualte new GdkPoints array and subpath lists */
+
+	if (priv->path) {
+		GnomeCanvasPathDef * apath, * cpath, * opath;
+		ArtBpath * abpath;
+		GSList * clist, * olist;
+		gint pos;
+
+#if 0
+		/* Allocate array */
+		gdk->num_points = gnome_canvas_path_def_length (priv->path) * 1000 - 1;
+		gdk->points = g_new (GdkPoint, gdk->num_points);
+		g_print ("Points %d\n", gdk->num_points);
+		/* Transform path */
+#endif
+
+		abpath = art_bpath_affine_transform (gnome_canvas_path_def_bpath (priv->path), affine);
+		apath = gnome_canvas_path_def_new_from_bpath (abpath);
+
+		/* Split path into open and closed parts */
+
+		cpath = gnome_canvas_path_def_closed_parts (apath);
+		opath = gnome_canvas_path_def_open_parts (apath);
+		gnome_canvas_path_def_unref (apath);
+
+		/* Split partial paths into subpaths */
+
+		clist = gnome_canvas_path_def_split (cpath);
+		gnome_canvas_path_def_unref (cpath);
+		olist = gnome_canvas_path_def_split (opath);
+		gnome_canvas_path_def_unref (opath);
+
+		pos = 0;
+
+		/* Fill GdkPoints and add subpaths to list: closed subpaths */
+
+		while (clist) {
+			GnomeCanvasPathDef * path;
+			ArtBpath * bpath;
+			ArtVpath * vpath;
+			gint len, i;
+
+			path = (GnomeCanvasPathDef *) clist->data;
+			bpath = gnome_canvas_path_def_bpath (path);
+			vpath = art_bez_path_to_vec (bpath, 0.1);
+			for (len = 0; vpath[len].code != ART_END; len++) ;
+
+			gnome_canvas_shape_ensure_gdk_points (gdk, len);
+			for (i = 0; i < len; i++) {
+				gdk->points[pos + i].x = (gint) floor (vpath[i].x + 0.5);
+				gdk->points[pos + i].y = (gint) floor (vpath[i].y + 0.5);
+
+				if (bbox_set) {
+					x1 = MIN (x1, gdk->points[pos + i].x);
+					x2 = MAX (x2, gdk->points[pos + i].x);
+					y1 = MIN (y1, gdk->points[pos + i].y);
+					y2 = MAX (y2, gdk->points[pos + i].y);
+				} else {
+					bbox_set = TRUE;
+					x1 = x2 = gdk->points[pos + i].x;
+					y1 = y2 = gdk->points[pos + i].y;
+				}
+			}
+			gdk->num_points += len;
+
+			art_free (vpath);
+
+			if (len > 0) {
+				pos += len;
+				gdk->closed_paths = g_slist_append (gdk->closed_paths, GINT_TO_POINTER (len));
+			}
+
+			gnome_canvas_path_def_unref (path);
+			clist = g_slist_remove (clist, clist->data);
+		}
+
+		/* Fill GdkPoints and add subpaths to list: open subpaths */
+
+		while (olist) {
+			GnomeCanvasPathDef * path;
+			ArtBpath * bpath;
+			ArtVpath * vpath;
+			gint len, i;
+
+			path = (GnomeCanvasPathDef *) olist->data;
+			bpath = gnome_canvas_path_def_bpath (path);
+			vpath = art_bez_path_to_vec (bpath, 0.1);
+			for (len = 0; vpath[len].code != ART_END; len++) ;
+
+			gnome_canvas_shape_ensure_gdk_points (gdk, len);
+			for (i = 0; i < len; i++) {
+				gdk->points[pos + i].x = (gint) floor (vpath[i].x + 0.5);
+				gdk->points[pos + i].y = (gint) floor (vpath[i].y + 0.5);
+				
+				if (bbox_set) {
+					x1 = MIN (x1, gdk->points[pos + i].x);
+					x2 = MAX (x2, gdk->points[pos + i].x);
+					y1 = MIN (y1, gdk->points[pos + i].y);
+					y2 = MAX (y2, gdk->points[pos + i].y);
+				} else {
+					bbox_set = TRUE;
+					x1 = x2 = gdk->points[pos + i].x;
+					y1 = y2 = gdk->points[pos + i].y;
+				}
+			}
+			gdk->num_points += len;
+
+			art_free (vpath);
+
+			if (len > 0) {
+				pos += len;
+				gdk->open_paths = g_slist_append (gdk->open_paths, GINT_TO_POINTER (len));
+			}
+
+			gnome_canvas_path_def_unref (path);
+			olist = g_slist_remove (olist, olist->data);
+		}
+
+	}
+
+	if (bbox_set) {
+		if (priv->outline_set) {
+			int stroke_border = (priv->join == GDK_JOIN_MITER)
+				? ceil (10.43*width/2) /* 10.43 is the miter limit for X11 */
+				: ceil (width/2);
+			x1 -= stroke_border;
+			x2 += stroke_border;
+			y1 -= stroke_border;
+			y2 += stroke_border;
+		}
+		
+		gnome_canvas_update_bbox (GNOME_CANVAS_ITEM (shape),
+					  x1, y1,
+					  x2 + 1, y2 + 1);
+	}
+	
+}
+
+static void
+gnome_canvas_shape_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasShape * shape;
+	GnomeCanvasShapePriv * priv;
+	ArtSVP * svp;
+
+	shape = GNOME_CANVAS_SHAPE (item);
+
+	priv = shape->priv;
+
+	/* Common part */
+	if (parent_class->update) {
+		(* parent_class->update) (item, affine, clip_path, flags);
+	}
+
+	/* Outline width */
+	shape->priv->scale = art_affine_expansion (affine);
+
+	/* Reset bbox */
+	if (item->canvas->aa) {
+		gnome_canvas_item_reset_bounds (item);
+	}
+	
+	/* Clipped fill SVP */
+
+	if ((priv->fill_set) && (priv->path) && (gnome_canvas_path_def_any_closed (priv->path))) {
+		GnomeCanvasPathDef * cpath;
+		ArtSvpWriter *swr;
+		ArtVpath *vpath;
+		ArtBpath *abp;
+		ArtSVP *svp2;
+
+		/* Get closed part of path */
+
+		cpath = gnome_canvas_path_def_closed_parts (shape->priv->path);
+		abp = art_bpath_affine_transform (gnome_canvas_path_def_bpath (cpath), affine);
+		gnome_canvas_path_def_unref (cpath);
+
+		/* Render, until SVP */
+
+		vpath = art_bez_path_to_vec (abp, 0.1);
+		art_free (abp);
+
+		svp = art_svp_from_vpath (vpath);
+		art_free (vpath);
+
+		swr = art_svp_writer_rewind_new (shape->priv->wind);
+		art_svp_intersector (svp, swr);
+
+		svp2 = art_svp_writer_rewind_reap (swr);
+		art_svp_free (svp);
+
+		if (item->canvas->aa) {
+			/* Update clipped path */
+			gnome_canvas_item_update_svp_clip (item,
+							   &shape->priv->fill_svp,
+							   svp2,
+							   clip_path);
+		} else {
+			if (priv->fill_svp) {
+				art_svp_free (priv->fill_svp);
+				priv->fill_svp = NULL;
+			}
+			/* No clipping */
+			shape->priv->fill_svp = svp2;
+		}
+	}
+
+	if (priv->outline_set && priv->path && !gnome_canvas_path_def_is_empty (priv->path)) {
+		gdouble width;
+		ArtBpath * abp;
+		ArtVpath * vpath;
+
+		/* Set linewidth */
+
+		if (priv->width_pixels) {
+			width = priv->width;
+		} else {
+			width = priv->width * priv->scale;
+		}
+		
+		if (width < 0.5) width = 0.5;
+		
+		/* Render full path until vpath */
+
+		abp = art_bpath_affine_transform (gnome_canvas_path_def_bpath (priv->path), affine);
+
+		vpath = art_bez_path_to_vec (abp, 0.1);
+		art_free (abp);
+
+		/* If dashed, apply dash */
+
+		if (priv->dash.dash != NULL)
+		{
+			ArtVpath *old = vpath;
+			
+			vpath = art_vpath_dash (old, &priv->dash);
+			art_free (old);
+		}
+		
+		/* Stroke vpath to SVP */
+
+		svp = art_svp_vpath_stroke (vpath,
+					    gnome_canvas_join_gdk_to_art (priv->join),
+					    gnome_canvas_cap_gdk_to_art (priv->cap),
+					    width,
+					    priv->miterlimit,
+					    0.25);
+		art_free (vpath);
+
+		if (item->canvas->aa) {
+			/* Update clipped */
+			gnome_canvas_item_update_svp_clip (item, &priv->outline_svp, svp, clip_path);
+		} else {
+			if (priv->outline_svp) {
+				art_svp_free (priv->outline_svp);
+				priv->outline_svp = NULL;
+			}
+			/* No clipping (yet) */
+			shape->priv->outline_svp = svp;
+		}
+	}
+
+	/* Gdk requires additional handling */
+	
+	if (!item->canvas->aa) {
+		gnome_canvas_shape_update_gdk (shape, affine, clip_path, flags);
+	}
+}
+
+static double
+gnome_canvas_shape_point (GnomeCanvasItem *item, double x, double y,
+			    int cx, int cy, GnomeCanvasItem **actual_item)
+{
+	GnomeCanvasShape *shape;
+	double dist;
+	int wind;
+
+#if 0
+	/* fixme: This is just for debugging, canvas should ensure that */
+	/* fixme: IF YOU ARE SURE THAT IT IS CORRECT BEHAVIOUR, you can remove warning */
+	/* fixme: and make it to return silently */
+	g_return_val_if_fail (!item->canvas->need_update, 1e18);
+#endif
+
+	shape = GNOME_CANVAS_SHAPE (item);
+
+	/* todo: update? */
+	if (shape->priv->fill_set && shape->priv->fill_svp) {
+		wind = art_svp_point_wind (shape->priv->fill_svp, cx, cy);
+		if ((shape->priv->wind == ART_WIND_RULE_NONZERO) && (wind != 0)) {
+			*actual_item = item;
+			return 0.0;
+		}
+		if ((shape->priv->wind == ART_WIND_RULE_ODDEVEN) && ((wind & 0x1) != 0)) {
+			*actual_item = item;
+			return 0.0;
+		}
+	}
+
+	if (shape->priv->outline_set && shape->priv->outline_svp) {
+		wind = art_svp_point_wind (shape->priv->outline_svp, cx, cy);
+		if (wind) {
+			*actual_item = item;
+			return 0.0;
+		}
+	}
+
+	if (shape->priv->outline_set && shape->priv->outline_svp) {
+		dist = art_svp_point_dist (shape->priv->outline_svp, cx, cy);
+	} else if (shape->priv->fill_set && shape->priv->outline_svp) {
+		dist = art_svp_point_dist (shape->priv->fill_svp, cx, cy);
+	} else {
+		return 1e12;
+	}
+
+	*actual_item = item;
+
+	return dist;
+}
+
+/* Helpers */
+
+/* Get 32bit rgba color from GdkColor */
+
+static guint32
+get_rgba_from_color (GdkColor * color)
+{
+	return ((color->red & 0xff00) << 16) | ((color->green & 0xff00) << 8) | (color->blue & 0xff00) | 0xff;
+}
+
+/* Get Gdk pixel value from 32bit rgba color */
+
+static gulong
+get_pixel_from_rgba (GnomeCanvasItem *item, guint32 rgba_color)
+{
+	return gnome_canvas_get_color_pixel (item->canvas, rgba_color);
+}
+
+/* Convenience function to set a GC's foreground color to the specified pixel value */
+
+static void
+set_gc_foreground (GdkGC *gc, gulong pixel)
+{
+	GdkColor c;
+
+	g_assert (gc != NULL);
+
+	c.pixel = pixel;
+
+	gdk_gc_set_foreground (gc, &c);
+}
+
+/* Sets the stipple pattern for the specified gc */
+
+static void
+set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure)
+{
+	if (*internal_stipple && !reconfigure)
+		gdk_bitmap_unref (*internal_stipple);
+
+	*internal_stipple = stipple;
+	if (stipple && !reconfigure)
+		gdk_bitmap_ref (stipple);
+
+	if (gc) {
+		if (stipple) {
+			gdk_gc_set_stipple (gc, stipple);
+			gdk_gc_set_fill (gc, GDK_STIPPLED);
+		} else
+			gdk_gc_set_fill (gc, GDK_SOLID);
+	}
+}
+
+/* Creates private Gdk struct, if not present */
+/* We cannot do it during ::init, as we have to know canvas */
+
+static void
+gcbp_ensure_gdk (GnomeCanvasShape * shape)
+{
+	g_assert (!((GnomeCanvasItem *) shape)->canvas->aa);
+
+	if (!shape->priv->gdk) {
+		GnomeCanvasShapePrivGdk * gdk;
+
+		gdk = g_new (GnomeCanvasShapePrivGdk, 1);
+
+		gdk->fill_pixel = get_pixel_from_rgba ((GnomeCanvasItem *) shape, shape->priv->fill_rgba);
+		gdk->outline_pixel = get_pixel_from_rgba ((GnomeCanvasItem *) shape, shape->priv->outline_rgba);
+
+		gdk->fill_stipple = NULL;
+		gdk->outline_stipple = NULL;
+
+		gdk->fill_gc = NULL;
+		gdk->outline_gc = NULL;
+
+		gdk->len_points = 0;
+		gdk->num_points = 0;
+		gdk->points = NULL;
+		gdk->closed_paths = NULL;
+		gdk->open_paths = NULL;
+
+		gdk->ctx = NULL;
+
+		shape->priv->gdk = gdk;
+	}
+}
+
+/* Destroy private Gdk struct */
+/* It is here, to make ::destroy implementation shorter :) */
+
+static void
+gcbp_destroy_gdk (GnomeCanvasShape * shape)
+{
+	GnomeCanvasShapePrivGdk * gdk;
+
+	g_assert (!((GnomeCanvasItem *)shape)->canvas->aa);
+
+	gdk = shape->priv->gdk;
+
+	if (gdk) {
+		g_assert (!gdk->fill_gc);
+		g_assert (!gdk->outline_gc);
+
+		if (gdk->fill_stipple)
+			gdk_bitmap_unref (gdk->fill_stipple);
+
+		if (gdk->outline_stipple)
+			gdk_bitmap_unref (gdk->outline_stipple);
+
+		if (gdk->points)
+			g_free (gdk->points);
+
+		while (gdk->closed_paths)
+			gdk->closed_paths = g_slist_remove (gdk->closed_paths, gdk->closed_paths->data);
+		while (gdk->open_paths)
+			gdk->open_paths = g_slist_remove (gdk->open_paths, gdk->open_paths->data);
+
+		if (gdk->ctx)
+			gcbp_draw_ctx_unref (gdk->ctx);
+
+		g_free (gdk);
+
+		shape->priv->gdk = NULL;
+	}
+}
+
+/*
+ * Ensure, that per-canvas Ctx struct is present and bitmaps are
+ * big enough, to mask full redraw area. Ctx is refcounted and
+ * defined as "BpathDrawCtx" data member on parent canvas
+ */
+
+static void
+gcbp_ensure_mask (GnomeCanvasShape * shape, gint width, gint height)
+{
+	GnomeCanvasShapePrivGdk * gdk;
+	GCBPDrawCtx * ctx;
+
+	gdk = shape->priv->gdk;
+	g_assert (gdk != NULL);
+	ctx = gdk->ctx;
+
+	if (!ctx) {
+		/* Ctx is not yet defined for us */
+
+		GnomeCanvas * canvas;
+
+		canvas = GNOME_CANVAS_ITEM (shape)->canvas;
+
+		ctx = g_object_get_data (G_OBJECT (canvas), "BpathDrawCtx");
+
+		if (!ctx) {
+			/* Ctx is not defined for parent canvas yet */
+
+			ctx = g_new (GCBPDrawCtx, 1);
+
+			ctx->refcount = 1;
+			ctx->canvas = canvas;
+			ctx->width = 0;
+			ctx->height = 0;
+
+			ctx->mask = NULL;
+			ctx->clip = NULL;
+
+			ctx->clear_gc = NULL;
+			ctx->xor_gc = NULL;
+
+			g_object_set_data (G_OBJECT (canvas), "BpathDrawCtx", ctx);
+
+		} else {
+			ctx->refcount++;
+		}
+
+		gdk->ctx = ctx;
+
+	}
+
+	/* Now we are sure, that ctx is present and properly refcounted */
+
+	if ((width > ctx->width) || (height > ctx->height)) {
+		/* Ctx is too small */
+
+		GdkWindow * window;
+
+		window = ((GtkWidget *) (((GnomeCanvasItem *) shape)->canvas))->window;
+
+		if (ctx->clear_gc) gdk_gc_unref (ctx->clear_gc);
+		if (ctx->xor_gc) gdk_gc_unref (ctx->xor_gc);
+		if (ctx->mask) gdk_bitmap_unref (ctx->mask);
+		if (ctx->clip) gdk_bitmap_unref (ctx->clip);
+
+		ctx->mask = gdk_pixmap_new (window, width, height, 1);
+		ctx->clip = NULL;
+
+		ctx->clear_gc = gdk_gc_new (ctx->mask);
+		gdk_gc_set_function (ctx->clear_gc, GDK_CLEAR);
+
+		ctx->xor_gc = gdk_gc_new (ctx->mask);
+		gdk_gc_set_function (ctx->xor_gc, GDK_INVERT);
+	}
+}
+
+/* It is cleaner to have it here, not in parent function */
+
+static void
+gcbp_draw_ctx_unref (GCBPDrawCtx * ctx)
+{
+	if (--ctx->refcount < 1) {
+		if (ctx->clear_gc)
+			gdk_gc_unref (ctx->clear_gc);
+		if (ctx->xor_gc)
+			gdk_gc_unref (ctx->xor_gc);
+
+		if (ctx->mask)
+			gdk_bitmap_unref (ctx->mask);
+		if (ctx->clip)
+			gdk_bitmap_unref (ctx->clip);
+		
+		g_object_set_data (G_OBJECT (ctx->canvas), "BpathDrawCtx", NULL);
+		g_free (ctx);
+	}
+}
+
+static void
+gnome_canvas_shape_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	GnomeCanvasShape * shape;
+	GnomeCanvasShapePriv * priv;
+	ArtDRect bbox;
+	ArtSVP * svp;
+
+	shape = GNOME_CANVAS_SHAPE (item);
+
+	priv = shape->priv;
+
+	bbox.x0 = *x1;
+	bbox.y0 = *y1;
+	bbox.x1 = *x2;
+	bbox.y1 = *y2;
+
+	if (priv->outline_set && priv->path && !gnome_canvas_path_def_is_empty (priv->path)) {
+		gdouble width;
+		ArtVpath * vpath;
+
+		/* Set linewidth */
+
+		if (priv->width_pixels) {
+			width = priv->width;
+		} else {
+			width = priv->width * priv->scale;
+		}
+		
+		if (width < 0.5) width = 0.5;
+		
+		/* Render full path until vpath */
+
+		vpath = art_bez_path_to_vec (gnome_canvas_path_def_bpath (priv->path), 0.1);
+
+		/* If dashed, apply dash */
+
+		if (priv->dash.dash != NULL)
+		{
+			ArtVpath *old = vpath;
+			
+			vpath = art_vpath_dash (old, &priv->dash);
+			art_free (old);
+		}
+		
+		/* Stroke vpath to SVP */
+
+		svp = art_svp_vpath_stroke (vpath,
+					    gnome_canvas_join_gdk_to_art (priv->join),
+					    gnome_canvas_cap_gdk_to_art (priv->cap),
+					    width,
+					    priv->miterlimit,
+					    0.25);
+		art_free (vpath);
+		art_drect_svp (&bbox, svp);
+		art_svp_free (svp);
+	} else if ((priv->fill_set) && (priv->path) && (gnome_canvas_path_def_any_closed (priv->path))) {
+		GnomeCanvasPathDef *cpath;
+		ArtSvpWriter *swr;
+		ArtVpath *vpath;
+		ArtSVP *svp2;
+
+		/* Get closed part of path */
+		cpath = gnome_canvas_path_def_closed_parts (shape->priv->path);
+		/* Render, until SVP */
+		vpath = art_bez_path_to_vec (gnome_canvas_path_def_bpath (cpath), 0.1);
+		gnome_canvas_path_def_unref (cpath);
+
+		svp = art_svp_from_vpath (vpath);
+		art_free (vpath);
+		
+		swr = art_svp_writer_rewind_new (shape->priv->wind);
+		art_svp_intersector (svp, swr);
+		
+		svp2 = art_svp_writer_rewind_reap (swr);
+		art_svp_free (svp);
+  
+		art_drect_svp (&bbox, svp2);
+		art_svp_free (svp2);
+	}
+
+	*x1 = bbox.x0;
+	*y1 = bbox.y0;
+	*x2 = bbox.x1;
+	*y2 = bbox.y1;
+}
diff --git a/src/libgnomecanvas/gnome-canvas-shape.h b/src/libgnomecanvas/gnome-canvas-shape.h
new file mode 100644
index 0000000..d645465
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-shape.h
@@ -0,0 +1,81 @@
+/* Generic bezier shape item for GnomeCanvas
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Copyright (C) 1998,1999 The Free Software Foundation
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph acm org>
+ *          Lauris Kaplinski <lauris ximian com>
+ *          Rusty Conover <rconover bangtail net>
+ */
+
+#ifndef GNOME_CANVAS_SHAPE_H
+#define GNOME_CANVAS_SHAPE_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-path-def.h>
+
+G_BEGIN_DECLS
+
+
+/* Shape item for the canvas.
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * fill_color		string			W		X color specification for fill color,
+ *								or NULL pointer for no color (transparent).
+ * fill_color_gdk	GdkColor*		RW		Allocated GdkColor for fill.
+ * outline_color	string			W		X color specification for outline color,
+ *								or NULL pointer for no color (transparent).
+ * outline_color_gdk	GdkColor*		RW		Allocated GdkColor for outline.
+ * fill_stipple		GdkBitmap*		RW		Stipple pattern for fill
+ * outline_stipple	GdkBitmap*		RW		Stipple pattern for outline
+ * width_pixels		uint			RW		Width of the outline in pixels.  The outline will
+ *								not be scaled when the canvas zoom factor is changed.
+ * width_units		double			RW		Width of the outline in canvas units.  The outline
+ *								will be scaled when the canvas zoom factor is changed.
+ * cap_style		GdkCapStyle		RW		Cap ("endpoint") style for the bpath.
+ * join_style		GdkJoinStyle		RW		Join ("vertex") style for the bpath.
+ * wind                 ArtWindRule             RW              Winding rule for the bpath.
+ * dash			ArtVpathDash		RW		Dashing pattern
+ * miterlimit		double			RW		Minimum angle between segments, where miter join
+ *								rule is applied.
+ */
+
+#define GNOME_TYPE_CANVAS_SHAPE            (gnome_canvas_shape_get_type ())
+#define GNOME_CANVAS_SHAPE(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_SHAPE, GnomeCanvasShape))
+#define GNOME_CANVAS_SHAPE_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_SHAPE, GnomeCanvasShapeClass))
+#define GNOME_IS_CANVAS_SHAPE(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_SHAPE))
+#define GNOME_IS_CANVAS_SHAPE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_SHAPE))
+
+
+typedef struct _GnomeCanvasShape GnomeCanvasShape;
+typedef struct _GnomeCanvasShapePriv GnomeCanvasShapePriv;
+typedef struct _GnomeCanvasShapeClass GnomeCanvasShapeClass;
+
+struct _GnomeCanvasShape {
+	GnomeCanvasItem item;
+
+	GnomeCanvasShapePriv *priv;	/* Private data */
+};
+
+struct _GnomeCanvasShapeClass {
+	GnomeCanvasItemClass parent_class;
+};
+
+
+/* WARNING! These are not usable from modifying shapes from user programs */
+/* These are meant, to set master shape from subclass ::update method */
+void gnome_canvas_shape_set_path_def (GnomeCanvasShape *shape, GnomeCanvasPathDef *def);
+GnomeCanvasPathDef *gnome_canvas_shape_get_path_def (GnomeCanvasShape *shape);
+
+/* Standard Gtk function */
+GType gnome_canvas_shape_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-text.c b/src/libgnomecanvas/gnome-canvas-text.c
new file mode 100644
index 0000000..1b9d050
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-text.c
@@ -0,0 +1,1740 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * $Id: gnome-canvas-text.c 1192 2006-11-22 12:28:00Z herzi $
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Text item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas
+ * widget.  Tk is copyrighted by the Regents of the University of California,
+ * Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ * Port to Pango co-done by Gergõ Érdi <cactus cactus rulez org>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <string.h>
+#include "gnome-canvas-text.h"
+#include <pango/pangoft2.h>
+
+#include "libart_lgpl/art_affine.h"
+#include "libart_lgpl/art_rgb_a_affine.h"
+#include "libart_lgpl/art_rgb.h"
+#include "libart_lgpl/art_rgb_bitmap_affine.h"
+#include "gnome-canvas-util.h"
+#include "gnome-canvas-i18n.h"
+
+
+
+/* Object argument IDs */
+enum {
+	PROP_0,
+
+	/* Text contents */
+	PROP_TEXT,
+	PROP_MARKUP,
+
+	/* Position */
+	PROP_X,
+	PROP_Y,
+
+	/* Font */
+	PROP_FONT,
+	PROP_FONT_DESC,
+	PROP_FAMILY, PROP_FAMILY_SET,
+
+	/* Style */
+	PROP_ATTRIBUTES,
+	PROP_STYLE,         PROP_STYLE_SET,
+	PROP_VARIANT,       PROP_VARIANT_SET,
+	PROP_WEIGHT,        PROP_WEIGHT_SET,
+	PROP_STRETCH,	    PROP_STRETCH_SET,
+	PROP_SIZE,          PROP_SIZE_SET,
+	PROP_SIZE_POINTS,
+	PROP_STRIKETHROUGH, PROP_STRIKETHROUGH_SET,
+	PROP_UNDERLINE,     PROP_UNDERLINE_SET,
+	PROP_RISE,          PROP_RISE_SET,
+	PROP_SCALE,         PROP_SCALE_SET,
+
+	/* Clipping */
+	PROP_ANCHOR,
+	PROP_JUSTIFICATION,
+	PROP_CLIP_WIDTH,
+	PROP_CLIP_HEIGHT,
+	PROP_CLIP,
+	PROP_X_OFFSET,
+	PROP_Y_OFFSET,
+
+	/* Coloring */
+	PROP_FILL_COLOR,
+	PROP_FILL_COLOR_GDK,
+	PROP_FILL_COLOR_RGBA,
+	PROP_FILL_STIPPLE,
+
+	/* Rendered size accessors */
+	PROP_TEXT_WIDTH,
+	PROP_TEXT_HEIGHT
+};
+
+struct _GnomeCanvasTextPrivate {
+	guint render_dirty : 1;
+	FT_Bitmap bitmap;
+};
+
+
+static void gnome_canvas_text_class_init (GnomeCanvasTextClass *class);
+static void gnome_canvas_text_init (GnomeCanvasText *text);
+static void gnome_canvas_text_destroy (GtkObject *object);
+static void gnome_canvas_text_set_property (GObject            *object,
+					    guint               param_id,
+					    const GValue       *value,
+					    GParamSpec         *pspec);
+static void gnome_canvas_text_get_property (GObject            *object,
+					    guint               param_id,
+					    GValue             *value,
+					    GParamSpec         *pspec);
+
+static void gnome_canvas_text_update (GnomeCanvasItem *item, double *affine,
+				      ArtSVP *clip_path, int flags);
+static void gnome_canvas_text_realize (GnomeCanvasItem *item);
+static void gnome_canvas_text_unrealize (GnomeCanvasItem *item);
+static void gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+				    int x, int y, int width, int height);
+static double gnome_canvas_text_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+				       GnomeCanvasItem **actual_item);
+static void gnome_canvas_text_bounds (GnomeCanvasItem *item,
+				      double *x1, double *y1, double *x2, double *y2);
+static void gnome_canvas_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+static void gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
+					  const gchar     *markup);
+
+static void gnome_canvas_text_set_font_desc    (GnomeCanvasText *textitem,
+					        PangoFontDescription *font_desc);
+
+static void gnome_canvas_text_apply_font_desc  (GnomeCanvasText *textitem);
+static void gnome_canvas_text_apply_attributes (GnomeCanvasText *textitem);
+
+static void add_attr (PangoAttrList  *attr_list,
+		      PangoAttribute *attr);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+
+/**
+ * gnome_canvas_text_get_type:
+ * @void:
+ *
+ * Registers the &GnomeCanvasText class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value: The type ID of the &GnomeCanvasText class.
+ **/
+GType
+gnome_canvas_text_get_type (void)
+{
+	static GType text_type;
+
+	if (!text_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasTextClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_text_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasText),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_text_init,
+			NULL			/* value_table */
+		};
+
+		text_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasText",
+						    &object_info, 0);
+	}
+
+	return text_type;
+}
+
+/* Class initialization function for the text item */
+static void
+gnome_canvas_text_class_init (GnomeCanvasTextClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_text_set_property;
+	gobject_class->get_property = gnome_canvas_text_get_property;
+
+	/* Text */
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_TEXT,
+                 g_param_spec_string ("text",
+				      "Text",
+				      "Text to render",
+                                      NULL,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_MARKUP,
+                 g_param_spec_string ("markup",
+				      "Markup",
+				      "Marked up text to render",
+				      NULL,
+                                      (G_PARAM_WRITABLE)));
+
+	/* Position */
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X,
+                 g_param_spec_double ("x", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y,
+                 g_param_spec_double ("y", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+
+	/* Font */
+	g_object_class_install_property
+                (gobject_class,
+                 PROP_FONT,
+                 g_param_spec_string ("font",
+				      "Font",
+				      "Font description as a string",
+                                      NULL,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+        g_object_class_install_property
+		(gobject_class,
+		 PROP_FONT_DESC,
+		 g_param_spec_boxed ("font_desc",
+				     "Font description",
+				     "Font description as a PangoFontDescription struct",
+				     PANGO_TYPE_FONT_DESCRIPTION,
+				     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_FAMILY,
+		 g_param_spec_string ("family",
+				      "Font family",
+				      "Name of the font family, e.g. Sans, Helvetica, Times, Monospace",
+				      NULL,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	/* Style */
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ATTRIBUTES,
+                 g_param_spec_boxed ("attributes", NULL, NULL,
+				     PANGO_TYPE_ATTR_LIST,
+				     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_STYLE,
+		 g_param_spec_enum ("style",
+				    "Font style",
+				    "Font style",
+				    PANGO_TYPE_STYLE,
+				    PANGO_STYLE_NORMAL,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_VARIANT,
+		 g_param_spec_enum ("variant",
+				    "Font variant",
+				    "Font variant",
+				    PANGO_TYPE_VARIANT,
+				    PANGO_VARIANT_NORMAL,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_WEIGHT,
+		 g_param_spec_int ("weight",
+				   "Font weight",
+				   "Font weight",
+				   0,
+				   G_MAXINT,
+				   PANGO_WEIGHT_NORMAL,
+				   G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_STRETCH,
+		 g_param_spec_enum ("stretch",
+				    "Font stretch",
+				    "Font stretch",
+				    PANGO_TYPE_STRETCH,
+				    PANGO_STRETCH_NORMAL,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_SIZE,
+		 g_param_spec_int ("size",
+				   "Font size",
+				   "Font size (as a multiple of PANGO_SCALE, eg. 12*PANGO_SCALE for a 12pt font size)",
+				   0,
+				   G_MAXINT,
+				   0,
+				   G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		PROP_SIZE_POINTS,
+		g_param_spec_double ("size_points",
+				     "Font points",
+				     "Font size in points (eg. 12 for a 12pt font size)",
+				     0.0,
+				     G_MAXDOUBLE,
+				     0.0,
+				     G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_RISE,
+		 g_param_spec_int ("rise",
+				   "Rise",
+				   "Offset of text above the baseline (below the baseline if rise is negative)",
+				   -G_MAXINT,
+				   G_MAXINT,
+				   0,
+				   G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_STRIKETHROUGH,
+		 g_param_spec_boolean ("strikethrough",
+				       "Strikethrough",
+				       "Whether to strike through the text",
+				       FALSE,
+				       G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_UNDERLINE,
+		 g_param_spec_enum ("underline",
+				    "Underline",
+				    "Style of underline for this text",
+				    PANGO_TYPE_UNDERLINE,
+				    PANGO_UNDERLINE_NONE,
+				    G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+	g_object_class_install_property
+		(gobject_class,
+		 PROP_SCALE,
+		 g_param_spec_double ("scale",
+				      "Scale",
+				      "Size of font, relative to default size",
+				      0.0,
+				      G_MAXDOUBLE,
+				      1.0,
+				      G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+        g_object_class_install_property
+		(gobject_class,
+                 PROP_ANCHOR,
+                 g_param_spec_enum ("anchor", NULL, NULL,
+                                    GTK_TYPE_ANCHOR_TYPE,
+                                    GTK_ANCHOR_CENTER,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_JUSTIFICATION,
+                 g_param_spec_enum ("justification", NULL, NULL,
+                                    GTK_TYPE_JUSTIFICATION,
+                                    GTK_JUSTIFY_LEFT,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CLIP_WIDTH,
+                 g_param_spec_double ("clip_width", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CLIP_HEIGHT,
+                 g_param_spec_double ("clip_height", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_CLIP,
+                 g_param_spec_boolean ("clip", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X_OFFSET,
+                 g_param_spec_double ("x_offset", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y_OFFSET,
+                 g_param_spec_double ("y_offset", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR,
+                 g_param_spec_string ("fill_color",
+				      "Color",
+				      "Text color, as string",
+                                      NULL,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_GDK,
+                 g_param_spec_boxed ("fill_color_gdk",
+				     "Color",
+				     "Text color, as a GdkColor",
+				     GDK_TYPE_COLOR,
+				     (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_COLOR_RGBA,
+                 g_param_spec_uint ("fill_color_rgba",
+				    "Color",
+				    "Text color, as an R/G/B/A combined integer",
+				    0, G_MAXUINT, 0,
+				    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_FILL_STIPPLE,
+                 g_param_spec_object ("fill_stipple", NULL, NULL,
+                                      GDK_TYPE_DRAWABLE,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_TEXT_WIDTH,
+                 g_param_spec_double ("text_width",
+				      "Text width",
+				      "Width of the rendered text",
+				      0.0, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READABLE));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_TEXT_HEIGHT,
+                 g_param_spec_double ("text_height",
+				      "Text height",
+				      "Height of the rendered text",
+				      0.0, G_MAXDOUBLE, 0.0,
+				      G_PARAM_READABLE));
+
+	/* Style props are set (explicitly applied) or not */
+#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (gobject_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE))
+
+	ADD_SET_PROP ("family_set", PROP_FAMILY_SET,
+		      "Font family set",
+		      "Whether this tag affects the font family");
+
+	ADD_SET_PROP ("style_set", PROP_STYLE_SET,
+		      "Font style set",
+		      "Whether this tag affects the font style");
+
+	ADD_SET_PROP ("variant_set", PROP_VARIANT_SET,
+		      "Font variant set",
+		      "Whether this tag affects the font variant");
+
+	ADD_SET_PROP ("weight_set", PROP_WEIGHT_SET,
+		      "Font weight set",
+		      "Whether this tag affects the font weight");
+
+	ADD_SET_PROP ("stretch_set", PROP_STRETCH_SET,
+		      "Font stretch set",
+		      "Whether this tag affects the font stretch");
+
+	ADD_SET_PROP ("size_set", PROP_SIZE_SET,
+		      "Font size set",
+		      "Whether this tag affects the font size");
+
+	ADD_SET_PROP ("rise_set", PROP_RISE_SET,
+		      "Rise set",
+		      "Whether this tag affects the rise");
+
+	ADD_SET_PROP ("strikethrough_set", PROP_STRIKETHROUGH_SET,
+		      "Strikethrough set",
+		      "Whether this tag affects strikethrough");
+
+	ADD_SET_PROP ("underline_set", PROP_UNDERLINE_SET,
+		      "Underline set",
+		      "Whether this tag affects underlining");
+
+	ADD_SET_PROP ("scale_set", PROP_SCALE_SET,
+		      "Scale set",
+		      "Whether this tag affects font scaling");
+#undef ADD_SET_PROP
+
+	object_class->destroy = gnome_canvas_text_destroy;
+
+	item_class->update = gnome_canvas_text_update;
+	item_class->realize = gnome_canvas_text_realize;
+	item_class->unrealize = gnome_canvas_text_unrealize;
+	item_class->draw = gnome_canvas_text_draw;
+	item_class->point = gnome_canvas_text_point;
+	item_class->bounds = gnome_canvas_text_bounds;
+	item_class->render = gnome_canvas_text_render;
+}
+
+/* Object initialization function for the text item */
+static void
+gnome_canvas_text_init (GnomeCanvasText *text)
+{
+	text->x = 0.0;
+	text->y = 0.0;
+	text->anchor = GTK_ANCHOR_CENTER;
+	text->justification = GTK_JUSTIFY_LEFT;
+	text->clip_width = 0.0;
+	text->clip_height = 0.0;
+	text->xofs = 0.0;
+	text->yofs = 0.0;
+	text->layout = NULL;
+
+	text->font_desc = NULL;
+
+	text->underline     = PANGO_UNDERLINE_NONE;
+	text->strikethrough = FALSE;
+	text->rise          = 0;
+
+	text->underline_set = FALSE;
+	text->strike_set    = FALSE;
+	text->rise_set      = FALSE;
+
+	text->priv = g_new (GnomeCanvasTextPrivate, 1);
+	text->priv->bitmap.buffer = NULL;
+	text->priv->render_dirty = 1;
+}
+
+/* Destroy handler for the text item */
+static void
+gnome_canvas_text_destroy (GtkObject *object)
+{
+	GnomeCanvasText *text;
+
+	g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+	text = GNOME_CANVAS_TEXT (object);
+
+	/* remember, destroy can be run multiple times! */
+
+	g_free (text->text);
+	text->text = NULL;
+
+	if (text->layout)
+	    g_object_unref (G_OBJECT (text->layout));
+	text->layout = NULL;
+
+	if (text->font_desc) {
+		pango_font_description_free (text->font_desc);
+		text->font_desc = NULL;
+	}
+
+	if (text->attr_list)
+		pango_attr_list_unref (text->attr_list);
+	text->attr_list = NULL;
+
+	if (text->stipple)
+		gdk_bitmap_unref (text->stipple);
+	text->stipple = NULL;
+
+	if (text->priv && text->priv->bitmap.buffer) {
+		g_free (text->priv->bitmap.buffer);
+	}
+	g_free (text->priv);
+	text->priv = NULL;
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+get_bounds (GnomeCanvasText *text, double *px1, double *py1, double *px2, double *py2)
+{
+	GnomeCanvasItem *item;
+	double wx, wy;
+
+	item = GNOME_CANVAS_ITEM (text);
+
+	/* Get canvas pixel coordinates for text position */
+
+
+	wx = text->x;
+	wy = text->y;
+	gnome_canvas_item_i2w (item, &wx, &wy);
+	gnome_canvas_w2c (item->canvas, wx + text->xofs, wy + text->yofs, &text->cx, &text->cy);
+
+	/* Get canvas pixel coordinates for clip rectangle position */
+
+	gnome_canvas_w2c (item->canvas, wx, wy, &text->clip_cx, &text->clip_cy);
+	text->clip_cwidth = text->clip_width * item->canvas->pixels_per_unit;
+	text->clip_cheight = text->clip_height * item->canvas->pixels_per_unit;
+
+	/* Anchor text */
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		text->cx -= text->max_width / 2;
+		text->clip_cx -= text->clip_cwidth / 2;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		text->cx -= text->max_width;
+		text->clip_cx -= text->clip_cwidth;
+		break;
+
+	default:
+		break;
+	}
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		text->cy -= text->height / 2;
+		text->clip_cy -= text->clip_cheight / 2;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		text->cy -= text->height;
+		text->clip_cy -= text->clip_cheight;
+		break;
+
+	default:
+		break;
+	}
+
+	/* Bounds */
+
+	if (text->clip) {
+		*px1 = text->clip_cx;
+		*py1 = text->clip_cy;
+		*px2 = text->clip_cx + text->clip_cwidth;
+		*py2 = text->clip_cy + text->clip_cheight;
+	} else {
+		*px1 = text->cx;
+		*py1 = text->cy;
+		*px2 = text->cx + text->max_width;
+		*py2 = text->cy + text->height;
+	}
+}
+
+/* Convenience function to set the text's GC's foreground color */
+static void
+set_text_gc_foreground (GnomeCanvasText *text)
+{
+	GdkColor c;
+
+	if (!text->gc)
+		return;
+
+	c.pixel = text->pixel;
+	gdk_gc_set_foreground (text->gc, &c);
+}
+
+/* Sets the stipple pattern for the text */
+static void
+set_stipple (GnomeCanvasText *text, GdkBitmap *stipple, int reconfigure)
+{
+	if (text->stipple && !reconfigure)
+		gdk_bitmap_unref (text->stipple);
+
+	text->stipple = stipple;
+	if (stipple && !reconfigure)
+		gdk_bitmap_ref (stipple);
+
+	if (text->gc) {
+		if (stipple) {
+			gdk_gc_set_stipple (text->gc, stipple);
+			gdk_gc_set_fill (text->gc, GDK_STIPPLED);
+		} else
+			gdk_gc_set_fill (text->gc, GDK_SOLID);
+	}
+}
+
+static PangoFontMask
+get_property_font_set_mask (guint prop_id)
+{
+  switch (prop_id)
+    {
+    case PROP_FAMILY_SET:
+      return PANGO_FONT_MASK_FAMILY;
+    case PROP_STYLE_SET:
+      return PANGO_FONT_MASK_STYLE;
+    case PROP_VARIANT_SET:
+      return PANGO_FONT_MASK_VARIANT;
+    case PROP_WEIGHT_SET:
+      return PANGO_FONT_MASK_WEIGHT;
+    case PROP_STRETCH_SET:
+      return PANGO_FONT_MASK_STRETCH;
+    case PROP_SIZE_SET:
+      return PANGO_FONT_MASK_SIZE;
+    }
+
+  return 0;
+}
+
+static void
+ensure_font (GnomeCanvasText *text)
+{
+	if (!text->font_desc)
+		text->font_desc = pango_font_description_new ();
+}
+
+/* Set_arg handler for the text item */
+static void
+gnome_canvas_text_set_property (GObject            *object,
+				guint               param_id,
+				const GValue       *value,
+				GParamSpec         *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasText *text;
+	GdkColor color = { 0, 0, 0, 0, };
+	GdkColor *pcolor;
+	gboolean color_changed;
+	int have_pixel;
+	PangoAlignment align;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	text = GNOME_CANVAS_TEXT (object);
+
+	color_changed = FALSE;
+	have_pixel = FALSE;
+
+
+	if (!text->layout) {
+
+	        PangoContext *gtk_context, *context;
+		gtk_context = gtk_widget_get_pango_context (GTK_WIDGET (item->canvas));
+
+	        if (item->canvas->aa)  {
+			PangoLanguage *language;
+			gint	pixels, mm;
+			double	dpi_x;
+			double	dpi_y;
+
+			pixels = gdk_screen_width ();
+			mm = gdk_screen_width_mm ();
+			dpi_x = (((double) pixels * 25.4) / (double) mm);
+
+			pixels = gdk_screen_height ();
+			mm = gdk_screen_height_mm ();
+			dpi_y = (((double) pixels * 25.4) / (double) mm);
+
+		        context = pango_ft2_get_context (dpi_x, dpi_y);
+			language = pango_context_get_language (gtk_context);
+			pango_context_set_language (context, language);
+			pango_context_set_base_dir (context,
+						    pango_context_get_base_dir (gtk_context));
+			pango_context_set_font_description (context,
+							    pango_context_get_font_description (gtk_context));
+
+		} else
+			context = gtk_context;
+
+
+		text->layout = pango_layout_new (context);
+
+	        if (item->canvas->aa)
+		        g_object_unref (G_OBJECT (context));
+	}
+
+	switch (param_id) {
+	case PROP_TEXT:
+		g_free (text->text);
+
+		text->text = g_value_dup_string (value);
+		pango_layout_set_text (text->layout, text->text, -1);
+
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_MARKUP:
+		gnome_canvas_text_set_markup (text,
+					      g_value_get_string (value));
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_X:
+		text->x = g_value_get_double (value);
+		break;
+
+	case PROP_Y:
+		text->y = g_value_get_double (value);
+		break;
+
+	case PROP_FONT: {
+		const char *font_name;
+		PangoFontDescription *font_desc;
+
+		font_name = g_value_get_string (value);
+		if (font_name)
+			font_desc = pango_font_description_from_string (font_name);
+		else
+			font_desc = NULL;
+
+		gnome_canvas_text_set_font_desc (text, font_desc);
+		if (font_desc)
+			pango_font_description_free (font_desc);
+
+		break;
+	}
+
+	case PROP_FONT_DESC:
+		gnome_canvas_text_set_font_desc (text, g_value_peek_pointer (value));
+		break;
+
+	case PROP_FAMILY:
+	case PROP_STYLE:
+	case PROP_VARIANT:
+	case PROP_WEIGHT:
+	case PROP_STRETCH:
+	case PROP_SIZE:
+	case PROP_SIZE_POINTS:
+		ensure_font (text);
+
+		switch (param_id) {
+		case PROP_FAMILY:
+			pango_font_description_set_family (text->font_desc,
+							   g_value_get_string (value));
+			break;
+		case PROP_STYLE:
+			pango_font_description_set_style (text->font_desc,
+							  g_value_get_enum (value));
+			break;
+		case PROP_VARIANT:
+			pango_font_description_set_variant (text->font_desc,
+							    g_value_get_enum (value));
+			break;
+		case PROP_WEIGHT:
+			pango_font_description_set_weight (text->font_desc,
+							   g_value_get_int (value));
+			break;
+		case PROP_STRETCH:
+			pango_font_description_set_stretch (text->font_desc,
+							    g_value_get_enum (value));
+			break;
+		case PROP_SIZE:
+			/* FIXME: This is bogus! It should be pixels, not points/PANGO_SCALE! */
+			pango_font_description_set_size (text->font_desc,
+							 g_value_get_int (value));
+			break;
+		case PROP_SIZE_POINTS:
+			pango_font_description_set_size (text->font_desc,
+							 g_value_get_double (value) * PANGO_SCALE);
+			break;
+		}
+
+		gnome_canvas_text_apply_font_desc (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_FAMILY_SET:
+	case PROP_STYLE_SET:
+	case PROP_VARIANT_SET:
+	case PROP_WEIGHT_SET:
+	case PROP_STRETCH_SET:
+	case PROP_SIZE_SET:
+		if (!g_value_get_boolean (value) && text->font_desc)
+			pango_font_description_unset_fields (text->font_desc,
+							     get_property_font_set_mask (param_id));
+		break;
+
+	case PROP_SCALE:
+		text->scale = g_value_get_double (value);
+		text->scale_set = TRUE;
+
+		gnome_canvas_text_apply_font_desc (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_SCALE_SET:
+		text->scale_set = g_value_get_boolean (value);
+
+		gnome_canvas_text_apply_font_desc (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_UNDERLINE:
+		text->underline = g_value_get_enum (value);
+		text->underline_set = TRUE;
+
+		gnome_canvas_text_apply_attributes (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_UNDERLINE_SET:
+		text->underline_set = g_value_get_boolean (value);
+
+		gnome_canvas_text_apply_attributes (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_STRIKETHROUGH:
+		text->strikethrough = g_value_get_boolean (value);
+		text->strike_set = TRUE;
+
+		gnome_canvas_text_apply_attributes (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_STRIKETHROUGH_SET:
+		text->strike_set = g_value_get_boolean (value);
+
+		gnome_canvas_text_apply_attributes (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_RISE:
+		text->rise = g_value_get_int (value);
+		text->rise_set = TRUE;
+
+		gnome_canvas_text_apply_attributes (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_RISE_SET:
+		text->rise_set = TRUE;
+
+		gnome_canvas_text_apply_attributes (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_ATTRIBUTES:
+		if (text->attr_list)
+			pango_attr_list_unref (text->attr_list);
+
+		text->attr_list = g_value_peek_pointer (value);
+		pango_attr_list_ref (text->attr_list);
+
+		gnome_canvas_text_apply_attributes (text);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_ANCHOR:
+		text->anchor = g_value_get_enum (value);
+		break;
+
+	case PROP_JUSTIFICATION:
+		text->justification = g_value_get_enum (value);
+
+		switch (text->justification) {
+		case GTK_JUSTIFY_LEFT:
+		        align = PANGO_ALIGN_LEFT;
+			break;
+		case GTK_JUSTIFY_CENTER:
+		        align = PANGO_ALIGN_CENTER;
+			break;
+		case GTK_JUSTIFY_RIGHT:
+		        align = PANGO_ALIGN_RIGHT;
+			break;
+		default:
+		        /* GTK_JUSTIFY_FILL isn't supported yet. */
+		        align = PANGO_ALIGN_LEFT;
+			break;
+		}
+		pango_layout_set_alignment (text->layout, align);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_CLIP_WIDTH:
+		text->clip_width = fabs (g_value_get_double (value));
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_CLIP_HEIGHT:
+		text->clip_height = fabs (g_value_get_double (value));
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_CLIP:
+		text->clip = g_value_get_boolean (value);
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_X_OFFSET:
+		text->xofs = g_value_get_double (value);
+		break;
+
+	case PROP_Y_OFFSET:
+		text->yofs = g_value_get_double (value);
+		break;
+
+        case PROP_FILL_COLOR: {
+		const char *color_name;
+
+		color_name = g_value_get_string (value);
+		if (color_name) {
+			gdk_color_parse (color_name, &color);
+
+			text->rgba = ((color.red & 0xff00) << 16 |
+				      (color.green & 0xff00) << 8 |
+				      (color.blue & 0xff00) |
+				      0xff);
+			color_changed = TRUE;
+		}
+		text->priv->render_dirty = 1;
+		break;
+	}
+
+	case PROP_FILL_COLOR_GDK:
+		pcolor = g_value_get_boxed (value);
+		if (pcolor) {
+		    GdkColormap *colormap;
+
+		    color = *pcolor;
+		    colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas));
+		    gdk_rgb_find_color (colormap, &color);
+		    have_pixel = TRUE;
+		}
+
+		text->rgba = ((color.red & 0xff00) << 16 |
+			      (color.green & 0xff00) << 8|
+			      (color.blue & 0xff00) |
+			      0xff);
+		color_changed = TRUE;
+		break;
+
+        case PROP_FILL_COLOR_RGBA:
+		text->rgba = g_value_get_uint (value);
+		color_changed = TRUE;
+		text->priv->render_dirty = 1;
+		break;
+
+	case PROP_FILL_STIPPLE:
+		set_stipple (text, (GdkBitmap *)g_value_get_object (value), FALSE);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+
+	if (color_changed) {
+		if (have_pixel)
+			text->pixel = color.pixel;
+		else
+			text->pixel = gnome_canvas_get_color_pixel (item->canvas, text->rgba);
+
+		if (!item->canvas->aa)
+			set_text_gc_foreground (text);
+	}
+
+	/* Calculate text dimensions */
+
+	if (text->layout)
+	        pango_layout_get_pixel_size (text->layout,
+					     &text->max_width,
+					     &text->height);
+	else {
+		text->max_width = 0;
+		text->height = 0;
+	}
+
+	gnome_canvas_item_request_update (item);
+}
+
+/* Get_arg handler for the text item */
+static void
+gnome_canvas_text_get_property (GObject            *object,
+				guint               param_id,
+				GValue             *value,
+				GParamSpec         *pspec)
+{
+	GnomeCanvasText *text;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_TEXT (object));
+
+	text = GNOME_CANVAS_TEXT (object);
+
+	switch (param_id) {
+	case PROP_TEXT:
+		g_value_set_string (value, text->text);
+		break;
+
+	case PROP_X:
+		g_value_set_double (value, text->x);
+		break;
+
+	case PROP_Y:
+		g_value_set_double (value, text->y);
+		break;
+
+	case PROP_FONT:
+	case PROP_FONT_DESC:
+	case PROP_FAMILY:
+	case PROP_STYLE:
+	case PROP_VARIANT:
+	case PROP_WEIGHT:
+	case PROP_STRETCH:
+	case PROP_SIZE:
+	case PROP_SIZE_POINTS:
+		ensure_font (text);
+
+		switch (param_id) {
+		case PROP_FONT:
+		{
+			/* FIXME GValue imposes a totally gratuitous string copy
+			 * here, we could just hand off string ownership
+			 */
+			gchar *str;
+
+			str = pango_font_description_to_string (text->font_desc);
+			g_value_set_string (value, str);
+			g_free (str);
+
+			break;
+		}
+
+		case PROP_FONT_DESC:
+			g_value_set_boxed (value, text->font_desc);
+			break;
+
+		case PROP_FAMILY:
+			g_value_set_string (value, pango_font_description_get_family (text->font_desc));
+			break;
+
+		case PROP_STYLE:
+			g_value_set_enum (value, pango_font_description_get_style (text->font_desc));
+			break;
+
+		case PROP_VARIANT:
+			g_value_set_enum (value, pango_font_description_get_variant (text->font_desc));
+			break;
+
+		case PROP_WEIGHT:
+			g_value_set_int (value, pango_font_description_get_weight (text->font_desc));
+			break;
+
+		case PROP_STRETCH:
+			g_value_set_enum (value, pango_font_description_get_stretch (text->font_desc));
+			break;
+
+		case PROP_SIZE:
+			g_value_set_int (value, pango_font_description_get_size (text->font_desc));
+			break;
+
+		case PROP_SIZE_POINTS:
+			g_value_set_double (value, ((double)pango_font_description_get_size (text->font_desc)) / (double)PANGO_SCALE);
+			break;
+		}
+		break;
+
+	case PROP_FAMILY_SET:
+	case PROP_STYLE_SET:
+	case PROP_VARIANT_SET:
+	case PROP_WEIGHT_SET:
+	case PROP_STRETCH_SET:
+	case PROP_SIZE_SET:
+	{
+		PangoFontMask set_mask = text->font_desc ? pango_font_description_get_set_fields (text->font_desc) : 0;
+		PangoFontMask test_mask = get_property_font_set_mask (param_id);
+		g_value_set_boolean (value, (set_mask & test_mask) != 0);
+
+		break;
+	}
+
+	case PROP_SCALE:
+		g_value_set_double (value, text->scale);
+		break;
+	case PROP_SCALE_SET:
+		g_value_set_boolean (value, text->scale_set);
+		break;
+
+	case PROP_UNDERLINE:
+		g_value_set_enum (value, text->underline);
+		break;
+	case PROP_UNDERLINE_SET:
+		g_value_set_boolean (value, text->underline_set);
+		break;
+
+	case PROP_STRIKETHROUGH:
+		g_value_set_boolean (value, text->strikethrough);
+		break;
+	case PROP_STRIKETHROUGH_SET:
+		g_value_set_boolean (value, text->strike_set);
+		break;
+
+	case PROP_RISE:
+		g_value_set_int (value, text->rise);
+		break;
+	case PROP_RISE_SET:
+		g_value_set_boolean (value, text->rise_set);
+		break;
+
+	case PROP_ATTRIBUTES:
+		g_value_set_boxed (value, text->attr_list);
+		break;
+
+	case PROP_ANCHOR:
+		g_value_set_enum (value, text->anchor);
+		break;
+
+	case PROP_JUSTIFICATION:
+		g_value_set_enum (value, text->justification);
+		break;
+
+	case PROP_CLIP_WIDTH:
+		g_value_set_double (value, text->clip_width);
+		break;
+
+	case PROP_CLIP_HEIGHT:
+		g_value_set_double (value, text->clip_height);
+		break;
+
+	case PROP_CLIP:
+		g_value_set_boolean (value, text->clip);
+		break;
+
+	case PROP_X_OFFSET:
+		g_value_set_double (value, text->xofs);
+		break;
+
+	case PROP_Y_OFFSET:
+		g_value_set_double (value, text->yofs);
+		break;
+
+	case PROP_FILL_COLOR:
+                g_value_set_string_take_ownership (value,
+						   g_strdup_printf ("#%02x%02x%02x",
+                                                                    text->rgba >> 24,
+                                                                    (text->rgba >> 16) & 0xff,
+                                                                    (text->rgba >> 8) & 0xff));
+		break;
+
+	case PROP_FILL_COLOR_GDK: {
+		GnomeCanvas *canvas = GNOME_CANVAS_ITEM (text)->canvas;
+		GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+		GdkColor color;
+
+		gdk_colormap_query_color (colormap, text->pixel, &color);
+		g_value_set_boxed (value, &color);
+		break;
+	}
+	case PROP_FILL_COLOR_RGBA:
+		g_value_set_uint (value, text->rgba);
+		break;
+
+	case PROP_FILL_STIPPLE:
+		g_value_set_object (value, text->stipple);
+		break;
+
+	case PROP_TEXT_WIDTH:
+		g_value_set_double (value, text->max_width / text->item.canvas->pixels_per_unit);
+		break;
+
+	case PROP_TEXT_HEIGHT:
+		g_value_set_double (value, text->height / text->item.canvas->pixels_per_unit);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+/* */
+static void
+gnome_canvas_text_apply_font_desc (GnomeCanvasText *text)
+{
+	PangoFontDescription *font_desc =
+		pango_font_description_copy (
+			GTK_WIDGET (GNOME_CANVAS_ITEM (text)->canvas)->style->font_desc);
+
+	if (text->font_desc)
+		pango_font_description_merge (font_desc, text->font_desc, TRUE);
+
+	pango_layout_set_font_description (text->layout, font_desc);
+	pango_font_description_free (font_desc);
+}
+
+static void
+add_attr (PangoAttrList  *attr_list,
+	  PangoAttribute *attr)
+{
+	attr->start_index = 0;
+	attr->end_index = G_MAXINT;
+
+	pango_attr_list_insert (attr_list, attr);
+}
+
+/* */
+static void
+gnome_canvas_text_apply_attributes (GnomeCanvasText *text)
+{
+	PangoAttrList *attr_list;
+
+	if (text->attr_list)
+		attr_list = pango_attr_list_copy (text->attr_list);
+	else
+		attr_list = pango_attr_list_new ();
+
+	if (text->underline_set)
+		add_attr (attr_list, pango_attr_underline_new (text->underline));
+	if (text->strike_set)
+		add_attr (attr_list, pango_attr_strikethrough_new (text->strikethrough));
+	if (text->rise_set)
+		add_attr (attr_list, pango_attr_rise_new (text->rise));
+
+	pango_layout_set_attributes (text->layout, attr_list);
+	pango_attr_list_unref (attr_list);
+}
+
+static void
+gnome_canvas_text_set_font_desc (GnomeCanvasText      *text,
+				 PangoFontDescription *font_desc)
+{
+	if (text->font_desc)
+		pango_font_description_free (text->font_desc);
+
+	if (font_desc)
+		text->font_desc = pango_font_description_copy (font_desc);
+	else
+		text->font_desc = NULL;
+
+	gnome_canvas_text_apply_font_desc (text);
+	text->priv->render_dirty = 1;
+}
+
+/* Setting the text from a Pango markup string */
+static void
+gnome_canvas_text_set_markup (GnomeCanvasText *textitem,
+			      const gchar     *markup)
+{
+	PangoAttrList *attr_list = NULL;
+	gchar         *text = NULL;
+	GError        *error = NULL;
+
+	if (markup && !pango_parse_markup (markup, -1,
+					   0,
+					   &attr_list, &text, NULL,
+					   &error))
+	{
+		g_warning ("Failed to set cell text from markup due to error parsing markup: %s",
+			   error->message);
+		g_error_free (error);
+		return;
+	}
+
+	g_free (textitem->text);
+	if (textitem->attr_list)
+		pango_attr_list_unref (textitem->attr_list);
+
+	textitem->text = text;
+	textitem->attr_list = attr_list;
+
+	pango_layout_set_text (textitem->layout, text, -1);
+
+	gnome_canvas_text_apply_attributes (textitem);
+}
+
+/* Update handler for the text item */
+static void
+gnome_canvas_text_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasText *text;
+	double x1, y1, x2, y2;
+
+	text = GNOME_CANVAS_TEXT (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, affine, clip_path, flags);
+
+	set_text_gc_foreground (text);
+	set_stipple (text, text->stipple, TRUE);
+	get_bounds (text, &x1, &y1, &x2, &y2);
+
+	gnome_canvas_update_bbox (item,
+				  floor (x1), floor (y1),
+				  ceil (x2), ceil (y2));
+}
+
+/* Realize handler for the text item */
+static void
+gnome_canvas_text_realize (GnomeCanvasItem *item)
+{
+	GnomeCanvasText *text;
+
+	text = GNOME_CANVAS_TEXT (item);
+
+	if (parent_class->realize)
+		(* parent_class->realize) (item);
+
+	text->gc = gdk_gc_new (item->canvas->layout.bin_window);
+}
+
+/* Unrealize handler for the text item */
+static void
+gnome_canvas_text_unrealize (GnomeCanvasItem *item)
+{
+	GnomeCanvasText *text;
+
+	text = GNOME_CANVAS_TEXT (item);
+
+	gdk_gc_unref (text->gc);
+	text->gc = NULL;
+
+	if (parent_class->unrealize)
+		(* parent_class->unrealize) (item);
+}
+
+/* Draw handler for the text item */
+static void
+gnome_canvas_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+			int x, int y, int width, int height)
+{
+	GnomeCanvasText *text;
+	GdkRectangle rect;
+
+	text = GNOME_CANVAS_TEXT (item);
+
+	if (!text->text)
+		return;
+
+	if (text->clip) {
+		rect.x = text->clip_cx - x;
+		rect.y = text->clip_cy - y;
+		rect.width = text->clip_cwidth;
+		rect.height = text->clip_cheight;
+
+		gdk_gc_set_clip_rectangle (text->gc, &rect);
+	}
+
+	if (text->stipple)
+		gnome_canvas_set_stipple_origin (item->canvas, text->gc);
+
+
+	gdk_draw_layout (drawable, text->gc, text->cx - x, text->cy - y, text->layout);
+
+	if (text->clip)
+		gdk_gc_set_clip_rectangle (text->gc, NULL);
+}
+
+
+/* Render handler for the text item */
+static void
+gnome_canvas_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+	GnomeCanvasText *text;
+	guint32 fg_color;
+	int render_x = 0, render_y = 0; /* offsets for text rendering,
+					 * for clipping rectangles */
+	int x, y;
+	int w, h;
+	guchar *dst, *src;
+	int src_dx, src_dy;
+	int i, alpha;
+	int bm_rows, bm_width;
+
+	text = GNOME_CANVAS_TEXT (item);
+
+	if (!text->text)
+		return;
+
+	fg_color = text->rgba;
+
+        gnome_canvas_buf_ensure_buf (buf);
+
+	bm_rows = (text->clip) ? text->clip_cheight : text->height;
+	bm_width = (text->clip) ? text->clip_cwidth : text->max_width;
+	if(text->priv->render_dirty ||
+	   bm_rows != text->priv->bitmap.rows ||
+	   bm_width != text->priv->bitmap.width) {
+		if(text->priv->bitmap.buffer) {
+			g_free(text->priv->bitmap.buffer);
+		}
+		text->priv->bitmap.rows =  bm_rows;
+		text->priv->bitmap.width = bm_width;
+		text->priv->bitmap.pitch = (text->priv->bitmap.width+3)&~3;
+		text->priv->bitmap.buffer = g_malloc0 (text->priv->bitmap.rows * text->priv->bitmap.pitch);
+		text->priv->bitmap.num_grays = 256;
+		text->priv->bitmap.pixel_mode = ft_pixel_mode_grays;
+
+		/* What this does is when a clipping rectangle is
+		   being used shift the rendering of the text by the
+		   correct amount so that the correct result is
+		   obtained as if all text was rendered, then clipped.
+		   In this sense we can use smaller buffers and less
+		   rendeirng since hopefully FreeType2 checks to see
+		   if the glyph falls in the bounding box before
+		   rasterizing it. */
+
+		if(text->clip) {
+			render_x = text->cx - text->clip_cx;
+			render_y = text->cy - text->clip_cy;
+		}
+
+		pango_ft2_render_layout (&text->priv->bitmap, text->layout, render_x, render_y);
+
+		text->priv->render_dirty = 0;
+	}
+
+	if (text->clip) {
+		x = text->clip_cx - buf->rect.x0;
+		y = text->clip_cy - buf->rect.y0;
+	} else {
+		x = text->cx - buf->rect.x0;
+		y = text->cy - buf->rect.y0;
+	}
+
+	w = text->priv->bitmap.width;
+	h = text->priv->bitmap.rows;
+
+	src_dx = src_dy = 0;
+
+	if (x + w > buf->rect.x1 - buf->rect.x0) {
+		w = buf->rect.x1 - buf->rect.x0 - x;
+	}
+
+	if (y + h > buf->rect.y1 - buf->rect.y0) {
+		h = buf->rect.y1 - buf->rect.y0 - y;
+	}
+
+	if (x < 0) {
+		w -= - x;
+		src_dx += - x;
+		x = 0;
+	}
+
+	if (y < 0) {
+		h -= -y;
+		src_dy += - y;
+		y = 0;
+	}
+
+	dst = buf->buf + y * buf->buf_rowstride + x * 3;
+	src = text->priv->bitmap.buffer +
+		src_dy * text->priv->bitmap.pitch + src_dx;
+	while (h-- > 0) {
+		i = w;
+		while (i-- > 0) {
+			/* FIXME: Do the libart thing instead of divide by 255 */
+			alpha = ((fg_color & 0xff) * (*src)) / 255;
+			dst[0] = (dst[0] * (255 - alpha) + ((fg_color >> 24) & 0xff) * alpha) / 255;
+			dst[1] = (dst[1] * (255 - alpha) + ((fg_color >> 16) & 0xff) * alpha) / 255;
+			dst[2] = (dst[2] * (255 - alpha) + ((fg_color >> 8) & 0xff) * alpha) / 255;
+			dst += 3;
+			src += 1;
+		}
+		dst += buf->buf_rowstride - w*3;
+		src += text->priv->bitmap.pitch - w;
+	}
+
+	buf->is_bg = 0;
+	return;
+}
+
+/* Point handler for the text item */
+static double
+gnome_canvas_text_point (GnomeCanvasItem *item, double x, double y,
+			 int cx, int cy, GnomeCanvasItem **actual_item)
+{
+	GnomeCanvasText *text;
+	PangoLayoutIter *iter;
+	int x1, y1, x2, y2;
+	int dx, dy;
+	double dist, best;
+
+	text = GNOME_CANVAS_TEXT (item);
+
+	*actual_item = item;
+
+	/* The idea is to build bounding rectangles for each of the lines of
+	 * text (clipped by the clipping rectangle, if it is activated) and see
+	 * whether the point is inside any of these.  If it is, we are done.
+	 * Otherwise, calculate the distance to the nearest rectangle.
+	 */
+
+	best = 1.0e36;
+
+	iter = pango_layout_get_iter (text->layout);
+	do {
+ 	        PangoRectangle log_rect;
+
+		pango_layout_iter_get_line_extents (iter, NULL, &log_rect);
+
+		x1 = text->cx + PANGO_PIXELS (log_rect.x);
+		y1 = text->cy + PANGO_PIXELS (log_rect.y);
+		x2 = x1 + PANGO_PIXELS (log_rect.width);
+		y2 = y1 + PANGO_PIXELS (log_rect.height);
+
+		if (text->clip) {
+			if (x1 < text->clip_cx)
+				x1 = text->clip_cx;
+
+			if (y1 < text->clip_cy)
+				y1 = text->clip_cy;
+
+			if (x2 > (text->clip_cx + text->clip_width))
+				x2 = text->clip_cx + text->clip_width;
+
+			if (y2 > (text->clip_cy + text->clip_height))
+				y2 = text->clip_cy + text->clip_height;
+
+			if ((x1 >= x2) || (y1 >= y2))
+				continue;
+		}
+
+		/* Calculate distance from point to rectangle */
+
+		if (cx < x1)
+			dx = x1 - cx;
+		else if (cx >= x2)
+			dx = cx - x2 + 1;
+		else
+			dx = 0;
+
+		if (cy < y1)
+			dy = y1 - cy;
+		else if (cy >= y2)
+			dy = cy - y2 + 1;
+		else
+			dy = 0;
+
+		if ((dx == 0) && (dy == 0)) {
+			pango_layout_iter_free(iter);
+			return 0.0;
+		}
+
+		dist = sqrt (dx * dx + dy * dy);
+		if (dist < best)
+			best = dist;
+
+	} while (pango_layout_iter_next_line(iter));
+
+	pango_layout_iter_free(iter);
+
+	return best / item->canvas->pixels_per_unit;
+}
+
+/* Bounds handler for the text item */
+static void
+gnome_canvas_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	GnomeCanvasText *text;
+	double width, height;
+
+	text = GNOME_CANVAS_TEXT (item);
+
+	*x1 = text->x;
+	*y1 = text->y;
+
+	if (text->clip) {
+		width = text->clip_width;
+		height = text->clip_height;
+	} else {
+		width = text->max_width / item->canvas->pixels_per_unit;
+		height = text->height / item->canvas->pixels_per_unit;
+	}
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		*x1 -= width / 2.0;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		*x1 -= width;
+		break;
+
+	default:
+		break;
+	}
+
+	switch (text->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		*y1 -= height / 2.0;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		*y1 -= height;
+		break;
+
+	default:
+		break;
+	}
+
+	*x2 = *x1 + width;
+	*y2 = *y1 + height;
+}
diff --git a/src/libgnomecanvas/gnome-canvas-text.h b/src/libgnomecanvas/gnome-canvas-text.h
new file mode 100644
index 0000000..36ed844
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-text.h
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Text item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ * Port to Pango co-done by Gergõ �rdi <cactus cactus rulez org>
+ */
+
+#ifndef GNOME_CANVAS_TEXT_H
+#define GNOME_CANVAS_TEXT_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Text item for the canvas.  Text items are positioned by an anchor point and an anchor direction.
+ *
+ * A clipping rectangle may be specified for the text.  The rectangle is anchored at the text's anchor
+ * point, and is specified by clipping width and height parameters.  If the clipping rectangle is
+ * enabled, it will clip the text.
+ *
+ * In addition, x and y offset values may be specified.  These specify an offset from the anchor
+ * position.  If used in conjunction with the clipping rectangle, these could be used to implement
+ * simple scrolling of the text within the clipping rectangle.
+ *
+ * Properties marked with [*] also have _set properties associated
+ * with them, that determine if the specified value should be used
+ * instead of the default (style-defined) values
+ *
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * text			string			RW		The string of the text label
+ * markup		string			 W		A Pango markup string for the text label
+ *
+ * x			double			RW		X coordinate of anchor point
+ * y			double			RW		Y coordinate of anchor point
+ *
+ * font			string			 W		A string describing the font
+ * font_desc	        PangoFontDescription*	RW		Pointer to a PangoFontDescriptor
+ * attributes           PangoAttrList*          RW		Pointer to a Pango attribute list
+ * style		PangoStyle		RW		Pango style of font to use	[*]
+ * variant		PangoVariant		RW		Pango variant of font to use	[*]
+ * weight		int			RW		Pango weight of font to use	[*]
+ * stretch		PangoStretch		RW		Pango stretch of font to use	[*]
+ * size			int			RW		Size (in pixels) of font	[*]
+ * size_points		double			RW		Size (in points) of font
+ * scale                double                  RW              Ratio to scale font		[*]
+ *
+ * anchor		GtkAnchorType		RW		Anchor side for the text
+ * justification	GtkJustification	RW		Justification for multiline text
+ * clip_width		double			RW		Width of clip rectangle
+ * clip_height		double			RW		Height of clip rectangle
+ * clip			boolean			RW		Use clipping rectangle?
+ * x_offset		double			RW		Horizontal offset distance from anchor position
+ * y_offset		double			RW		Vertical offset distance from anchor position
+ *
+ * text_width		double			R		Used to query the width of the rendered text
+ * text_height		double			R		Used to query the rendered height of the text
+ *
+ * fill_color		string			 W		X color specification for text
+ * fill_color_gdk	GdkColor*		RW		Pointer to an allocated GdkColor
+ * fill_color_rgba	guint   		RW		RGBA value used for AA color.
+ * fill_stipple		GdkBitmap*		RW		Stipple pattern for filling the text
+ */
+
+#define GNOME_TYPE_CANVAS_TEXT            (gnome_canvas_text_get_type ())
+#define GNOME_CANVAS_TEXT(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasText))
+#define GNOME_CANVAS_TEXT_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasTextClass))
+#define GNOME_IS_CANVAS_TEXT(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_TEXT))
+#define GNOME_IS_CANVAS_TEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_TEXT))
+#define GNOME_CANVAS_TEXT_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_TEXT, GnomeCanvasTextClass))
+
+
+typedef struct _GnomeCanvasText GnomeCanvasText;
+typedef struct _GnomeCanvasTextClass GnomeCanvasTextClass;
+
+typedef struct _GnomeCanvasTextPrivate GnomeCanvasTextPrivate;
+
+struct _GnomeCanvasText {
+	GnomeCanvasItem item;
+
+	PangoFontDescription *font_desc; /* Font description for text */
+	PangoAttrList *attr_list;        /* Attribute list of the text (caching) */
+	PangoUnderline underline;
+	gboolean       strikethrough;
+	int            rise;
+	double         scale;
+	
+	char *text;			/* Text to display */
+	GdkBitmap *stipple;		/* Stipple for text */
+	GdkGC *gc;			/* GC for drawing text */
+        PangoLayout *layout;            /* The PangoLayout containing the text */
+
+	gulong pixel;			/* Fill color */
+
+	double x, y;			/* Position at anchor */
+
+	double clip_width;		/* Width of optional clip rectangle */
+	double clip_height;		/* Height of optional clip rectangle */
+
+	double xofs, yofs;		/* Text offset distance from anchor position */
+
+	double affine[6];               /* The item -> canvas affine */ /*AA*/
+
+	GtkAnchorType anchor;		/* Anchor side for text */
+	GtkJustification justification;	/* Justification for text */
+
+	int cx, cy;			/* Top-left canvas coordinates for text */
+	int clip_cx, clip_cy;		/* Top-left canvas coordinates for clip rectangle */
+	int clip_cwidth, clip_cheight;	/* Size of clip rectangle in pixels */
+	int max_width;			/* Maximum width of text lines */
+	int height;			/* Rendered text height in pixels */
+
+        guint32 rgba;			/* RGBA color for text */ /*AA*/
+
+	guint clip : 1;			/* Use clip rectangle? */
+
+	guint underline_set : 1;        /* Apply specified underline style? */
+	guint strike_set    : 1;        /* Apply specified strikethrough style? */
+	guint rise_set      : 1;        /* Apply specified ascension/descension? */
+
+	guint scale_set     : 1;        /* Apply specified font scaling ratio? */
+
+	GnomeCanvasTextPrivate *priv;	
+};
+
+struct _GnomeCanvasTextClass {
+	GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_text_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-util.c b/src/libgnomecanvas/gnome-canvas-util.c
new file mode 100644
index 0000000..d306292
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-util.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Miscellaneous utility functions for the GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+
+/* needed for M_PI_2 under 'gcc -ansi -predantic' on GNU/Linux */
+#ifndef _BSD_SOURCE
+#  define _BSD_SOURCE 1
+#endif
+#include <sys/types.h>
+
+#include <glib.h>
+#include <math.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_ops.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <libart_lgpl/art_uta_svp.h>
+#include <libart_lgpl/art_rect_svp.h>
+
+/**
+ * gnome_canvas_points_new:
+ * @num_points: The number of points to allocate space for in the array.
+ * 
+ * Creates a structure that should be used to pass an array of points to
+ * items.
+ * 
+ * Return value: A newly-created array of points.  It should be filled in
+ * by the user.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_new (int num_points)
+{
+	GnomeCanvasPoints *points;
+
+	g_return_val_if_fail (num_points > 1, NULL);
+
+	points = g_new (GnomeCanvasPoints, 1);
+	points->num_points = num_points;
+	points->coords = g_new (double, 2 * num_points);
+	points->ref_count = 1;
+
+	return points;
+}
+
+/**
+ * gnome_canvas_points_ref:
+ * @points: A canvas points structure.
+ * 
+ * Increases the reference count of the specified points structure.
+ * 
+ * Return value: The canvas points structure itself.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_ref (GnomeCanvasPoints *points)
+{
+	g_return_val_if_fail (points != NULL, NULL);
+
+	points->ref_count += 1;
+	return points;
+}
+
+/**
+ * gnome_canvas_points_free:
+ * @points: A canvas points structure.
+ * 
+ * Decreases the reference count of the specified points structure.  If it
+ * reaches zero, then the structure is freed.
+ **/
+void
+gnome_canvas_points_free (GnomeCanvasPoints *points)
+{
+	g_return_if_fail (points != NULL);
+
+	points->ref_count -= 1;
+	if (points->ref_count == 0) {
+		g_free (points->coords);
+		g_free (points);
+	}
+}
+
+/**
+ * gnome_canvas_get_miter_points:
+ * @x1: X coordinate of the first point
+ * @y1: Y coordinate of the first point
+ * @x2: X coordinate of the second (angle) point
+ * @y2: Y coordinate of the second (angle) point
+ * @x3: X coordinate of the third point
+ * @y3: Y coordinate of the third point
+ * @width: Width of the line
+ * @mx1: The X coordinate of the first miter point is returned here.
+ * @my1: The Y coordinate of the first miter point is returned here.
+ * @mx2: The X coordinate of the second miter point is returned here.
+ * @my2: The Y coordinate of the second miter point is returned here.
+ * 
+ * Given three points forming an angle, computes the coordinates of the inside
+ * and outside points of the mitered corner formed by a line of a given width at
+ * that angle.
+ * 
+ * Return value: FALSE if the angle is less than 11 degrees (this is the same
+ * threshold as X uses.  If this occurs, the return points are not modified.
+ * Otherwise, returns TRUE.
+ **/
+int
+gnome_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+			       double width,
+			       double *mx1, double *my1, double *mx2, double *my2)
+{
+	double theta1;		/* angle of segment p2-p1 */
+	double theta2;		/* angle of segment p2-p3 */
+	double theta;		/* angle between line segments */
+	double theta3;		/* angle that bisects theta1 and theta2 and points to p1 */
+	double dist;		/* distance of miter points from p2 */
+	double dx, dy;		/* x and y offsets corresponding to dist */
+
+#define ELEVEN_DEGREES (11.0 * G_PI / 180.0)
+
+	if (y2 == y1)
+		theta1 = (x2 < x1) ? 0.0 : G_PI;
+	else if (x2 == x1)
+		theta1 = (y2 < y1) ? G_PI_2 : -G_PI_2;
+	else
+		theta1 = atan2 (y1 - y2, x1 - x2);
+
+	if (y3 == y2)
+		theta2 = (x3 > x2) ? 0 : G_PI;
+	else if (x3 == x2)
+		theta2 = (y3 > y2) ? G_PI_2 : -G_PI_2;
+	else
+		theta2 = atan2 (y3 - y2, x3 - x2);
+
+	theta = theta1 - theta2;
+
+	if (theta > G_PI)
+		theta -= 2.0 * G_PI;
+	else if (theta < -G_PI)
+		theta += 2.0 * G_PI;
+
+	if ((theta < ELEVEN_DEGREES) && (theta > -ELEVEN_DEGREES))
+		return FALSE;
+
+	dist = 0.5 * width / sin (0.5 * theta);
+	if (dist < 0.0)
+		dist = -dist;
+
+	theta3 = (theta1 + theta2) / 2.0;
+	if (sin (theta3 - (theta1 + G_PI)) < 0.0)
+		theta3 += G_PI;
+
+	dx = dist * cos (theta3);
+	dy = dist * sin (theta3);
+
+	*mx1 = x2 + dx;
+	*mx2 = x2 - dx;
+	*my1 = y2 + dy;
+	*my2 = y2 - dy;
+
+	return TRUE;
+}
+
+/**
+ * gnome_canvas_get_butt_points:
+ * @x1: X coordinate of first point in the line
+ * @y1: Y cooordinate of first point in the line
+ * @x2: X coordinate of second point (endpoint) of the line
+ * @y2: Y coordinate of second point (endpoint) of the line
+ * @width: Width of the line
+ * @project: Whether the butt points should project out by width/2 distance
+ * @bx1: X coordinate of first butt point is returned here
+ * @by1: Y coordinate of first butt point is returned here
+ * @bx2: X coordinate of second butt point is returned here
+ * @by2: Y coordinate of second butt point is returned here
+ * 
+ * Computes the butt points of a line segment.
+ **/
+void
+gnome_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+			      double width, int project,
+			      double *bx1, double *by1, double *bx2, double *by2)
+{
+	double length;
+	double dx, dy;
+
+	width *= 0.5;
+	dx = x2 - x1;
+	dy = y2 - y1;
+	length = sqrt (dx * dx + dy * dy);
+
+	if (length < GNOME_CANVAS_EPSILON) {
+		*bx1 = *bx2 = x2;
+		*by1 = *by2 = y2;
+	} else {
+		dx = -width * (y2 - y1) / length;
+		dy = width * (x2 - x1) / length;
+
+		*bx1 = x2 + dx;
+		*bx2 = x2 - dx;
+		*by1 = y2 + dy;
+		*by2 = y2 - dy;
+
+		if (project) {
+			*bx1 += dy;
+			*bx2 += dy;
+			*by1 -= dx;
+			*by2 -= dx;
+		}
+	}
+}
+
+/**
+ * gnome_canvas_polygon_to_point:
+ * @poly: Vertices of the polygon.  X coordinates are in the even indices, and Y
+ * coordinates are in the odd indices
+ * @num_points: Number of points in the polygon
+ * @x: X coordinate of the point
+ * @y: Y coordinate of the point
+ * 
+ * Computes the distance between a point and a polygon.
+ * 
+ * Return value: The distance from the point to the polygon, or zero if the
+ * point is inside the polygon.
+ **/
+double
+gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y)
+{
+	double best;
+	int intersections;
+	int i;
+	double *p;
+	double dx, dy;
+
+	/* Iterate through all the edges in the polygon, updating best and intersections.
+	 *
+	 * When computing intersections, include left X coordinate of line within its range, but not
+	 * Y coordinate.  Otherwise if the point lies exactly below a vertex we'll count it as two
+	 * intersections.
+	 */
+
+	best = 1.0e36;
+	intersections = 0;
+
+	for (i = num_points, p = poly; i > 1; i--, p += 2) {
+		double px, py, dist;
+
+		/* Compute the point on the current edge closest to the point and update the
+		 * intersection count.  This must be done separately for vertical edges, horizontal
+		 * edges, and others.
+		 */
+
+		if (p[2] == p[0]) {
+			/* Vertical edge */
+
+			px = p[0];
+
+			if (p[1] >= p[3]) {
+				py = MIN (p[1], y);
+				py = MAX (py, p[3]);
+			} else {
+				py = MIN (p[3], y);
+				py = MAX (py, p[1]);
+			}
+		} else if (p[3] == p[1]) {
+			/* Horizontal edge */
+
+			py = p[1];
+
+			if (p[0] >= p[2]) {
+				px = MIN (p[0], x);
+				px = MAX (px, p[2]);
+
+				if ((y < py) && (x < p[0]) && (x >= p[2]))
+					intersections++;
+			} else {
+				px = MIN (p[2], x);
+				px = MAX (px, p[0]);
+
+				if ((y < py) && (x < p[2]) && (x >= p[0]))
+					intersections++;
+			}
+		} else {
+			double m1, b1, m2, b2;
+			int lower;
+
+			/* Diagonal edge.  Convert the edge to a line equation (y = m1*x + b1), then
+			 * compute a line perpendicular to this edge but passing through the point,
+			 * (y = m2*x + b2).
+			 */
+
+			m1 = (p[3] - p[1]) / (p[2] - p[0]);
+			b1 = p[1] - m1 * p[0];
+
+			m2 = -1.0 / m1;
+			b2 = y - m2 * x;
+
+			px = (b2 - b1) / (m1 - m2);
+			py = m1 * px + b1;
+
+			if (p[0] > p[2]) {
+				if (px > p[0]) {
+					px = p[0];
+					py = p[1];
+				} else if (px < p[2]) {
+					px = p[2];
+					py = p[3];
+				}
+			} else {
+				if (px > p[2]) {
+					px = p[2];
+					py = p[3];
+				} else if (px < p[0]) {
+					px = p[0];
+					py = p[1];
+				}
+			}
+
+			lower = (m1 * x + b1) > y;
+
+			if (lower && (x >= MIN (p[0], p[2])) && (x < MAX (p[0], p[2])))
+				intersections++;
+		}
+
+		/* Compute the distance to the closest point, and see if that is the best so far */
+
+		dx = x - px;
+		dy = y - py;
+		dist = sqrt (dx * dx + dy * dy);
+		if (dist < best)
+			best = dist;
+	}
+
+	/* We've processed all the points.  If the number of intersections is odd, the point is
+	 * inside the polygon.
+	 */
+
+	if (intersections & 0x1)
+		return 0.0;
+	else
+		return best;
+}
+
+/* Here are some helper functions for aa rendering: */
+
+/**
+ * gnome_canvas_render_svp:
+ * @buf: the canvas buffer to render over
+ * @svp: the vector path to render
+ * @rgba: the rgba color to render
+ *
+ * Render the svp over the buf.
+ **/
+void
+gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba)
+{
+	guint32 fg_color, bg_color;
+	int alpha;
+
+	if (buf->is_bg) {
+		bg_color = buf->bg_color;
+		alpha = rgba & 0xff;
+		if (alpha == 0xff)
+			fg_color = rgba >> 8;
+		else {
+			/* composite over background color */
+			int bg_r, bg_g, bg_b;
+			int fg_r, fg_g, fg_b;
+			int tmp;
+
+			bg_r = (bg_color >> 16) & 0xff;
+			fg_r = (rgba >> 24) & 0xff;
+			tmp = (fg_r - bg_r) * alpha;
+			fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+			bg_g = (bg_color >> 8) & 0xff;
+			fg_g = (rgba >> 16) & 0xff;
+			tmp = (fg_g - bg_g) * alpha;
+			fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+			bg_b = bg_color & 0xff;
+			fg_b = (rgba >> 8) & 0xff;
+			tmp = (fg_b - bg_b) * alpha;
+			fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+			fg_color = (fg_r << 16) | (fg_g << 8) | fg_b;
+		}
+		art_rgb_svp_aa (svp,
+				buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+				fg_color, bg_color,
+				buf->buf, buf->buf_rowstride,
+				NULL);
+		buf->is_bg = 0;
+		buf->is_buf = 1;
+	} else {
+		art_rgb_svp_alpha (svp,
+				   buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+				   rgba,
+				   buf->buf, buf->buf_rowstride,
+				   NULL);
+	}
+}
+
+/**
+ * gnome_canvas_update_svp:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+	ArtSVP *old_svp;
+	ArtSVP *diff G_GNUC_UNUSED;
+	ArtUta *repaint_uta;
+
+	old_svp = *p_svp;
+
+	if (old_svp != NULL) {
+		ArtDRect bb;
+		art_drect_svp (&bb, old_svp);
+		if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+			repaint_uta = art_uta_from_svp (old_svp);
+			gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+		} else {
+			ArtIRect ib;
+			art_drect_to_irect (&ib, &bb);
+			gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+		}
+		art_svp_free (old_svp);
+	}
+
+	if (new_svp != NULL) {
+		ArtDRect bb;
+		art_drect_svp (&bb, new_svp);
+		if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+			repaint_uta = art_uta_from_svp (new_svp);
+			gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+		} else {
+			ArtIRect ib;
+			art_drect_to_irect (&ib, &bb);
+			gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+		}
+	}
+
+	*p_svp = new_svp;
+}
+
+/**
+ * gnome_canvas_update_svp_clip:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp, ArtSVP *clip_svp)
+{
+	ArtSVP *clipped_svp;
+
+	if (clip_svp != NULL) {
+		clipped_svp = art_svp_intersect (new_svp, clip_svp);
+		art_svp_free (new_svp);
+	} else {
+		clipped_svp = new_svp;
+	}
+	gnome_canvas_update_svp (canvas, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_reset_bounds:
+ * @item: A canvas item
+ * 
+ * Resets the bounding box of a canvas item to an empty rectangle.
+ **/
+void
+gnome_canvas_item_reset_bounds (GnomeCanvasItem *item)
+{
+	item->x1 = 0.0;
+	item->y1 = 0.0;
+	item->x2 = 0.0;
+	item->y2 = 0.0;
+}
+
+/**
+ * gnome_canvas_item_update_svp:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp. This routine also adds the
+ * svp's bbox to the item's.
+ **/
+void
+gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+	ArtDRect bbox;
+
+	gnome_canvas_update_svp (item->canvas, p_svp, new_svp);
+	if (new_svp) {
+		bbox.x0 = item->x1;
+		bbox.y0 = item->y1;
+		bbox.x1 = item->x2;
+		bbox.y1 = item->y2;
+		art_drect_svp_union (&bbox, new_svp);
+		item->x1 = bbox.x0;
+		item->y1 = bbox.y0;
+		item->x2 = bbox.x1;
+		item->y2 = bbox.y1;
+	}
+}
+
+/**
+ * gnome_canvas_item_update_svp_clip:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
+				   ArtSVP *clip_svp)
+{
+	ArtSVP *clipped_svp;
+
+	if (clip_svp != NULL) {
+		clipped_svp = art_svp_intersect (new_svp, clip_svp);
+		art_svp_free (new_svp);
+	} else {
+		clipped_svp = new_svp;
+	}
+
+	gnome_canvas_item_update_svp (item, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_request_redraw_svp
+ * @item: the item containing the svp
+ * @svp: the svp that needs to be redrawn
+ *
+ * Request redraw of the svp if in aa mode, or the entire item in in xlib mode.
+ **/ 
+void
+gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp)
+{
+	GnomeCanvas *canvas;
+	ArtUta *uta;
+
+	canvas = item->canvas;
+	if (canvas->aa) {
+		if (svp != NULL) {
+			uta = art_uta_from_svp (svp);
+			gnome_canvas_request_redraw_uta (canvas, uta);
+		}
+	} else {
+		gnome_canvas_request_redraw (canvas, item->x1, item->y1, item->x2, item->y2);		
+	}
+}
+
+/**
+ * gnome_canvas_update_bbox:
+ * @item: the canvas item needing update
+ * @x1: Left coordinate of the new bounding box
+ * @y1: Top coordinate of the new bounding box
+ * @x2: Right coordinate of the new bounding box
+ * @y2: Bottom coordinate of the new bounding box
+ *
+ * Sets the bbox to the new value, requesting full repaint.
+ **/
+void
+gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2)
+{
+	gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+	item->x1 = x1;
+	item->y1 = y1;
+	item->x2 = x2;
+	item->y2 = y2;
+	gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+}
+
+/**
+ * gnome_canvas_buf_ensure_buf:
+ * @buf: the buf that needs to be represened in RGB format
+ *
+ * Ensure that the buffer is in RGB format, suitable for compositing.
+ **/
+void
+gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf)
+{
+	guchar *bufptr;
+	int y;
+
+	if (!buf->is_buf) {
+		bufptr = buf->buf;
+		for (y = buf->rect.y0; y < buf->rect.y1; y++) {
+			art_rgb_fill_run (bufptr,
+					  buf->bg_color >> 16,
+					  (buf->bg_color >> 8) & 0xff,
+					  buf->bg_color & 0xff,
+					  buf->rect.x1 - buf->rect.x0);
+			bufptr += buf->buf_rowstride;
+		}
+		buf->is_buf = 1;
+	}
+}
+
+/**
+ * gnome_canvas_join_gdk_to_art
+ * @gdk_join: a join type, represented in GDK format
+ *
+ * Convert from GDK line join specifier to libart.
+ *
+ * Return value: The line join specifier in libart format.
+ **/
+ArtPathStrokeJoinType
+gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join)
+{
+	switch (gdk_join) {
+	case GDK_JOIN_MITER:
+		return ART_PATH_STROKE_JOIN_MITER;
+
+	case GDK_JOIN_ROUND:
+		return ART_PATH_STROKE_JOIN_ROUND;
+
+	case GDK_JOIN_BEVEL:
+		return ART_PATH_STROKE_JOIN_BEVEL;
+
+	default:
+		g_assert_not_reached ();
+		return ART_PATH_STROKE_JOIN_MITER; /* shut up the compiler */
+	}
+}
+
+/**
+ * gnome_canvas_cap_gdk_to_art
+ * @gdk_cap: a cap type, represented in GDK format
+ *
+ * Convert from GDK line cap specifier to libart.
+ *
+ * Return value: The line cap specifier in libart format.
+ **/
+ArtPathStrokeCapType
+gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap)
+{
+	switch (gdk_cap) {
+	case GDK_CAP_BUTT:
+	case GDK_CAP_NOT_LAST:
+		return ART_PATH_STROKE_CAP_BUTT;
+
+	case GDK_CAP_ROUND:
+		return ART_PATH_STROKE_CAP_ROUND;
+
+	case GDK_CAP_PROJECTING:
+		return ART_PATH_STROKE_CAP_SQUARE;
+
+	default:
+		g_assert_not_reached ();
+		return ART_PATH_STROKE_CAP_BUTT; /* shut up the compiler */
+	}
+}
diff --git a/src/libgnomecanvas/gnome-canvas-util.h b/src/libgnomecanvas/gnome-canvas-util.h
new file mode 100644
index 0000000..cfd32c3
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-util.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Miscellaneous utility functions for the GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef GNOME_CANVAS_UTIL_H
+#define GNOME_CANVAS_UTIL_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+
+
+G_BEGIN_DECLS
+
+typedef struct _GnomeCanvasPoints GnomeCanvasPoints;
+
+/* This structure defines an array of points.  X coordinates are stored in the even-numbered
+ * indices, and Y coordinates are stored in the odd-numbered indices.  num_points indicates the
+ * number of points, so the array is 2*num_points elements big.
+ */
+struct _GnomeCanvasPoints {
+	double *coords;
+	int num_points;
+	int ref_count;
+};
+
+
+/* Allocate a new GnomeCanvasPoints structure with enough space for the specified number of points */
+GnomeCanvasPoints *gnome_canvas_points_new (int num_points);
+
+/* Increate ref count */
+GnomeCanvasPoints *gnome_canvas_points_ref (GnomeCanvasPoints *points);
+#define gnome_canvas_points_unref gnome_canvas_points_free
+
+/* Decrease ref count and free structure if it has reached zero */
+void gnome_canvas_points_free (GnomeCanvasPoints *points);
+
+/* Given three points forming an angle, compute the coordinates of the inside and outside points of
+ * the mitered corner formed by a line of a given width at that angle.
+ *
+ * If the angle is less than 11 degrees, then FALSE is returned and the return points are not
+ * modified.  Otherwise, TRUE is returned.
+ */
+int gnome_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+				   double width,
+				   double *mx1, double *my1, double *mx2, double *my2);
+
+/* Compute the butt points of a line segment.  If project is FALSE, then the results are as follows:
+ *
+ *            -------------------* (bx1, by1)
+ *                               |
+ *   (x1, y1) *------------------* (x2, y2)
+ *                               |
+ *            -------------------* (bx2, by2)
+ *
+ * that is, the line is not projected beyond (x2, y2).  If project is TRUE, then the results are as
+ * follows:
+ *
+ *            -------------------* (bx1, by1)
+ *                      (x2, y2) |
+ *   (x1, y1) *-------------*    |
+ *                               |
+ *            -------------------* (bx2, by2)
+ */
+void gnome_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+				   double width, int project,
+				   double *bx1, double *by1, double *bx2, double *by2);
+
+/* Calculate the distance from a polygon to a point.  The polygon's X coordinates are in the even
+ * indices of the poly array, and the Y coordinates are in the odd indices.
+ */
+double gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y);
+
+
+/* Render the svp over the buf. */
+void gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This function takes responsibility for
+ * freeing new_svp.
+ */
+void gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp);
+
+/* Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ */
+void gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp,
+				   ArtSVP *clip_svp);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp. This routine also adds the
+ * svp's bbox to the item's.
+ */
+void gnome_canvas_item_reset_bounds (GnomeCanvasItem *item);
+
+/* Sets the svp to the new value, requesting repaint on what's changed. This function takes responsibility for
+ * freeing new_svp. This routine also adds the svp's bbox to the item's.
+ */
+void gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp);
+
+/* Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ */
+void gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
+					ArtSVP *clip_svp);
+
+/* Request redraw of the svp if in aa mode, or the entire item in in xlib
+ * mode.
+ */
+void gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp);
+
+/* Sets the bbox to the new value, requesting full repaint. */
+void gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2);
+
+/* Ensure that the buffer is in RGB format, suitable for compositing. */
+void gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf);
+
+/* Convert from GDK line join specifier to libart. */
+ArtPathStrokeJoinType gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join);
+
+/* Convert from GDK line cap specifier to libart. */
+ArtPathStrokeCapType gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas-widget.c b/src/libgnomecanvas/gnome-canvas-widget.c
new file mode 100644
index 0000000..dd7cbb6
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-widget.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Widget item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#include <config.h>
+#include <math.h>
+#include <gtk/gtksignal.h>
+#include "gnome-canvas-widget.h"
+
+enum {
+	PROP_0,
+	PROP_WIDGET,
+	PROP_X,
+	PROP_Y,
+	PROP_WIDTH,
+	PROP_HEIGHT,
+	PROP_ANCHOR,
+	PROP_SIZE_PIXELS
+};
+
+
+static void gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class);
+static void gnome_canvas_widget_init       (GnomeCanvasWidget      *witem);
+static void gnome_canvas_widget_destroy    (GtkObject              *object);
+static void gnome_canvas_widget_get_property (GObject            *object,
+					      guint               param_id,
+					      GValue             *value,
+					      GParamSpec         *pspec);
+static void gnome_canvas_widget_set_property (GObject            *object,
+					      guint               param_id,
+					      const GValue       *value,
+					      GParamSpec         *pspec);
+
+static void   gnome_canvas_widget_update      (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static double gnome_canvas_widget_point       (GnomeCanvasItem *item, double x, double y,
+					       int cx, int cy, GnomeCanvasItem **actual_item);
+static void   gnome_canvas_widget_bounds      (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+static void gnome_canvas_widget_render (GnomeCanvasItem *item,
+					GnomeCanvasBuf *buf);
+static void gnome_canvas_widget_draw (GnomeCanvasItem *item,
+				      GdkDrawable *drawable,
+				      int x, int y,
+				      int width, int height);
+
+static GnomeCanvasItemClass *parent_class;
+
+
+GType
+gnome_canvas_widget_get_type (void)
+{
+	static GType widget_type;
+
+	if (!widget_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasWidgetClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_widget_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasWidget),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_widget_init,
+			NULL			/* value_table */
+		};
+
+		widget_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasWidget",
+						      &object_info, 0);
+	}
+
+	return widget_type;
+}
+
+static void
+gnome_canvas_widget_class_init (GnomeCanvasWidgetClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_widget_set_property;
+	gobject_class->get_property = gnome_canvas_widget_get_property;
+
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDGET,
+                 g_param_spec_object ("widget", NULL, NULL,
+                                      GTK_TYPE_WIDGET,
+                                      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_X,
+                 g_param_spec_double ("x", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_Y,
+                 g_param_spec_double ("y", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_WIDTH,
+                 g_param_spec_double ("width", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_HEIGHT,
+                 g_param_spec_double ("height", NULL, NULL,
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_ANCHOR,
+                 g_param_spec_enum ("anchor", NULL, NULL,
+                                    GTK_TYPE_ANCHOR_TYPE,
+                                    GTK_ANCHOR_NW,
+                                    (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+        g_object_class_install_property
+                (gobject_class,
+                 PROP_SIZE_PIXELS,
+                 g_param_spec_boolean ("size_pixels", NULL, NULL,
+				       FALSE,
+				       (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_widget_destroy;
+
+	item_class->update = gnome_canvas_widget_update;
+	item_class->point = gnome_canvas_widget_point;
+	item_class->bounds = gnome_canvas_widget_bounds;
+	item_class->render = gnome_canvas_widget_render;
+	item_class->draw = gnome_canvas_widget_draw;
+}
+
+static void
+gnome_canvas_widget_init (GnomeCanvasWidget *witem)
+{
+	witem->x = 0.0;
+	witem->y = 0.0;
+	witem->width = 0.0;
+	witem->height = 0.0;
+	witem->anchor = GTK_ANCHOR_NW;
+	witem->size_pixels = FALSE;
+}
+
+static void
+gnome_canvas_widget_destroy (GtkObject *object)
+{
+	GnomeCanvasWidget *witem;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+	witem = GNOME_CANVAS_WIDGET (object);
+
+	if (witem->widget && !witem->in_destroy) {
+		g_signal_handler_disconnect (witem->widget, witem->destroy_id);
+		gtk_widget_destroy (witem->widget);
+		witem->widget = NULL;
+	}
+
+	if (GTK_OBJECT_CLASS (parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+recalc_bounds (GnomeCanvasWidget *witem)
+{
+	GnomeCanvasItem *item;
+	double wx, wy;
+
+	item = GNOME_CANVAS_ITEM (witem);
+
+	/* Get world coordinates */
+
+	wx = witem->x;
+	wy = witem->y;
+	gnome_canvas_item_i2w (item, &wx, &wy);
+
+	/* Get canvas pixel coordinates */
+
+	gnome_canvas_w2c (item->canvas, wx, wy, &witem->cx, &witem->cy);
+
+	/* Anchor widget item */
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		witem->cx -= witem->cwidth / 2;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		witem->cx -= witem->cwidth;
+		break;
+
+        default:
+                break;
+	}
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		witem->cy -= witem->cheight / 2;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		witem->cy -= witem->cheight;
+		break;
+
+        default:
+                break;
+	}
+
+	/* Bounds */
+
+	item->x1 = witem->cx;
+	item->y1 = witem->cy;
+	item->x2 = witem->cx + witem->cwidth;
+	item->y2 = witem->cy + witem->cheight;
+
+	if (witem->widget)
+		gtk_layout_move (GTK_LAYOUT (item->canvas), witem->widget,
+				 witem->cx + item->canvas->zoom_xofs,
+				 witem->cy + item->canvas->zoom_yofs);
+}
+
+static void
+do_destroy (GtkObject *object, gpointer data)
+{
+	GnomeCanvasWidget *witem;
+
+	witem = data;
+
+	witem->in_destroy = TRUE;
+
+	gtk_object_destroy (data);
+}
+
+static void
+gnome_canvas_widget_set_property (GObject            *object,
+				  guint               param_id,
+				  const GValue       *value,
+				  GParamSpec         *pspec)
+{
+	GnomeCanvasItem *item;
+	GnomeCanvasWidget *witem;
+	GObject *obj;
+	int update;
+	int calc_bounds;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+	witem = GNOME_CANVAS_WIDGET (object);
+
+	update = FALSE;
+	calc_bounds = FALSE;
+
+	switch (param_id) {
+	case PROP_WIDGET:
+		if (witem->widget) {
+			g_signal_handler_disconnect (witem->widget, witem->destroy_id);
+			gtk_container_remove (GTK_CONTAINER (item->canvas), witem->widget);
+		}
+
+		obj = g_value_get_object (value);
+		if (obj) {
+			witem->widget = GTK_WIDGET (obj);
+			witem->destroy_id = g_signal_connect (obj, "destroy",
+							      G_CALLBACK (do_destroy),
+							      witem);
+			gtk_layout_put (GTK_LAYOUT (item->canvas), witem->widget,
+					witem->cx + item->canvas->zoom_xofs,
+					witem->cy + item->canvas->zoom_yofs);
+		}
+
+		update = TRUE;
+		break;
+
+	case PROP_X:
+	        if (witem->x != g_value_get_double (value))
+		{
+		        witem->x = g_value_get_double (value);
+			calc_bounds = TRUE;
+		}
+		break;
+
+	case PROP_Y:
+	        if (witem->y != g_value_get_double (value))
+		{
+		        witem->y = g_value_get_double (value);
+			calc_bounds = TRUE;
+		}
+		break;
+
+	case PROP_WIDTH:
+	        if (witem->width != fabs (g_value_get_double (value)))
+		{
+		        witem->width = fabs (g_value_get_double (value));
+			update = TRUE;
+		}
+		break;
+
+	case PROP_HEIGHT:
+	        if (witem->height != fabs (g_value_get_double (value)))
+		{
+		        witem->height = fabs (g_value_get_double (value));
+			update = TRUE;
+		}
+		break;
+
+	case PROP_ANCHOR:
+	        if (witem->anchor != g_value_get_enum (value))
+		{
+		        witem->anchor = g_value_get_enum (value);
+			update = TRUE;
+		}
+		break;
+
+	case PROP_SIZE_PIXELS:
+	        if (witem->size_pixels != g_value_get_boolean (value))
+		{
+		        witem->size_pixels = g_value_get_boolean (value);
+			update = TRUE;
+		}
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+
+	if (update)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->update) (item, NULL, NULL, 0);
+
+	if (calc_bounds)
+		recalc_bounds (witem);
+}
+
+static void
+gnome_canvas_widget_get_property (GObject            *object,
+				  guint               param_id,
+				  GValue             *value,
+				  GParamSpec         *pspec)
+{
+	GnomeCanvasWidget *witem;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_WIDGET (object));
+
+	witem = GNOME_CANVAS_WIDGET (object);
+
+	switch (param_id) {
+	case PROP_WIDGET:
+		g_value_set_object (value, (GObject *) witem->widget);
+		break;
+
+	case PROP_X:
+		g_value_set_double (value, witem->x);
+		break;
+
+	case PROP_Y:
+		g_value_set_double (value, witem->y);
+		break;
+
+	case PROP_WIDTH:
+		g_value_set_double (value, witem->width);
+		break;
+
+	case PROP_HEIGHT:
+		g_value_set_double (value, witem->height);
+		break;
+
+	case PROP_ANCHOR:
+		g_value_set_enum (value, witem->anchor);
+		break;
+
+	case PROP_SIZE_PIXELS:
+		g_value_set_boolean (value, witem->size_pixels);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_widget_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasWidget *witem;
+
+	witem = GNOME_CANVAS_WIDGET (item);
+
+	if (parent_class->update)
+		(* parent_class->update) (item, affine, clip_path, flags);
+
+	if (witem->widget) {
+		if (witem->size_pixels) {
+			witem->cwidth = (int) (witem->width + 0.5);
+			witem->cheight = (int) (witem->height + 0.5);
+		} else {
+			witem->cwidth = (int) (witem->width * item->canvas->pixels_per_unit + 0.5);
+			witem->cheight = (int) (witem->height * item->canvas->pixels_per_unit + 0.5);
+		}
+
+		gtk_widget_set_size_request (witem->widget, witem->cwidth, witem->cheight);
+	} else {
+		witem->cwidth = 0.0;
+		witem->cheight = 0.0;
+	}
+
+	recalc_bounds (witem);
+}
+
+static void
+gnome_canvas_widget_render (GnomeCanvasItem *item,
+			    GnomeCanvasBuf *buf)
+{
+#if 0
+	GnomeCanvasWidget *witem;
+
+	witem = GNOME_CANVAS_WIDGET (item);
+
+	if (witem->widget) 
+		gtk_widget_queue_draw (witem->widget);
+#endif
+
+}
+
+static void
+gnome_canvas_widget_draw (GnomeCanvasItem *item,
+			  GdkDrawable *drawable,
+			  int x, int y,
+			  int width, int height)
+{
+#if 0
+	GnomeCanvasWidget *witem;
+
+	witem = GNOME_CANVAS_WIDGET (item);
+
+	if (witem->widget)
+		gtk_widget_queue_draw (witem->widget);
+#endif
+}
+
+static double
+gnome_canvas_widget_point (GnomeCanvasItem *item, double x, double y,
+			   int cx, int cy, GnomeCanvasItem **actual_item)
+{
+	GnomeCanvasWidget *witem;
+	double x1, y1, x2, y2;
+	double dx, dy;
+
+	witem = GNOME_CANVAS_WIDGET (item);
+
+	*actual_item = item;
+
+	gnome_canvas_c2w (item->canvas, witem->cx, witem->cy, &x1, &y1);
+
+	x2 = x1 + (witem->cwidth - 1) / item->canvas->pixels_per_unit;
+	y2 = y1 + (witem->cheight - 1) / item->canvas->pixels_per_unit;
+
+	/* Is point inside widget bounds? */
+
+	if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2))
+		return 0.0;
+
+	/* Point is outside widget bounds */
+
+	if (x < x1)
+		dx = x1 - x;
+	else if (x > x2)
+		dx = x - x2;
+	else
+		dx = 0.0;
+
+	if (y < y1)
+		dy = y1 - y;
+	else if (y > y2)
+		dy = y - y2;
+	else
+		dy = 0.0;
+
+	return sqrt (dx * dx + dy * dy);
+}
+
+static void
+gnome_canvas_widget_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	GnomeCanvasWidget *witem;
+
+	witem = GNOME_CANVAS_WIDGET (item);
+
+	*x1 = witem->x;
+	*y1 = witem->y;
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_SW:
+		break;
+
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_S:
+		*x1 -= witem->width / 2.0;
+		break;
+
+	case GTK_ANCHOR_NE:
+	case GTK_ANCHOR_E:
+	case GTK_ANCHOR_SE:
+		*x1 -= witem->width;
+		break;
+
+        default:
+                break;
+	}
+
+	switch (witem->anchor) {
+	case GTK_ANCHOR_NW:
+	case GTK_ANCHOR_N:
+	case GTK_ANCHOR_NE:
+		break;
+
+	case GTK_ANCHOR_W:
+	case GTK_ANCHOR_CENTER:
+	case GTK_ANCHOR_E:
+		*y1 -= witem->height / 2.0;
+		break;
+
+	case GTK_ANCHOR_SW:
+	case GTK_ANCHOR_S:
+	case GTK_ANCHOR_SE:
+		*y1 -= witem->height;
+		break;
+
+        default:
+                break;
+	}
+
+	*x2 = *x1 + witem->width;
+	*y2 = *y1 + witem->height;
+}
diff --git a/src/libgnomecanvas/gnome-canvas-widget.h b/src/libgnomecanvas/gnome-canvas-widget.h
new file mode 100644
index 0000000..3eef1d9
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas-widget.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* Widget item type for GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico nuclecu unam mx>
+ */
+
+#ifndef GNOME_CANVAS_WIDGET_H
+#define GNOME_CANVAS_WIDGET_H
+
+
+#include <libgnomecanvas/gnome-canvas.h>
+
+
+G_BEGIN_DECLS
+
+
+/* Widget item for canvas.  The widget is positioned with respect to an anchor point.
+ * The following object arguments are available:
+ *
+ * name			type			read/write	description
+ * ------------------------------------------------------------------------------------------
+ * widget		GtkWidget*		RW		Pointer to the widget
+ * x			double			RW		X coordinate of anchor point
+ * y			double			RW		Y coordinate of anchor point
+ * width		double			RW		Width of widget (see below)
+ * height		double			RW		Height of widget (see below)
+ * anchor		GtkAnchorType		RW		Anchor side for widget
+ * size_pixels		boolean			RW		Specifies whether the widget size
+ *								is specified in pixels or canvas units.
+ *								If it is in pixels, then the widget will not
+ *								be scaled when the canvas zoom factor changes.
+ *								Otherwise, it will be scaled.
+ */
+
+
+#define GNOME_TYPE_CANVAS_WIDGET            (gnome_canvas_widget_get_type ())
+#define GNOME_CANVAS_WIDGET(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidget))
+#define GNOME_CANVAS_WIDGET_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidgetClass))
+#define GNOME_IS_CANVAS_WIDGET(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_WIDGET))
+#define GNOME_IS_CANVAS_WIDGET_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_WIDGET))
+#define GNOME_CANVAS_WIDGET_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_WIDGET, GnomeCanvasWidgetClass))
+
+
+typedef struct _GnomeCanvasWidget GnomeCanvasWidget;
+typedef struct _GnomeCanvasWidgetClass GnomeCanvasWidgetClass;
+
+struct _GnomeCanvasWidget {
+	GnomeCanvasItem item;
+
+	GtkWidget *widget;		/* The child widget */
+
+	double x, y;			/* Position at anchor */
+	double width, height;		/* Dimensions of widget */
+	GtkAnchorType anchor;		/* Anchor side for widget */
+
+	int cx, cy;			/* Top-left canvas coordinates for widget */
+	int cwidth, cheight;		/* Size of widget in pixels */
+
+	guint destroy_id;		/* Signal connection id for destruction of child widget */
+
+	guint size_pixels : 1;		/* Is size specified in (unchanging) pixels or units (get scaled)? */
+	guint in_destroy : 1;		/* Is child widget being destroyed? */
+};
+
+struct _GnomeCanvasWidgetClass {
+	GnomeCanvasItemClass parent_class;
+};
+
+
+/* Standard Gtk function */
+GType gnome_canvas_widget_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/gnome-canvas.c b/src/libgnomecanvas/gnome-canvas.c
new file mode 100644
index 0000000..a211f0c
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas.c
@@ -0,0 +1,4081 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/*
+ * GnomeCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget.  Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph gimp org>
+ */
+
+/*
+ * TO-DO list for the canvas:
+ *
+ * - Allow to specify whether GnomeCanvasImage sizes are in units or pixels (scale or don't scale).
+ *
+ * - Implement a flag for gnome_canvas_item_reparent() that tells the function to keep the item
+ *   visually in the same place, that is, to keep it in the same place with respect to the canvas
+ *   origin.
+ *
+ * - GC put functions for items.
+ *
+ * - Widget item (finish it).
+ *
+ * - GList *gnome_canvas_gimme_all_items_contained_in_this_area (GnomeCanvas *canvas, Rectangle area);
+ *
+ * - Retrofit all the primitive items with microtile support.
+ *
+ * - Curve support for line item.
+ *
+ * - Arc item (Havoc has it; to be integrated in GnomeCanvasEllipse).
+ *
+ * - Sane font handling API.
+ *
+ * - Get_arg methods for items:
+ *   - How to fetch the outline width and know whether it is in pixels or units?
+ */
+
+/*
+ * Raph's TODO list for the antialiased canvas integration:
+ *
+ * - ::point() method for text item not accurate when affine transformed.
+ *
+ * - Clip rectangle not implemented in aa renderer for text item.
+ *
+ * - Clip paths only partially implemented.
+ *
+ * - Add more image loading techniques to work around imlib deficiencies.
+ */
+
+#include <config.h>
+
+#include <math.h>
+#include <string.h>
+#include <stdio.h>
+#include <gdk/gdkprivate.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-i18n.h"
+#include "libart_lgpl/art_rect.h"
+#include "libart_lgpl/art_rect_uta.h"
+#include "libart_lgpl/art_uta_rect.h"
+#include "libart_lgpl/art_uta_ops.h"
+
+#include "gnome-canvas-marshal.h"
+#include "gnome-canvas-marshal.c"
+
+
+/* We must run our idle update handler *before* GDK wants to redraw. */
+#define CANVAS_IDLE_PRIORITY (GDK_PRIORITY_REDRAW - 5)
+
+
+static void gnome_canvas_request_update (GnomeCanvas      *canvas);
+static void group_add                   (GnomeCanvasGroup *group,
+					 GnomeCanvasItem  *item);
+static void group_remove                (GnomeCanvasGroup *group,
+					 GnomeCanvasItem  *item);
+static void add_idle                    (GnomeCanvas      *canvas);
+
+
+/*** GnomeCanvasItem ***/
+
+/* Some convenience stuff */
+#define GCI_UPDATE_MASK (GNOME_CANVAS_UPDATE_REQUESTED | GNOME_CANVAS_UPDATE_AFFINE | GNOME_CANVAS_UPDATE_CLIP | GNOME_CANVAS_UPDATE_VISIBILITY)
+#define GCI_EPSILON 1e-18
+#define GCI_PRINT_MATRIX(s,a) g_print ("%s %g %g %g %g %g %g\n", s, (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
+
+enum {
+	ITEM_PROP_0,
+	ITEM_PROP_PARENT
+};
+
+enum {
+	ITEM_EVENT,
+	ITEM_LAST_SIGNAL
+};
+
+static void gnome_canvas_item_class_init     (GnomeCanvasItemClass *class);
+static void gnome_canvas_item_init           (GnomeCanvasItem      *item);
+static int  emit_event                       (GnomeCanvas *canvas, GdkEvent *event);
+
+static guint item_signals[ITEM_LAST_SIGNAL];
+
+static GtkObjectClass *item_parent_class;
+
+
+/**
+ * gnome_canvas_item_get_type:
+ *
+ * Registers the &GnomeCanvasItem class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &GnomeCanvasItem class.
+ **/
+GType
+gnome_canvas_item_get_type (void)
+{
+	static GType canvas_item_type;
+
+	if (!canvas_item_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasItemClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_item_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasItem),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_item_init,
+			NULL			/* value_table */
+		};
+
+		canvas_item_type = g_type_register_static (GTK_TYPE_OBJECT, "GnomeCanvasItem",
+							   &object_info, 0);
+	}
+
+	return canvas_item_type;
+}
+
+/* Object initialization function for GnomeCanvasItem */
+static void
+gnome_canvas_item_init (GnomeCanvasItem *item)
+{
+	item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
+}
+
+/**
+ * gnome_canvas_item_new:
+ * @parent: The parent group for the new item.
+ * @type: The object type of the item.
+ * @first_arg_name: A list of object argument name/value pairs, NULL-terminated,
+ * used to configure the item.  For example, "fill_color", "black",
+ * "width_units", 5.0, NULL.
+ * @Varargs:
+ *
+ * Creates a new canvas item with @parent as its parent group.  The item is
+ * created at the top of its parent's stack, and starts up as visible.  The item
+ * is of the specified @type, for example, it can be
+ * gnome_canvas_rect_get_type().  The list of object arguments/value pairs is
+ * used to configure the item. If you need to pass construct time parameters, you
+ * should use g_object_new() to pass the parameters and
+ * gnome_canvas_item_construct() to set up the canvas item.
+ *
+ * Return value: The newly-created item.
+ **/
+GnomeCanvasItem *
+gnome_canvas_item_new (GnomeCanvasGroup *parent, GType type, const gchar *first_arg_name, ...)
+{
+	GnomeCanvasItem *item;
+	va_list args;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS_GROUP (parent), NULL);
+	g_return_val_if_fail (g_type_is_a (type, gnome_canvas_item_get_type ()), NULL);
+
+	item = GNOME_CANVAS_ITEM (g_object_new (type, NULL));
+
+	va_start (args, first_arg_name);
+	gnome_canvas_item_construct (item, parent, first_arg_name, args);
+	va_end (args);
+
+	return item;
+}
+
+
+/* Performs post-creation operations on a canvas item (adding it to its parent
+ * group, etc.)
+ */
+static void
+item_post_create_setup (GnomeCanvasItem *item)
+{
+	group_add (GNOME_CANVAS_GROUP (item->parent), item);
+
+	gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+	item->canvas->need_repick = TRUE;
+}
+
+/* Set_property handler for canvas items */
+static void
+gnome_canvas_item_set_property (GObject *gobject, guint param_id,
+				const GValue *value, GParamSpec *pspec)
+{
+	GnomeCanvasItem *item;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
+
+	item = GNOME_CANVAS_ITEM (gobject);
+
+	switch (param_id) {
+	case ITEM_PROP_PARENT:
+		if (item->parent != NULL) {
+		    g_warning ("Cannot set `parent' argument after item has "
+			       "already been constructed.");
+		} else if (g_value_get_object (value)) {
+			item->parent = GNOME_CANVAS_ITEM (g_value_get_object (value));
+			item->canvas = item->parent->canvas;
+			item_post_create_setup (item);
+		}
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/* Get_property handler for canvas items */
+static void
+gnome_canvas_item_get_property (GObject *gobject, guint param_id,
+				GValue *value, GParamSpec *pspec)
+{
+	GnomeCanvasItem *item;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (gobject));
+
+	item = GNOME_CANVAS_ITEM (gobject);
+
+	switch (param_id) {
+	case ITEM_PROP_PARENT:
+		g_value_set_object (value, item->parent);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/**
+ * gnome_canvas_item_construct:
+ * @item: An unconstructed canvas item.
+ * @parent: The parent group for the item.
+ * @first_arg_name: The name of the first argument for configuring the item.
+ * @args: The list of arguments used to configure the item.
+ *
+ * Constructs a canvas item; meant for use only by item implementations.
+ **/
+void
+gnome_canvas_item_construct (GnomeCanvasItem *item, GnomeCanvasGroup *parent,
+			     const gchar *first_arg_name, va_list args)
+{
+	g_return_if_fail (GNOME_IS_CANVAS_GROUP (parent));
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	item->parent = GNOME_CANVAS_ITEM (parent);
+	item->canvas = item->parent->canvas;
+
+	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+	item_post_create_setup (item);
+}
+
+
+/* If the item is visible, requests a redraw of it. */
+static void
+redraw_if_visible (GnomeCanvasItem *item)
+{
+	if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+}
+
+/* Standard object dispose function for canvas items */
+static void
+gnome_canvas_item_dispose (GObject *object)
+{
+	GnomeCanvasItem *item;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (object));
+
+	item = GNOME_CANVAS_ITEM (object);
+
+	if (item->canvas)
+		redraw_if_visible (item);
+
+	/* Make the canvas forget about us */
+
+	if (item->canvas && item == item->canvas->current_item) {
+		item->canvas->current_item = NULL;
+		item->canvas->need_repick = TRUE;
+	}
+
+	if (item->canvas && item == item->canvas->new_current_item) {
+		item->canvas->new_current_item = NULL;
+		item->canvas->need_repick = TRUE;
+	}
+
+	if (item->canvas && item == item->canvas->grabbed_item) {
+		item->canvas->grabbed_item = NULL;
+		gdk_pointer_ungrab (GDK_CURRENT_TIME);
+	}
+
+	if (item->canvas && item == item->canvas->focused_item)
+		item->canvas->focused_item = NULL;
+
+	/* Normal destroy stuff */
+
+	if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+	if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+	if (item->parent)
+		group_remove (GNOME_CANVAS_GROUP (item->parent), item);
+
+	g_free (item->xform);
+	item->xform = NULL;
+
+	G_OBJECT_CLASS (item_parent_class)->dispose (object);
+	/* items should remove any reference to item->canvas after the
+	   first ::destroy */
+	item->canvas = NULL;
+}
+
+/* Realize handler for canvas items */
+static void
+gnome_canvas_item_realize (GnomeCanvasItem *item)
+{
+	GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
+
+	gnome_canvas_item_request_update (item);
+}
+
+/* Unrealize handler for canvas items */
+static void
+gnome_canvas_item_unrealize (GnomeCanvasItem *item)
+{
+	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_REALIZED);
+}
+
+/* Map handler for canvas items */
+static void
+gnome_canvas_item_map (GnomeCanvasItem *item)
+{
+	GTK_OBJECT_SET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
+}
+
+/* Unmap handler for canvas items */
+static void
+gnome_canvas_item_unmap (GnomeCanvasItem *item)
+{
+	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_MAPPED);
+}
+
+/* Update handler for canvas items */
+static void
+gnome_canvas_item_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_UPDATE);
+	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_AFFINE);
+	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_CLIP);
+	GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_NEED_VIS);
+}
+
+#define noHACKISH_AFFINE
+
+/*
+ * This routine invokes the update method of the item
+ * Please notice, that we take parent to canvas pixel matrix as argument
+ * unlike virtual method ::update, whose argument is item 2 canvas pixel
+ * matrix
+ *
+ * I will try to force somewhat meaningful naming for affines (Lauris)
+ * General naming rule is FROM2TO, where FROM and TO are abbreviations
+ * So p2cpx is Parent2CanvasPixel and i2cpx is Item2CanvasPixel
+ * I hope that this helps to keep track of what really happens
+ *
+ */
+
+static void
+gnome_canvas_item_invoke_update (GnomeCanvasItem *item, double *p2cpx, ArtSVP *clip_path, int flags)
+{
+	int child_flags;
+	gdouble i2cpx[6];
+
+#ifdef HACKISH_AFFINE
+	double i2w[6], w2c[6], i2c[6];
+#endif
+
+	child_flags = flags;
+	if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+		child_flags &= ~GNOME_CANVAS_UPDATE_IS_VISIBLE;
+
+	/* Calculate actual item transformation matrix */
+
+	if (item->xform) {
+		if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+			/* Item has full affine */
+			art_affine_multiply (i2cpx, item->xform, p2cpx);
+		} else {
+			/* Item has only translation */
+			memcpy (i2cpx, p2cpx, 4 * sizeof (gdouble));
+			i2cpx[4] = item->xform[0] * p2cpx[0] + item->xform[1] * p2cpx[2] + p2cpx[4];
+			i2cpx[5] = item->xform[0] * p2cpx[1] + item->xform[1] * p2cpx[3] + p2cpx[5];
+		}
+	} else {
+		/* Item has no matrix (i.e. identity) */
+		memcpy (i2cpx, p2cpx, 6 * sizeof (gdouble));
+	}
+
+#ifdef HACKISH_AFFINE
+	gnome_canvas_item_i2w_affine (item, i2w);
+	gnome_canvas_w2c_affine (item->canvas, w2c);
+	art_affine_multiply (i2c, i2w, w2c);
+	/* invariant (doesn't hold now): child_affine == i2c */
+	child_affine = i2c;
+#endif
+
+	/* apply object flags to child flags */
+
+	child_flags &= ~GNOME_CANVAS_UPDATE_REQUESTED;
+
+	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
+		child_flags |= GNOME_CANVAS_UPDATE_REQUESTED;
+
+	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)
+		child_flags |= GNOME_CANVAS_UPDATE_AFFINE;
+
+	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_CLIP)
+		child_flags |= GNOME_CANVAS_UPDATE_CLIP;
+
+	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_VIS)
+		child_flags |= GNOME_CANVAS_UPDATE_VISIBILITY;
+
+	if (child_flags & GCI_UPDATE_MASK) {
+		if (GNOME_CANVAS_ITEM_GET_CLASS (item)->update)
+			GNOME_CANVAS_ITEM_GET_CLASS (item)->update (item, i2cpx, clip_path, child_flags);
+	}
+}
+
+/*
+ * This routine invokes the point method of the item.
+ * The arguments x, y should be in the parent item local coordinates.
+ *
+ * This is potentially evil, as we are relying on matrix inversion (Lauris)
+ */
+
+static double
+gnome_canvas_item_invoke_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
+{
+	/* Calculate x & y in item local coordinates */
+
+	if (item->xform) {
+		if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+			gdouble p2i[6], t;
+			/* Item has full affine */
+			art_affine_invert (p2i, item->xform);
+			t = x * p2i[0] + y * p2i[2] + p2i[4];
+			y = x * p2i[1] + y * p2i[3] + p2i[5];
+			x = t;
+		} else {
+			/* Item has only translation */
+			x -= item->xform[0];
+			y -= item->xform[1];
+		}
+	}
+
+#ifdef HACKISH_AFFINE
+	double i2w[6], w2c[6], i2c[6], c2i[6];
+	ArtPoint c, i;
+#endif
+
+#ifdef HACKISH_AFFINE
+	gnome_canvas_item_i2w_affine (item, i2w);
+	gnome_canvas_w2c_affine (item->canvas, w2c);
+	art_affine_multiply (i2c, i2w, w2c);
+	art_affine_invert (c2i, i2c);
+	c.x = cx;
+	c.y = cy;
+	art_affine_point (&i, &c, c2i);
+	x = i.x;
+	y = i.y;
+#endif
+
+	if (GNOME_CANVAS_ITEM_GET_CLASS (item)->point)
+		return GNOME_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
+
+	return 1e18;
+}
+
+/**
+ * gnome_canvas_item_set:
+ * @item: A canvas item.
+ * @first_arg_name: The list of object argument name/value pairs used to configure the item.
+ * @Varargs:
+ *
+ * Configures a canvas item.  The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+gnome_canvas_item_set (GnomeCanvasItem *item, const gchar *first_arg_name, ...)
+{
+	va_list args;
+
+	va_start (args, first_arg_name);
+	gnome_canvas_item_set_valist (item, first_arg_name, args);
+	va_end (args);
+}
+
+
+/**
+ * gnome_canvas_item_set_valist:
+ * @item: A canvas item.
+ * @first_arg_name: The name of the first argument used to configure the item.
+ * @args: The list of object argument name/value pairs used to configure the item.
+ *
+ * Configures a canvas item.  The arguments in the item are set to the specified
+ * values, and the item is repainted as appropriate.
+ **/
+void
+gnome_canvas_item_set_valist (GnomeCanvasItem *item, const gchar *first_arg_name, va_list args)
+{
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	g_object_set_valist (G_OBJECT (item), first_arg_name, args);
+
+#if 0
+	/* I commented this out, because item implementations have to schedule update/redraw */
+	redraw_if_visible (item);
+#endif
+
+	item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * gnome_canvas_item_affine_relative:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix.
+ *
+ * Combines the specified affine transformation matrix with the item's current
+ * transformation. NULL affine is not allowed.
+ **/
+#define GCIAR_EPSILON 1e-6
+void
+gnome_canvas_item_affine_relative (GnomeCanvasItem *item, const double affine[6])
+{
+	gdouble i2p[6];
+
+	g_return_if_fail (item != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (affine != NULL);
+
+	/* Calculate actual item transformation matrix */
+
+	if (item->xform) {
+		if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+			/* Item has full affine */
+			art_affine_multiply (i2p, affine, item->xform);
+		} else {
+			/* Item has only translation */
+			memcpy (i2p, affine, 6 * sizeof (gdouble));
+			i2p[4] += item->xform[0];
+			i2p[5] += item->xform[1];
+		}
+	} else {
+		/* Item has no matrix (i.e. identity) */
+		memcpy (i2p, affine, 6 * sizeof (gdouble));
+	}
+
+	gnome_canvas_item_affine_absolute (item, i2p);
+}
+
+/**
+ * gnome_canvas_item_affine_absolute:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix.
+ *
+ * Makes the item's affine transformation matrix be equal to the specified
+ * matrix. NULL affine is treated as identity.
+ **/
+void
+gnome_canvas_item_affine_absolute (GnomeCanvasItem *item, const double i2p[6])
+{
+	g_return_if_fail (item != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	if (i2p &&
+	    (fabs (i2p[0] - 1.0) < GCI_EPSILON) &&
+	    (fabs (i2p[1] - 0.0) < GCI_EPSILON) &&
+	    (fabs (i2p[2] - 0.0) < GCI_EPSILON) &&
+	    (fabs (i2p[3] - 1.0) < GCI_EPSILON) &&
+	    (fabs (i2p[4] - 0.0) < GCI_EPSILON) &&
+	    (fabs (i2p[5] - 0.0) < GCI_EPSILON)) {
+		/* We are identity */
+		i2p = NULL;
+	}
+
+	if (i2p) {
+		if (item->xform && !(item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
+			/* We do not want to deal with translation-only affines */
+			g_free (item->xform);
+			item->xform = NULL;
+		}
+		if (!item->xform) item->xform = g_new (gdouble, 6);
+		memcpy (item->xform, i2p, 6 * sizeof (gdouble));
+		item->object.flags |= GNOME_CANVAS_ITEM_AFFINE_FULL;
+	} else {
+		if (item->xform) {
+			g_free (item->xform);
+			item->xform = NULL;
+		}
+	}
+
+	if (!(item->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+		/* Request update */
+		item->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+		gnome_canvas_item_request_update (item);
+	}
+
+	item->canvas->need_repick = TRUE;
+}
+
+
+/**
+ * gnome_canvas_item_move:
+ * @item: A canvas item.
+ * @dx: Horizontal offset.
+ * @dy: Vertical offset.
+ *
+ * Moves a canvas item by creating an affine transformation matrix for
+ * translation by using the specified values. This happens in item
+ * local coordinate system, so if you have nontrivial transform, it
+ * most probably does not do, what you want.
+ **/
+void
+gnome_canvas_item_move (GnomeCanvasItem *item, double dx, double dy)
+{
+	double translate[6];
+
+	g_return_if_fail (item != NULL);
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	art_affine_translate (translate, dx, dy);
+
+	gnome_canvas_item_affine_relative (item, translate);
+}
+
+/* Convenience function to reorder items in a group's child list.  This puts the
+ * specified link after the "before" link. Returns TRUE if the list was changed.
+ */
+static gboolean
+put_item_after (GList *link, GList *before)
+{
+	GnomeCanvasGroup *parent;
+	GList *old_before, *old_after;
+	GList *after;
+
+	parent = GNOME_CANVAS_GROUP (GNOME_CANVAS_ITEM (link->data)->parent);
+
+	if (before)
+		after = before->next;
+	else
+		after = parent->item_list;
+
+	if (before == link || after == link)
+		return FALSE;
+
+	/* Unlink */
+
+	old_before = link->prev;
+	old_after = link->next;
+
+	if (old_before)
+		old_before->next = old_after;
+	else
+		parent->item_list = old_after;
+
+	if (old_after)
+		old_after->prev = old_before;
+	else
+		parent->item_list_end = old_before;
+
+	/* Relink */
+
+	link->prev = before;
+	if (before)
+		before->next = link;
+	else
+		parent->item_list = link;
+
+	link->next = after;
+	if (after)
+		after->prev = link;
+	else
+		parent->item_list_end = link;
+
+	return TRUE;
+}
+
+
+/**
+ * gnome_canvas_item_raise:
+ * @item: A canvas item.
+ * @positions: Number of steps to raise the item.
+ *
+ * Raises the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the top of the
+ * stack, then the item is put at the top.
+ **/
+void
+gnome_canvas_item_raise (GnomeCanvasItem *item, int positions)
+{
+	GList *link, *before;
+	GnomeCanvasGroup *parent;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (positions >= 0);
+
+	if (!item->parent || positions == 0)
+		return;
+
+	parent = GNOME_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	for (before = link; positions && before; positions--)
+		before = before->next;
+
+	if (!before)
+		before = parent->item_list_end;
+
+	if (put_item_after (link, before)) {
+		redraw_if_visible (item);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+
+/**
+ * gnome_canvas_item_lower:
+ * @item: A canvas item.
+ * @positions: Number of steps to lower the item.
+ *
+ * Lowers the item in its parent's stack by the specified number of positions.
+ * If the number of positions is greater than the distance to the bottom of the
+ * stack, then the item is put at the bottom.
+ **/
+void
+gnome_canvas_item_lower (GnomeCanvasItem *item, int positions)
+{
+	GList *link, *before;
+	GnomeCanvasGroup *parent;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (positions >= 1);
+
+	if (!item->parent || positions == 0)
+		return;
+
+	parent = GNOME_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	if (link->prev)
+		for (before = link->prev; positions && before; positions--)
+			before = before->prev;
+	else
+		before = NULL;
+
+	if (put_item_after (link, before)) {
+		redraw_if_visible (item);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+
+/**
+ * gnome_canvas_item_raise_to_top:
+ * @item: A canvas item.
+ *
+ * Raises an item to the top of its parent's stack.
+ **/
+void
+gnome_canvas_item_raise_to_top (GnomeCanvasItem *item)
+{
+	GList *link;
+	GnomeCanvasGroup *parent;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	if (!item->parent)
+		return;
+
+	parent = GNOME_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	if (put_item_after (link, parent->item_list_end)) {
+		redraw_if_visible (item);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+
+/**
+ * gnome_canvas_item_lower_to_bottom:
+ * @item: A canvas item.
+ *
+ * Lowers an item to the bottom of its parent's stack.
+ **/
+void
+gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item)
+{
+	GList *link;
+	GnomeCanvasGroup *parent;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	if (!item->parent)
+		return;
+
+	parent = GNOME_CANVAS_GROUP (item->parent);
+	link = g_list_find (parent->item_list, item);
+	g_assert (link != NULL);
+
+	if (put_item_after (link, NULL)) {
+		redraw_if_visible (item);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+
+/**
+ * gnome_canvas_item_show:
+ * @item: A canvas item.
+ *
+ * Shows a canvas item.  If the item was already shown, then no action is taken.
+ **/
+void
+gnome_canvas_item_show (GnomeCanvasItem *item)
+{
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE)) {
+		item->object.flags |= GNOME_CANVAS_ITEM_VISIBLE;
+		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+
+/**
+ * gnome_canvas_item_hide:
+ * @item: A canvas item.
+ *
+ * Hides a canvas item.  If the item was already hidden, then no action is
+ * taken.
+ **/
+void
+gnome_canvas_item_hide (GnomeCanvasItem *item)
+{
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	if (item->object.flags & GNOME_CANVAS_ITEM_VISIBLE) {
+		item->object.flags &= ~GNOME_CANVAS_ITEM_VISIBLE;
+		gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2 + 1, item->y2 + 1);
+		item->canvas->need_repick = TRUE;
+	}
+}
+
+
+/**
+ * gnome_canvas_item_grab:
+ * @item: A canvas item.
+ * @event_mask: Mask of events that will be sent to this item.
+ * @cursor: If non-NULL, the cursor that will be used while the grab is active.
+ * @etime: The timestamp required for grabbing the mouse, or GDK_CURRENT_TIME.
+ *
+ * Specifies that all events that match the specified event mask should be sent
+ * to the specified item, and also grabs the mouse by calling
+ * gdk_pointer_grab().  The event mask is also used when grabbing the pointer.
+ * If @cursor is not NULL, then that cursor is used while the grab is active.
+ * The @etime parameter is the timestamp required for grabbing the mouse.
+ *
+ * Return value: If an item was already grabbed, it returns %GDK_GRAB_ALREADY_GRABBED.  If
+ * the specified item was hidden by calling gnome_canvas_item_hide(), then it
+ * returns %GDK_GRAB_NOT_VIEWABLE.  Else, it returns the result of calling
+ * gdk_pointer_grab().
+ **/
+int
+gnome_canvas_item_grab (GnomeCanvasItem *item, guint event_mask, GdkCursor *cursor, guint32 etime)
+{
+	int retval;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (item), GDK_GRAB_NOT_VIEWABLE);
+	g_return_val_if_fail (GTK_WIDGET_MAPPED (item->canvas), GDK_GRAB_NOT_VIEWABLE);
+
+	if (item->canvas->grabbed_item)
+		return GDK_GRAB_ALREADY_GRABBED;
+
+	if (!(item->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+		return GDK_GRAB_NOT_VIEWABLE;
+
+	retval = gdk_pointer_grab (item->canvas->layout.bin_window,
+				   FALSE,
+				   event_mask,
+				   NULL,
+				   cursor,
+				   etime);
+
+	if (retval != GDK_GRAB_SUCCESS)
+		return retval;
+
+	item->canvas->grabbed_item = item;
+	item->canvas->grabbed_event_mask = event_mask;
+	item->canvas->current_item = item; /* So that events go to the grabbed item */
+
+	return retval;
+}
+
+
+/**
+ * gnome_canvas_item_ungrab:
+ * @item: A canvas item that holds a grab.
+ * @etime: The timestamp for ungrabbing the mouse.
+ *
+ * Ungrabs the item, which must have been grabbed in the canvas, and ungrabs the
+ * mouse.
+ **/
+void
+gnome_canvas_item_ungrab (GnomeCanvasItem *item, guint32 etime)
+{
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	if (item->canvas->grabbed_item != item)
+		return;
+
+	item->canvas->grabbed_item = NULL;
+
+	gdk_pointer_ungrab (etime);
+}
+
+
+/**
+ * gnome_canvas_item_i2w_affine:
+ * @item: A canvas item
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from the item's coordinate system to
+ * world coordinates.
+ **/
+void
+gnome_canvas_item_i2w_affine (GnomeCanvasItem *item, double affine[6])
+{
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (affine != NULL);
+
+	art_affine_identity (affine);
+
+	while (item) {
+		if (item->xform != NULL) {
+			if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+				art_affine_multiply (affine, affine, item->xform);
+			} else {
+				affine[4] += item->xform[0];
+				affine[5] += item->xform[1];
+			}
+		}
+
+		item = item->parent;
+	}
+}
+
+/**
+ * gnome_canvas_item_w2i:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from world coordinates to item-relative
+ * coordinates.
+ **/
+void
+gnome_canvas_item_w2i (GnomeCanvasItem *item, double *x, double *y)
+{
+	double affine[6], inv[6];
+	ArtPoint w, i;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (x != NULL);
+	g_return_if_fail (y != NULL);
+
+	gnome_canvas_item_i2w_affine (item, affine);
+	art_affine_invert (inv, affine);
+	w.x = *x;
+	w.y = *y;
+	art_affine_point (&i, &w, inv);
+	*x = i.x;
+	*y = i.y;
+}
+
+
+/**
+ * gnome_canvas_item_i2w:
+ * @item: A canvas item.
+ * @x: X coordinate to convert (input/output value).
+ * @y: Y coordinate to convert (input/output value).
+ *
+ * Converts a coordinate pair from item-relative coordinates to world
+ * coordinates.
+ **/
+void
+gnome_canvas_item_i2w (GnomeCanvasItem *item, double *x, double *y)
+{
+	double affine[6];
+	ArtPoint w, i;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (x != NULL);
+	g_return_if_fail (y != NULL);
+
+	gnome_canvas_item_i2w_affine (item, affine);
+	i.x = *x;
+	i.y = *y;
+	art_affine_point (&w, &i, affine);
+	*x = w.x;
+	*y = w.y;
+}
+
+/**
+ * gnome_canvas_item_i2c_affine:
+ * @item: A canvas item.
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from item-relative coordinates to
+ * canvas pixel coordinates.
+ **/
+void
+gnome_canvas_item_i2c_affine (GnomeCanvasItem *item, double affine[6])
+{
+	double i2w[6], w2c[6];
+
+	gnome_canvas_item_i2w_affine (item, i2w);
+	gnome_canvas_w2c_affine (item->canvas, w2c);
+	art_affine_multiply (affine, i2w, w2c);
+}
+
+/* Returns whether the item is an inferior of or is equal to the parent. */
+static int
+is_descendant (GnomeCanvasItem *item, GnomeCanvasItem *parent)
+{
+	for (; item; item = item->parent)
+		if (item == parent)
+			return TRUE;
+
+	return FALSE;
+}
+
+/**
+ * gnome_canvas_item_reparent:
+ * @item: A canvas item.
+ * @new_group: A canvas group.
+ *
+ * Changes the parent of the specified item to be the new group.  The item keeps
+ * its group-relative coordinates as for its old parent, so the item may change
+ * its absolute position within the canvas.
+ **/
+void
+gnome_canvas_item_reparent (GnomeCanvasItem *item, GnomeCanvasGroup *new_group)
+{
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (GNOME_IS_CANVAS_GROUP (new_group));
+
+	/* Both items need to be in the same canvas */
+	g_return_if_fail (item->canvas == GNOME_CANVAS_ITEM (new_group)->canvas);
+
+	/* The group cannot be an inferior of the item or be the item itself --
+	 * this also takes care of the case where the item is the root item of
+	 * the canvas.  */
+	g_return_if_fail (!is_descendant (GNOME_CANVAS_ITEM (new_group), item));
+
+	/* Everything is ok, now actually reparent the item */
+
+	g_object_ref (G_OBJECT (item)); /* protect it from the unref in group_remove */
+
+	redraw_if_visible (item);
+
+	group_remove (GNOME_CANVAS_GROUP (item->parent), item);
+	item->parent = GNOME_CANVAS_ITEM (new_group);
+	group_add (new_group, item);
+
+	/* Redraw and repick */
+
+	redraw_if_visible (item);
+	item->canvas->need_repick = TRUE;
+
+	g_object_unref (G_OBJECT (item));
+}
+
+/**
+ * gnome_canvas_item_grab_focus:
+ * @item: A canvas item.
+ *
+ * Makes the specified item take the keyboard focus, so all keyboard events will
+ * be sent to it.  If the canvas widget itself did not have the focus, it grabs
+ * it as well.
+ **/
+void
+gnome_canvas_item_grab_focus (GnomeCanvasItem *item)
+{
+	GnomeCanvasItem *focused_item;
+	GdkEvent ev;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+	g_return_if_fail (GTK_WIDGET_CAN_FOCUS (GTK_WIDGET (item->canvas)));
+
+	focused_item = item->canvas->focused_item;
+
+	if (focused_item) {
+		ev.focus_change.type = GDK_FOCUS_CHANGE;
+		ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+		ev.focus_change.send_event = FALSE;
+		ev.focus_change.in = FALSE;
+
+		emit_event (item->canvas, &ev);
+	}
+
+	item->canvas->focused_item = item;
+	gtk_widget_grab_focus (GTK_WIDGET (item->canvas));
+
+	if (focused_item) {
+		ev.focus_change.type = GDK_FOCUS_CHANGE;
+		ev.focus_change.window = GTK_LAYOUT (item->canvas)->bin_window;
+		ev.focus_change.send_event = FALSE;
+		ev.focus_change.in = TRUE;
+
+		emit_event (item->canvas, &ev);
+	}
+}
+
+
+/**
+ * gnome_canvas_item_get_bounds:
+ * @item: A canvas item.
+ * @x1: Leftmost edge of the bounding box (return value).
+ * @y1: Upper edge of the bounding box (return value).
+ * @x2: Rightmost edge of the bounding box (return value).
+ * @y2: Lower edge of the bounding box (return value).
+ *
+ * Queries the bounding box of a canvas item.  The bounds are returned in the
+ * coordinate system of the item's parent.
+ **/
+void
+gnome_canvas_item_get_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	double tx1, ty1, tx2, ty2;
+	ArtPoint p1, p2, p3, p4;
+	ArtPoint q1, q2, q3, q4;
+	double min_x1, min_y1, min_x2, min_y2;
+	double max_x1, max_y1, max_x2, max_y2;
+
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	tx1 = ty1 = tx2 = ty2 = 0.0;
+
+	/* Get the item's bounds in its coordinate system */
+
+	if (GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->bounds) (item, &tx1, &ty1, &tx2, &ty2);
+
+	/* Make the bounds relative to the item's parent coordinate system */
+
+	if (item->xform && (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL)) {
+		p1.x = p2.x = tx1;
+		p1.y = p4.y = ty1;
+		p3.x = p4.x = tx2;
+		p2.y = p3.y = ty2;
+
+		art_affine_point (&q1, &p1, item->xform);
+		art_affine_point (&q2, &p2, item->xform);
+		art_affine_point (&q3, &p3, item->xform);
+		art_affine_point (&q4, &p4, item->xform);
+
+		if (q1.x < q2.x) {
+			min_x1 = q1.x;
+			max_x1 = q2.x;
+		} else {
+			min_x1 = q2.x;
+			max_x1 = q1.x;
+		}
+
+		if (q1.y < q2.y) {
+			min_y1 = q1.y;
+			max_y1 = q2.y;
+		} else {
+			min_y1 = q2.y;
+			max_y1 = q1.y;
+		}
+
+		if (q3.x < q4.x) {
+			min_x2 = q3.x;
+			max_x2 = q4.x;
+		} else {
+			min_x2 = q4.x;
+			max_x2 = q3.x;
+		}
+
+		if (q3.y < q4.y) {
+			min_y2 = q3.y;
+			max_y2 = q4.y;
+		} else {
+			min_y2 = q4.y;
+			max_y2 = q3.y;
+		}
+
+		tx1 = MIN (min_x1, min_x2);
+		ty1 = MIN (min_y1, min_y2);
+		tx2 = MAX (max_x1, max_x2);
+		ty2 = MAX (max_y1, max_y2);
+	} else if (item->xform) {
+		tx1 += item->xform[0];
+		ty1 += item->xform[1];
+		tx2 += item->xform[0];
+		ty2 += item->xform[1];
+	}
+
+	/* Return the values */
+
+	if (x1)
+		*x1 = tx1;
+
+	if (y1)
+		*y1 = ty1;
+
+	if (x2)
+		*x2 = tx2;
+
+	if (y2)
+		*y2 = ty2;
+}
+
+
+/**
+ * gnome_canvas_item_request_update
+ * @item: A canvas item.
+ *
+ * To be used only by item implementations.  Requests that the canvas queue an
+ * update for the specified item.
+ **/
+void
+gnome_canvas_item_request_update (GnomeCanvasItem *item)
+{
+	if (item->object.flags & GNOME_CANVAS_ITEM_NEED_UPDATE)
+		return;
+
+	item->object.flags |= GNOME_CANVAS_ITEM_NEED_UPDATE;
+
+	if (item->parent != NULL) {
+		/* Recurse up the tree */
+		gnome_canvas_item_request_update (item->parent);
+	} else {
+		/* Have reached the top of the tree, make sure the update call gets scheduled. */
+		gnome_canvas_request_update (item->canvas);
+	}
+}
+
+/*** GnomeCanvasGroup ***/
+
+
+enum {
+	GROUP_PROP_0,
+	GROUP_PROP_X,
+	GROUP_PROP_Y
+};
+
+
+static void gnome_canvas_group_class_init  (GnomeCanvasGroupClass *class);
+static void gnome_canvas_group_init        (GnomeCanvasGroup      *group);
+static void gnome_canvas_group_set_property(GObject               *object,
+					    guint                  param_id,
+					    const GValue          *value,
+					    GParamSpec            *pspec);
+static void gnome_canvas_group_get_property(GObject               *object,
+					    guint                  param_id,
+					    GValue                *value,
+					    GParamSpec            *pspec);
+
+static void gnome_canvas_group_destroy     (GtkObject             *object);
+
+static void   gnome_canvas_group_update      (GnomeCanvasItem *item, double *affine,
+					      ArtSVP *clip_path, int flags);
+static void   gnome_canvas_group_realize     (GnomeCanvasItem *item);
+static void   gnome_canvas_group_unrealize   (GnomeCanvasItem *item);
+static void   gnome_canvas_group_map         (GnomeCanvasItem *item);
+static void   gnome_canvas_group_unmap       (GnomeCanvasItem *item);
+static void   gnome_canvas_group_draw        (GnomeCanvasItem *item, GdkDrawable *drawable,
+					      int x, int y, int width, int height);
+static double gnome_canvas_group_point       (GnomeCanvasItem *item, double x, double y,
+					      int cx, int cy,
+					      GnomeCanvasItem **actual_item);
+static void   gnome_canvas_group_bounds      (GnomeCanvasItem *item, double *x1, double *y1,
+					      double *x2, double *y2);
+static void   gnome_canvas_group_render      (GnomeCanvasItem *item,
+					      GnomeCanvasBuf *buf);
+
+
+static GnomeCanvasItemClass *group_parent_class;
+
+
+/**
+ * gnome_canvas_group_get_type:
+ *
+ * Registers the &GnomeCanvasGroup class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &GnomeCanvasGroup class.
+ **/
+GType
+gnome_canvas_group_get_type (void)
+{
+	static GType canvas_group_type;
+
+	if (!canvas_group_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasGroupClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_group_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvasGroup),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_group_init,
+			NULL			/* value_table */
+		};
+
+		canvas_group_type = g_type_register_static (GNOME_TYPE_CANVAS_ITEM, "GnomeCanvasGroup",
+							    &object_info, 0);
+	}
+
+	return canvas_group_type;
+}
+
+/* Class initialization function for GnomeCanvasGroupClass */
+static void
+gnome_canvas_group_class_init (GnomeCanvasGroupClass *class)
+{
+	GObjectClass *gobject_class;
+	GtkObjectClass *object_class;
+	GnomeCanvasItemClass *item_class;
+
+	gobject_class = (GObjectClass *) class;
+	object_class = (GtkObjectClass *) class;
+	item_class = (GnomeCanvasItemClass *) class;
+
+	group_parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_group_set_property;
+	gobject_class->get_property = gnome_canvas_group_get_property;
+
+	g_object_class_install_property
+		(gobject_class, GROUP_PROP_X,
+		 g_param_spec_double ("x",
+				      "X",
+				      "X",
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+	g_object_class_install_property
+		(gobject_class, GROUP_PROP_Y,
+		 g_param_spec_double ("y",
+				      "Y",
+				      "Y",
+				      -G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	object_class->destroy = gnome_canvas_group_destroy;
+
+	item_class->update = gnome_canvas_group_update;
+	item_class->realize = gnome_canvas_group_realize;
+	item_class->unrealize = gnome_canvas_group_unrealize;
+	item_class->map = gnome_canvas_group_map;
+	item_class->unmap = gnome_canvas_group_unmap;
+	item_class->draw = gnome_canvas_group_draw;
+	item_class->render = gnome_canvas_group_render;
+	item_class->point = gnome_canvas_group_point;
+	item_class->bounds = gnome_canvas_group_bounds;
+}
+
+/* Object initialization function for GnomeCanvasGroup */
+static void
+gnome_canvas_group_init (GnomeCanvasGroup *group)
+{
+#if 0
+	group->xpos = 0.0;
+	group->ypos = 0.0;
+#endif
+}
+
+/* Translate handler for canvas groups */
+static double *
+gnome_canvas_ensure_translate (GnomeCanvasItem *item)
+{
+	if (item->xform == NULL) {
+		GTK_OBJECT_UNSET_FLAGS (item, GNOME_CANVAS_ITEM_AFFINE_FULL);
+		item->xform = g_new (double, 2);
+		item->xform[0] = 0.0;
+		item->xform[1] = 0.0;
+		return item->xform;
+	} else if (item->object.flags & GNOME_CANVAS_ITEM_AFFINE_FULL) {
+		return item->xform + 4;
+	} else {
+		return item->xform;
+	}
+}
+
+/* Set_property handler for canvas groups */
+static void
+gnome_canvas_group_set_property (GObject *gobject, guint param_id,
+				 const GValue *value, GParamSpec *pspec)
+{
+	GnomeCanvasItem *item;
+	double *xlat;
+
+	g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
+
+	item = GNOME_CANVAS_ITEM (gobject);
+
+	switch (param_id) {
+	case GROUP_PROP_X:
+		xlat = gnome_canvas_ensure_translate (item);
+		xlat[0] = g_value_get_double (value);
+		break;
+
+	case GROUP_PROP_Y:
+		xlat = gnome_canvas_ensure_translate (item);
+		xlat[1] = g_value_get_double (value);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/* Get_property handler for canvas groups */
+static void
+gnome_canvas_group_get_property (GObject *gobject, guint param_id,
+				 GValue *value, GParamSpec *pspec)
+{
+	GnomeCanvasItem *item;
+
+	g_return_if_fail (GNOME_IS_CANVAS_GROUP (gobject));
+
+	item = GNOME_CANVAS_ITEM (gobject);
+
+	switch (param_id) {
+	case GROUP_PROP_X:
+		if (item->xform == NULL)
+			g_value_set_double (value, 0);
+		else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
+			g_value_set_double (value, item->xform[4]);
+		else
+			g_value_set_double (value, item->xform[0]);
+		break;
+
+	case GROUP_PROP_Y:
+		if (item->xform == NULL)
+			g_value_set_double (value, 0);
+		else if (GTK_OBJECT (gobject)->flags & GNOME_CANVAS_ITEM_AFFINE_FULL)
+			g_value_set_double (value, item->xform[5]);
+		else
+			g_value_set_double (value, item->xform[1]);
+		break;
+
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, param_id, pspec);
+		break;
+	}
+}
+
+/* Destroy handler for canvas groups */
+static void
+gnome_canvas_group_destroy (GtkObject *object)
+{
+	GnomeCanvasGroup *group;
+
+	g_return_if_fail (GNOME_IS_CANVAS_GROUP (object));
+
+	group = GNOME_CANVAS_GROUP (object);
+
+	while (group->item_list) {
+		// child is unref'ed by the child's group_remove().
+		gtk_object_destroy (GTK_OBJECT (group->item_list->data));
+	}
+
+	if (GTK_OBJECT_CLASS (group_parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (group_parent_class)->destroy) (object);
+}
+
+/* Update handler for canvas groups */
+static void
+gnome_canvas_group_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags)
+{
+	GnomeCanvasGroup *group;
+	GList *list;
+	GnomeCanvasItem *i;
+	ArtDRect bbox, child_bbox;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	(* group_parent_class->update) (item, affine, clip_path, flags);
+
+	bbox.x0 = 0;
+	bbox.y0 = 0;
+	bbox.x1 = 0;
+	bbox.y1 = 0;
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		gnome_canvas_item_invoke_update (i, affine, clip_path, flags);
+
+		child_bbox.x0 = i->x1;
+		child_bbox.y0 = i->y1;
+		child_bbox.x1 = i->x2;
+		child_bbox.y1 = i->y2;
+		art_drect_union (&bbox, &bbox, &child_bbox);
+	}
+	item->x1 = bbox.x0;
+	item->y1 = bbox.y0;
+	item->x2 = bbox.x1;
+	item->y2 = bbox.y1;
+}
+
+/* Realize handler for canvas groups */
+static void
+gnome_canvas_group_realize (GnomeCanvasItem *item)
+{
+	GnomeCanvasGroup *group;
+	GList *list;
+	GnomeCanvasItem *i;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (!(i->object.flags & GNOME_CANVAS_ITEM_REALIZED))
+			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->realize) (i);
+	}
+
+	(* group_parent_class->realize) (item);
+}
+
+/* Unrealize handler for canvas groups */
+static void
+gnome_canvas_group_unrealize (GnomeCanvasItem *item)
+{
+	GnomeCanvasGroup *group;
+	GList *list;
+	GnomeCanvasItem *i;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->unrealize) (i);
+	}
+
+	(* group_parent_class->unrealize) (item);
+}
+
+/* Map handler for canvas groups */
+static void
+gnome_canvas_group_map (GnomeCanvasItem *item)
+{
+	GnomeCanvasGroup *group;
+	GList *list;
+	GnomeCanvasItem *i;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (!(i->object.flags & GNOME_CANVAS_ITEM_MAPPED))
+			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->map) (i);
+	}
+
+	(* group_parent_class->map) (item);
+}
+
+/* Unmap handler for canvas groups */
+static void
+gnome_canvas_group_unmap (GnomeCanvasItem *item)
+{
+	GnomeCanvasGroup *group;
+	GList *list;
+	GnomeCanvasItem *i;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		i = list->data;
+
+		if (i->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+			(* GNOME_CANVAS_ITEM_GET_CLASS (i)->unmap) (i);
+	}
+
+	(* group_parent_class->unmap) (item);
+}
+
+/* Draw handler for canvas groups */
+static void
+gnome_canvas_group_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
+			 int x, int y, int width, int height)
+{
+	GnomeCanvasGroup *group;
+	GList *list;
+	GnomeCanvasItem *child = NULL;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+		     && ((child->x1 < (x + width))
+			 && (child->y1 < (y + height))
+			 && (child->x2 > x)
+			 && (child->y2 > y)))
+		    || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
+			&& (child->x1 < child->canvas->redraw_x2)
+			&& (child->y1 < child->canvas->redraw_y2)
+			&& (child->x2 > child->canvas->redraw_x1)
+			&& (child->y2 > child->canvas->redraw_y2)))
+			if (GNOME_CANVAS_ITEM_GET_CLASS (child)->draw)
+				(* GNOME_CANVAS_ITEM_GET_CLASS (child)->draw) (
+					child, drawable, x, y, width, height);
+	}
+}
+
+/* Point handler for canvas groups */
+static double
+gnome_canvas_group_point (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+			  GnomeCanvasItem **actual_item)
+{
+	GnomeCanvasGroup *group;
+	GList *list;
+	GnomeCanvasItem *child, *point_item;
+	int x1, y1, x2, y2;
+	double gx, gy;
+	double dist, best;
+	int has_point;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	x1 = cx - item->canvas->close_enough;
+	y1 = cy - item->canvas->close_enough;
+	x2 = cx + item->canvas->close_enough;
+	y2 = cy + item->canvas->close_enough;
+
+	best = 0.0;
+	*actual_item = NULL;
+
+	gx = x;
+	gy = y;
+
+	dist = 0.0; /* keep gcc happy */
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if ((child->x1 > x2) || (child->y1 > y2) || (child->x2 < x1) || (child->y2 < y1))
+			continue;
+
+		point_item = NULL; /* cater for incomplete item implementations */
+
+		if ((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+		    && GNOME_CANVAS_ITEM_GET_CLASS (child)->point) {
+			dist = gnome_canvas_item_invoke_point (child, gx, gy, cx, cy, &point_item);
+			has_point = TRUE;
+		} else
+			has_point = FALSE;
+
+		if (has_point
+		    && point_item
+		    && ((int) (dist * item->canvas->pixels_per_unit + 0.5)
+			<= item->canvas->close_enough)) {
+			best = dist;
+			*actual_item = point_item;
+		}
+	}
+
+	return best;
+}
+
+/* Bounds handler for canvas groups */
+static void
+gnome_canvas_group_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+	GnomeCanvasGroup *group;
+	GnomeCanvasItem *child;
+	GList *list;
+	double tx1, ty1, tx2, ty2;
+	double minx, miny, maxx, maxy;
+	int set;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	/* Get the bounds of the first visible item */
+
+	child = NULL; /* Unnecessary but eliminates a warning. */
+
+	set = FALSE;
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if (child->object.flags & GNOME_CANVAS_ITEM_VISIBLE) {
+			set = TRUE;
+			gnome_canvas_item_get_bounds (child, &minx, &miny, &maxx, &maxy);
+			break;
+		}
+	}
+
+	/* If there were no visible items, return an empty bounding box */
+
+	if (!set) {
+		*x1 = *y1 = *x2 = *y2 = 0.0;
+		return;
+	}
+
+	/* Now we can grow the bounds using the rest of the items */
+
+	list = list->next;
+
+	for (; list; list = list->next) {
+		child = list->data;
+
+		if (!(child->object.flags & GNOME_CANVAS_ITEM_VISIBLE))
+			continue;
+
+		gnome_canvas_item_get_bounds (child, &tx1, &ty1, &tx2, &ty2);
+
+		if (tx1 < minx)
+			minx = tx1;
+
+		if (ty1 < miny)
+			miny = ty1;
+
+		if (tx2 > maxx)
+			maxx = tx2;
+
+		if (ty2 > maxy)
+			maxy = ty2;
+	}
+
+	*x1 = minx;
+	*y1 = miny;
+	*x2 = maxx;
+	*y2 = maxy;
+}
+
+/* Render handler for canvas groups */
+static void
+gnome_canvas_group_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
+{
+	GnomeCanvasGroup *group;
+	GnomeCanvasItem *child;
+	GList *list;
+
+	group = GNOME_CANVAS_GROUP (item);
+
+	for (list = group->item_list; list; list = list->next) {
+		child = list->data;
+
+		if (((child->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+		     && ((child->x1 < buf->rect.x1)
+			 && (child->y1 < buf->rect.y1)
+			 && (child->x2 > buf->rect.x0)
+			 && (child->y2 > buf->rect.y0)))
+		    || ((GTK_OBJECT_FLAGS (child) & GNOME_CANVAS_ITEM_ALWAYS_REDRAW)
+			&& (child->x1 < child->canvas->redraw_x2)
+			&& (child->y1 < child->canvas->redraw_y2)
+			&& (child->x2 > child->canvas->redraw_x1)
+			&& (child->y2 > child->canvas->redraw_y2)))
+			if (GNOME_CANVAS_ITEM_GET_CLASS (child)->render)
+				(* GNOME_CANVAS_ITEM_GET_CLASS (child)->render) (
+					child, buf);
+	}
+}
+
+/* Adds an item to a group */
+static void
+group_add (GnomeCanvasGroup *group, GnomeCanvasItem *item)
+{
+	g_object_ref (G_OBJECT (item));
+	gtk_object_sink (GTK_OBJECT (item));
+
+	if (!group->item_list) {
+		group->item_list = g_list_append (group->item_list, item);
+		group->item_list_end = group->item_list;
+	} else
+		group->item_list_end = g_list_append (group->item_list_end, item)->next;
+
+	if (group->item.object.flags & GNOME_CANVAS_ITEM_REALIZED)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->realize) (item);
+
+	if (group->item.object.flags & GNOME_CANVAS_ITEM_MAPPED)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (item)->map) (item);
+
+	g_object_notify (G_OBJECT (item), "parent");
+}
+
+/* Removes an item from a group */
+static void
+group_remove (GnomeCanvasGroup *group, GnomeCanvasItem *item)
+{
+	GList *children;
+
+	g_return_if_fail (GNOME_IS_CANVAS_GROUP (group));
+	g_return_if_fail (GNOME_IS_CANVAS_ITEM (item));
+
+	for (children = group->item_list; children; children = children->next)
+		if (children->data == item) {
+			if (item->object.flags & GNOME_CANVAS_ITEM_MAPPED)
+				(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unmap) (item);
+
+			if (item->object.flags & GNOME_CANVAS_ITEM_REALIZED)
+				(* GNOME_CANVAS_ITEM_GET_CLASS (item)->unrealize) (item);
+
+			/* Unparent the child */
+
+			item->parent = NULL;
+			g_object_unref (G_OBJECT (item));
+
+			/* Remove it from the list */
+
+			if (children == group->item_list_end)
+				group->item_list_end = children->prev;
+
+			group->item_list = g_list_remove_link (group->item_list, children);
+			g_list_free (children);
+			break;
+		}
+}
+
+
+/*** GnomeCanvas ***/
+
+
+enum {
+	DRAW_BACKGROUND,
+	RENDER_BACKGROUND,
+	LAST_SIGNAL
+};
+
+static void gnome_canvas_class_init          (GnomeCanvasClass *class);
+static void gnome_canvas_init                (GnomeCanvas      *canvas);
+static void gnome_canvas_destroy             (GtkObject        *object);
+static void gnome_canvas_map                 (GtkWidget        *widget);
+static void gnome_canvas_unmap               (GtkWidget        *widget);
+static void gnome_canvas_realize             (GtkWidget        *widget);
+static void gnome_canvas_unrealize           (GtkWidget        *widget);
+static void gnome_canvas_size_allocate       (GtkWidget        *widget,
+					      GtkAllocation    *allocation);
+static gint gnome_canvas_button              (GtkWidget        *widget,
+					      GdkEventButton   *event);
+static gint gnome_canvas_motion              (GtkWidget        *widget,
+					      GdkEventMotion   *event);
+static gint gnome_canvas_expose              (GtkWidget        *widget,
+					      GdkEventExpose   *event);
+static gboolean gnome_canvas_key             (GtkWidget        *widget,
+					      GdkEventKey      *event);
+static gint gnome_canvas_crossing            (GtkWidget        *widget,
+					      GdkEventCrossing *event);
+static gint gnome_canvas_focus_in            (GtkWidget        *widget,
+					      GdkEventFocus    *event);
+static gint gnome_canvas_focus_out           (GtkWidget        *widget,
+					      GdkEventFocus    *event);
+static void gnome_canvas_request_update_real (GnomeCanvas      *canvas);
+static void gnome_canvas_draw_background     (GnomeCanvas      *canvas,
+					      GdkDrawable      *drawable,
+					      int               x,
+					      int               y,
+					      int               width,
+					      int               height);
+
+
+static GtkLayoutClass *canvas_parent_class;
+
+static guint canvas_signals[LAST_SIGNAL];
+
+enum {
+	PROP_AA = 1
+};
+
+/**
+ * gnome_canvas_get_type:
+ *
+ * Registers the &GnomeCanvas class if necessary, and returns the type ID
+ * associated to it.
+ *
+ * Return value:  The type ID of the &GnomeCanvas class.
+ **/
+GType
+gnome_canvas_get_type (void)
+{
+	static GType canvas_type;
+
+	if (!canvas_type) {
+		static const GTypeInfo object_info = {
+			sizeof (GnomeCanvasClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gnome_canvas_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,			/* class_data */
+			sizeof (GnomeCanvas),
+			0,			/* n_preallocs */
+			(GInstanceInitFunc) gnome_canvas_init,
+			NULL			/* value_table */
+		};
+
+		canvas_type = g_type_register_static (GTK_TYPE_LAYOUT, "GnomeCanvas",
+						      &object_info, 0);
+	}
+
+	return canvas_type;
+}
+
+static void
+gnome_canvas_get_property (GObject    *object,
+			   guint       prop_id,
+			   GValue     *value,
+			   GParamSpec *pspec)
+{
+	switch (prop_id) {
+	case PROP_AA:
+		g_value_set_boolean (value, GNOME_CANVAS (object)->aa);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gnome_canvas_set_property (GObject      *object,
+			   guint         prop_id,
+			   const GValue *value,
+			   GParamSpec   *pspec)
+{
+	switch (prop_id) {
+	case PROP_AA:
+		GNOME_CANVAS (object)->aa = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/* Class initialization function for GnomeCanvasClass */
+static void
+gnome_canvas_class_init (GnomeCanvasClass *klass)
+{
+	GObjectClass   *gobject_class;
+	GtkObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+
+	gobject_class = (GObjectClass *)klass;
+	object_class  = (GtkObjectClass *) klass;
+	widget_class  = (GtkWidgetClass *) klass;
+
+	canvas_parent_class = g_type_class_peek_parent (klass);
+
+	gobject_class->set_property = gnome_canvas_set_property;
+	gobject_class->get_property = gnome_canvas_get_property;
+
+	object_class->destroy = gnome_canvas_destroy;
+
+	widget_class->map = gnome_canvas_map;
+	widget_class->unmap = gnome_canvas_unmap;
+	widget_class->realize = gnome_canvas_realize;
+	widget_class->unrealize = gnome_canvas_unrealize;
+	widget_class->size_allocate = gnome_canvas_size_allocate;
+	widget_class->button_press_event = gnome_canvas_button;
+	widget_class->button_release_event = gnome_canvas_button;
+	widget_class->motion_notify_event = gnome_canvas_motion;
+	widget_class->expose_event = gnome_canvas_expose;
+	widget_class->key_press_event = gnome_canvas_key;
+	widget_class->key_release_event = gnome_canvas_key;
+	widget_class->enter_notify_event = gnome_canvas_crossing;
+	widget_class->leave_notify_event = gnome_canvas_crossing;
+	widget_class->focus_in_event = gnome_canvas_focus_in;
+	widget_class->focus_out_event = gnome_canvas_focus_out;
+
+	klass->draw_background = gnome_canvas_draw_background;
+	klass->render_background = NULL;
+	klass->request_update = gnome_canvas_request_update_real;
+
+	g_object_class_install_property (G_OBJECT_CLASS (object_class),
+					 PROP_AA,
+					 g_param_spec_boolean ("aa",
+							       "Antialiased",
+							       "The antialiasing mode of the canvas.",
+							       FALSE,
+							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	canvas_signals[DRAW_BACKGROUND] =
+		g_signal_new ("draw_background",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeCanvasClass, draw_background),
+			      NULL, NULL,
+			      gnome_canvas_marshal_VOID__OBJECT_INT_INT_INT_INT,
+			      G_TYPE_NONE, 5, GDK_TYPE_DRAWABLE,
+			      G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
+	canvas_signals[RENDER_BACKGROUND] =
+		g_signal_new ("render_background",
+			      G_TYPE_FROM_CLASS (object_class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeCanvasClass, render_background),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__POINTER,
+			      G_TYPE_NONE, 1, G_TYPE_POINTER);
+}
+
+/* Callback used when the root item of a canvas is destroyed.  The user should
+ * never ever do this, so we panic if this happens.
+ */
+static void
+panic_root_destroyed (GtkObject *object, gpointer data)
+{
+	g_error ("Eeeek, root item %p of canvas %p was destroyed!", object, data);
+}
+
+/* Object initialization function for GnomeCanvas */
+static void
+gnome_canvas_init (GnomeCanvas *canvas)
+{
+	GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
+
+	canvas->need_update = FALSE;
+	canvas->need_redraw = FALSE;
+	canvas->redraw_area = NULL;
+	canvas->idle_id = 0;
+
+	canvas->scroll_x1 = 0.0;
+	canvas->scroll_y1 = 0.0;
+	canvas->scroll_x2 = canvas->layout.width;
+	canvas->scroll_y2 = canvas->layout.height;
+
+	canvas->pixels_per_unit = 1.0;
+
+	canvas->pick_event.type = GDK_LEAVE_NOTIFY;
+	canvas->pick_event.crossing.x = 0;
+	canvas->pick_event.crossing.y = 0;
+
+	canvas->dither = GDK_RGB_DITHER_MAX;
+
+	/* This may not be what people want, but it is set to be turned on by
+	 * default to have the same initial behavior as the canvas in GNOME 1.4.
+	 */
+	canvas->center_scroll_region = TRUE;
+
+	gtk_layout_set_hadjustment (GTK_LAYOUT (canvas), NULL);
+	gtk_layout_set_vadjustment (GTK_LAYOUT (canvas), NULL);
+
+	/* Disable the gtk+ double buffering since the canvas uses it's own. */
+	gtk_widget_set_double_buffered (GTK_WIDGET (canvas), FALSE);
+
+	/* Create the root item as a special case */
+
+	canvas->root = GNOME_CANVAS_ITEM (g_object_new (gnome_canvas_group_get_type (), NULL));
+	canvas->root->canvas = canvas;
+
+	g_object_ref (canvas->root);
+	gtk_object_sink (GTK_OBJECT (canvas->root));
+
+	canvas->root_destroy_id = g_signal_connect (canvas->root, "destroy",
+						    G_CALLBACK (panic_root_destroyed),
+						    canvas);
+
+	canvas->need_repick = TRUE;
+}
+
+/* Convenience function to remove the idle handler of a canvas */
+static void
+remove_idle (GnomeCanvas *canvas)
+{
+	if (canvas->idle_id == 0)
+		return;
+
+	gtk_idle_remove (canvas->idle_id);
+	canvas->idle_id = 0;
+}
+
+/* Removes the transient state of the canvas (idle handler, grabs). */
+static void
+shutdown_transients (GnomeCanvas *canvas)
+{
+	/* We turn off the need_redraw flag, since if the canvas is mapped again
+	 * it will request a redraw anyways.  We do not turn off the need_update
+	 * flag, though, because updates are not queued when the canvas remaps
+	 * itself.
+	 */
+	if (canvas->need_redraw) {
+		canvas->need_redraw = FALSE;
+		art_uta_free (canvas->redraw_area);
+		canvas->redraw_area = NULL;
+		canvas->redraw_x1 = 0;
+		canvas->redraw_y1 = 0;
+		canvas->redraw_x2 = 0;
+		canvas->redraw_y2 = 0;
+	}
+
+	if (canvas->grabbed_item) {
+		canvas->grabbed_item = NULL;
+		gdk_pointer_ungrab (GDK_CURRENT_TIME);
+	}
+
+	remove_idle (canvas);
+}
+
+/* Destroy handler for GnomeCanvas */
+static void
+gnome_canvas_destroy (GtkObject *object)
+{
+	GnomeCanvas *canvas;
+
+	g_return_if_fail (GNOME_IS_CANVAS (object));
+
+	/* remember, destroy can be run multiple times! */
+
+	canvas = GNOME_CANVAS (object);
+
+	if (canvas->root_destroy_id) {
+		g_signal_handler_disconnect (canvas->root, canvas->root_destroy_id);
+		canvas->root_destroy_id = 0;
+	}
+	if (canvas->root) {
+		gtk_object_destroy (GTK_OBJECT (canvas->root));
+		g_object_unref (G_OBJECT (canvas->root));
+		canvas->root = NULL;
+	}
+
+	shutdown_transients (canvas);
+
+	if (GTK_OBJECT_CLASS (canvas_parent_class)->destroy)
+		(* GTK_OBJECT_CLASS (canvas_parent_class)->destroy) (object);
+}
+
+/**
+ * gnome_canvas_new:
+ *
+ * Creates a new empty canvas in non-antialiased mode.
+ *
+ * Return value: A newly-created canvas.
+ **/
+GtkWidget *
+gnome_canvas_new (void)
+{
+	return GTK_WIDGET (g_object_new (gnome_canvas_get_type (), NULL));
+}
+
+/**
+ * gnome_canvas_new_aa:
+ *
+ * Creates a new empty canvas in antialiased mode.
+ *
+ * Return value: A newly-created antialiased canvas.
+ **/
+GtkWidget *
+gnome_canvas_new_aa (void)
+{
+	return GTK_WIDGET (g_object_new (GNOME_TYPE_CANVAS,
+					 "aa", TRUE,
+					 NULL));
+}
+
+/* Map handler for the canvas */
+static void
+gnome_canvas_map (GtkWidget *widget)
+{
+	GnomeCanvas *canvas;
+
+	g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+	/* Normal widget mapping stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->map)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->map) (widget);
+
+	canvas = GNOME_CANVAS (widget);
+
+	if (canvas->need_update)
+		add_idle (canvas);
+
+	/* Map items */
+
+	if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->map) (canvas->root);
+}
+
+/* Unmap handler for the canvas */
+static void
+gnome_canvas_unmap (GtkWidget *widget)
+{
+	GnomeCanvas *canvas;
+
+	g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+	canvas = GNOME_CANVAS (widget);
+
+	shutdown_transients (canvas);
+
+	/* Unmap items */
+
+	if (GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap)
+		(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unmap) (canvas->root);
+
+	/* Normal widget unmapping stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->unmap)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->unmap) (widget);
+}
+
+/* Realize handler for the canvas */
+static void
+gnome_canvas_realize (GtkWidget *widget)
+{
+	GnomeCanvas *canvas;
+
+	g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+	/* Normal widget realization stuff */
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->realize)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->realize) (widget);
+
+	canvas = GNOME_CANVAS (widget);
+
+	gdk_window_set_events (canvas->layout.bin_window,
+			       (gdk_window_get_events (canvas->layout.bin_window)
+				 | GDK_EXPOSURE_MASK
+				 | GDK_BUTTON_PRESS_MASK
+				 | GDK_BUTTON_RELEASE_MASK
+				 | GDK_POINTER_MOTION_MASK
+				 | GDK_KEY_PRESS_MASK
+				 | GDK_KEY_RELEASE_MASK
+				 | GDK_ENTER_NOTIFY_MASK
+				 | GDK_LEAVE_NOTIFY_MASK
+				 | GDK_FOCUS_CHANGE_MASK));
+
+	/* Create our own temporary pixmap gc and realize all the items */
+
+	canvas->pixmap_gc = gdk_gc_new (canvas->layout.bin_window);
+
+	(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->realize) (canvas->root);
+}
+
+/* Unrealize handler for the canvas */
+static void
+gnome_canvas_unrealize (GtkWidget *widget)
+{
+	GnomeCanvas *canvas;
+
+	g_return_if_fail (GNOME_IS_CANVAS (widget));
+
+	canvas = GNOME_CANVAS (widget);
+
+	shutdown_transients (canvas);
+
+	/* Unrealize items and parent widget */
+
+	(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->unrealize) (canvas->root);
+
+	gdk_gc_destroy (canvas->pixmap_gc);
+	canvas->pixmap_gc = NULL;
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->unrealize)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->unrealize) (widget);
+}
+
+/* Handles scrolling of the canvas.  Adjusts the scrolling and zooming offset to
+ * keep as much as possible of the canvas scrolling region in view.
+ */
+static void
+scroll_to (GnomeCanvas *canvas, int cx, int cy)
+{
+	int scroll_width, scroll_height;
+	int right_limit, bottom_limit;
+	int old_zoom_xofs, old_zoom_yofs;
+	int changed_x = FALSE, changed_y = FALSE;
+	int canvas_width, canvas_height;
+
+	canvas_width = GTK_WIDGET (canvas)->allocation.width;
+	canvas_height = GTK_WIDGET (canvas)->allocation.height;
+
+	scroll_width = floor ((canvas->scroll_x2 - canvas->scroll_x1) * canvas->pixels_per_unit
+			      + 0.5);
+	scroll_height = floor ((canvas->scroll_y2 - canvas->scroll_y1) * canvas->pixels_per_unit
+			       + 0.5);
+
+	right_limit = scroll_width - canvas_width;
+	bottom_limit = scroll_height - canvas_height;
+
+	old_zoom_xofs = canvas->zoom_xofs;
+	old_zoom_yofs = canvas->zoom_yofs;
+
+	if (right_limit < 0) {
+		cx = 0;
+
+		if (canvas->center_scroll_region) {
+			canvas->zoom_xofs = (canvas_width - scroll_width) / 2;
+			scroll_width = canvas_width;
+		} else
+			canvas->zoom_xofs = 0;
+	} else if (cx < 0) {
+		cx = 0;
+		canvas->zoom_xofs = 0;
+	} else if (cx > right_limit) {
+		cx = right_limit;
+		canvas->zoom_xofs = 0;
+	} else
+		canvas->zoom_xofs = 0;
+
+	if (bottom_limit < 0) {
+		cy = 0;
+
+		if (canvas->center_scroll_region) {
+			canvas->zoom_yofs = (canvas_height - scroll_height) / 2;
+			scroll_height = canvas_height;
+		} else
+			canvas->zoom_yofs = 0;
+	} else if (cy < 0) {
+		cy = 0;
+		canvas->zoom_yofs = 0;
+	} else if (cy > bottom_limit) {
+		cy = bottom_limit;
+		canvas->zoom_yofs = 0;
+	} else
+		canvas->zoom_yofs = 0;
+
+	if ((canvas->zoom_xofs != old_zoom_xofs) || (canvas->zoom_yofs != old_zoom_yofs)) {
+		/* This can only occur, if either canvas size or widget size changes */
+		/* So I think we can request full redraw here */
+		/* The reason is, that coverage UTA will be invalidated by offset change */
+		/* fixme: Strictly this is not correct - we have to remove our own idle (Lauris) */
+		/* More stuff - we have to mark root as needing fresh affine (Lauris) */
+		if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+			canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+			gnome_canvas_request_update (canvas);
+		}
+		gtk_widget_queue_draw (GTK_WIDGET (canvas));
+	}
+
+	if (canvas->layout.hadjustment && ((int) canvas->layout.hadjustment->value) != cx) {
+		canvas->layout.hadjustment->value = cx;
+		changed_x = TRUE;
+	}
+
+	if (canvas->layout.vadjustment && ((int) canvas->layout.vadjustment->value) != cy) {
+		canvas->layout.vadjustment->value = cy;
+		changed_y = TRUE;
+	}
+
+	if ((scroll_width != (int) canvas->layout.width)
+	    || (scroll_height != (int) canvas->layout.height))
+		gtk_layout_set_size (GTK_LAYOUT (canvas), scroll_width, scroll_height);
+
+	/* Signal GtkLayout that it should do a redraw. */
+
+	if (changed_x)
+		g_signal_emit_by_name (canvas->layout.hadjustment, "value_changed");
+
+	if (changed_y)
+		g_signal_emit_by_name (canvas->layout.vadjustment, "value_changed");
+}
+
+/* Size allocation handler for the canvas */
+static void
+gnome_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+	GnomeCanvas *canvas;
+
+	g_return_if_fail (GNOME_IS_CANVAS (widget));
+	g_return_if_fail (allocation != NULL);
+
+	if (GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate)
+		(* GTK_WIDGET_CLASS (canvas_parent_class)->size_allocate) (widget, allocation);
+
+	canvas = GNOME_CANVAS (widget);
+
+	/* Recenter the view, if appropriate */
+
+	canvas->layout.hadjustment->page_size = allocation->width;
+	canvas->layout.hadjustment->page_increment = allocation->width / 2;
+
+	canvas->layout.vadjustment->page_size = allocation->height;
+	canvas->layout.vadjustment->page_increment = allocation->height / 2;
+
+	scroll_to (canvas,
+		   canvas->layout.hadjustment->value,
+		   canvas->layout.vadjustment->value);
+
+	g_signal_emit_by_name (canvas->layout.hadjustment, "changed");
+	g_signal_emit_by_name (canvas->layout.vadjustment, "changed");
+}
+
+/* Emits an event for an item in the canvas, be it the current item, grabbed
+ * item, or focused item, as appropriate.
+ */
+
+static int
+emit_event (GnomeCanvas *canvas, GdkEvent *event)
+{
+	GdkEvent *ev;
+	gint finished;
+	GnomeCanvasItem *item;
+	GnomeCanvasItem *parent;
+	guint mask;
+
+	/* Perform checks for grabbed items */
+
+	if (canvas->grabbed_item &&
+	    !is_descendant (canvas->current_item, canvas->grabbed_item)) {
+		/* I think this warning is annoying and I don't know what it's for
+		 * so I'll disable it for now.
+		 */
+/*                g_warning ("emit_event() returning FALSE!\n");*/
+		return FALSE;
+        }
+
+	if (canvas->grabbed_item) {
+		switch (event->type) {
+		case GDK_ENTER_NOTIFY:
+			mask = GDK_ENTER_NOTIFY_MASK;
+			break;
+
+		case GDK_LEAVE_NOTIFY:
+			mask = GDK_LEAVE_NOTIFY_MASK;
+			break;
+
+		case GDK_MOTION_NOTIFY:
+			mask = GDK_POINTER_MOTION_MASK;
+			break;
+
+		case GDK_BUTTON_PRESS:
+		case GDK_2BUTTON_PRESS:
+		case GDK_3BUTTON_PRESS:
+			mask = GDK_BUTTON_PRESS_MASK;
+			break;
+
+		case GDK_BUTTON_RELEASE:
+			mask = GDK_BUTTON_RELEASE_MASK;
+			break;
+
+		case GDK_KEY_PRESS:
+			mask = GDK_KEY_PRESS_MASK;
+			break;
+
+		case GDK_KEY_RELEASE:
+			mask = GDK_KEY_RELEASE_MASK;
+			break;
+
+		default:
+			mask = 0;
+			break;
+		}
+
+		if (!(mask & canvas->grabbed_event_mask))
+			return FALSE;
+	}
+
+	/* Convert to world coordinates -- we have two cases because of diferent
+	 * offsets of the fields in the event structures.
+	 */
+
+	ev = gdk_event_copy (event);
+
+	switch (ev->type)
+        {
+	case GDK_ENTER_NOTIFY:
+	case GDK_LEAVE_NOTIFY:
+		gnome_canvas_window_to_world (canvas,
+					      ev->crossing.x, ev->crossing.y,
+					      &ev->crossing.x, &ev->crossing.y);
+		break;
+
+	case GDK_MOTION_NOTIFY:
+	case GDK_BUTTON_PRESS:
+	case GDK_2BUTTON_PRESS:
+	case GDK_3BUTTON_PRESS:
+	case GDK_BUTTON_RELEASE:
+                gnome_canvas_window_to_world (canvas,
+                                              ev->motion.x, ev->motion.y,
+                                              &ev->motion.x, &ev->motion.y);
+                break;
+
+	default:
+		break;
+	}
+
+	/* Choose where we send the event */
+
+	item = canvas->current_item;
+
+	if (canvas->focused_item
+	    && ((event->type == GDK_KEY_PRESS) ||
+		(event->type == GDK_KEY_RELEASE) ||
+		(event->type == GDK_FOCUS_CHANGE)))
+		item = canvas->focused_item;
+
+	/* The event is propagated up the hierarchy (for if someone connected to
+	 * a group instead of a leaf event), and emission is stopped if a
+	 * handler returns TRUE, just like for GtkWidget events.
+	 */
+
+	finished = FALSE;
+
+	while (item && !finished) {
+		g_object_ref (G_OBJECT (item));
+
+		g_signal_emit (item, item_signals[ITEM_EVENT], 0,
+			       ev, &finished);
+
+		parent = item->parent;
+		g_object_unref (G_OBJECT (item));
+
+		item = parent;
+	}
+
+	gdk_event_free (ev);
+
+	return finished;
+}
+
+/* Re-picks the current item in the canvas, based on the event's coordinates.
+ * Also emits enter/leave events for items as appropriate.
+ */
+static int
+pick_current_item (GnomeCanvas *canvas, GdkEvent *event)
+{
+	int button_down;
+	double x, y;
+	int cx, cy;
+	int retval;
+
+	retval = FALSE;
+
+	/* If a button is down, we'll perform enter and leave events on the
+	 * current item, but not enter on any other item.  This is more or less
+	 * like X pointer grabbing for canvas items.
+	 */
+	button_down = canvas->state & (GDK_BUTTON1_MASK
+				       | GDK_BUTTON2_MASK
+				       | GDK_BUTTON3_MASK
+				       | GDK_BUTTON4_MASK
+				       | GDK_BUTTON5_MASK);
+	if (!button_down)
+		canvas->left_grabbed_item = FALSE;
+
+	/* Save the event in the canvas.  This is used to synthesize enter and
+	 * leave events in case the current item changes.  It is also used to
+	 * re-pick the current item if the current one gets deleted.  Also,
+	 * synthesize an enter event.
+	 */
+	if (event != &canvas->pick_event) {
+		if ((event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_BUTTON_RELEASE)) {
+			/* these fields have the same offsets in both types of events */
+
+			canvas->pick_event.crossing.type       = GDK_ENTER_NOTIFY;
+			canvas->pick_event.crossing.window     = event->motion.window;
+			canvas->pick_event.crossing.send_event = event->motion.send_event;
+			canvas->pick_event.crossing.subwindow  = NULL;
+			canvas->pick_event.crossing.x          = event->motion.x;
+			canvas->pick_event.crossing.y          = event->motion.y;
+			canvas->pick_event.crossing.mode       = GDK_CROSSING_NORMAL;
+			canvas->pick_event.crossing.detail     = GDK_NOTIFY_NONLINEAR;
+			canvas->pick_event.crossing.focus      = FALSE;
+			canvas->pick_event.crossing.state      = event->motion.state;
+
+			/* these fields don't have the same offsets in both types of events */
+
+			if (event->type == GDK_MOTION_NOTIFY) {
+				canvas->pick_event.crossing.x_root = event->motion.x_root;
+				canvas->pick_event.crossing.y_root = event->motion.y_root;
+			} else {
+				canvas->pick_event.crossing.x_root = event->button.x_root;
+				canvas->pick_event.crossing.y_root = event->button.y_root;
+			}
+		} else
+			canvas->pick_event = *event;
+	}
+
+	/* Don't do anything else if this is a recursive call */
+
+	if (canvas->in_repick)
+		return retval;
+
+	/* LeaveNotify means that there is no current item, so we don't look for one */
+
+	if (canvas->pick_event.type != GDK_LEAVE_NOTIFY) {
+		/* these fields don't have the same offsets in both types of events */
+
+		if (canvas->pick_event.type == GDK_ENTER_NOTIFY) {
+			x = canvas->pick_event.crossing.x - canvas->zoom_xofs;
+			y = canvas->pick_event.crossing.y - canvas->zoom_yofs;
+		} else {
+			x = canvas->pick_event.motion.x - canvas->zoom_xofs;
+			y = canvas->pick_event.motion.y - canvas->zoom_yofs;
+		}
+
+		/* canvas pixel coords */
+
+		cx = (int) (x + 0.5);
+		cy = (int) (y + 0.5);
+
+		/* world coords */
+
+		x = canvas->scroll_x1 + x / canvas->pixels_per_unit;
+		y = canvas->scroll_y1 + y / canvas->pixels_per_unit;
+
+		/* find the closest item */
+
+		if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+			gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy,
+							&canvas->new_current_item);
+		else
+			canvas->new_current_item = NULL;
+	} else
+		canvas->new_current_item = NULL;
+
+	if ((canvas->new_current_item == canvas->current_item) && !canvas->left_grabbed_item)
+		return retval; /* current item did not change */
+
+	/* Synthesize events for old and new current items */
+
+	if ((canvas->new_current_item != canvas->current_item)
+	    && (canvas->current_item != NULL)
+	    && !canvas->left_grabbed_item) {
+		GdkEvent new_event;
+
+		new_event = canvas->pick_event;
+		new_event.type = GDK_LEAVE_NOTIFY;
+
+		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+		new_event.crossing.subwindow = NULL;
+		canvas->in_repick = TRUE;
+		retval = emit_event (canvas, &new_event);
+		canvas->in_repick = FALSE;
+	}
+
+	/* new_current_item may have been set to NULL during the call to emit_event() above */
+
+	if ((canvas->new_current_item != canvas->current_item) && button_down) {
+		canvas->left_grabbed_item = TRUE;
+		return retval;
+	}
+
+	/* Handle the rest of cases */
+
+	canvas->left_grabbed_item = FALSE;
+	canvas->current_item = canvas->new_current_item;
+
+	if (canvas->current_item != NULL) {
+		GdkEvent new_event;
+
+		new_event = canvas->pick_event;
+		new_event.type = GDK_ENTER_NOTIFY;
+		new_event.crossing.detail = GDK_NOTIFY_ANCESTOR;
+		new_event.crossing.subwindow = NULL;
+		retval = emit_event (canvas, &new_event);
+	}
+
+	return retval;
+}
+
+/* Button event handler for the canvas */
+static gint
+gnome_canvas_button (GtkWidget *widget, GdkEventButton *event)
+{
+	GnomeCanvas *canvas;
+	int mask;
+	int retval;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	retval = FALSE;
+
+	canvas = GNOME_CANVAS (widget);
+
+	/*
+	 * dispatch normally regardless of the event's window if an item has
+	 * has a pointer grab in effect
+	 */
+	if (!canvas->grabbed_item && event->window != canvas->layout.bin_window)
+		return retval;
+
+	switch (event->button) {
+	case 1:
+		mask = GDK_BUTTON1_MASK;
+		break;
+	case 2:
+		mask = GDK_BUTTON2_MASK;
+		break;
+	case 3:
+		mask = GDK_BUTTON3_MASK;
+		break;
+	case 4:
+		mask = GDK_BUTTON4_MASK;
+		break;
+	case 5:
+		mask = GDK_BUTTON5_MASK;
+		break;
+	default:
+		mask = 0;
+	}
+
+	switch (event->type) {
+	case GDK_BUTTON_PRESS:
+	case GDK_2BUTTON_PRESS:
+	case GDK_3BUTTON_PRESS:
+		/* Pick the current item as if the button were not pressed, and
+		 * then process the event.
+		 */
+		canvas->state = event->state;
+		pick_current_item (canvas, (GdkEvent *) event);
+		canvas->state ^= mask;
+		retval = emit_event (canvas, (GdkEvent *) event);
+		break;
+
+	case GDK_BUTTON_RELEASE:
+		/* Process the event as if the button were pressed, then repick
+		 * after the button has been released
+		 */
+		canvas->state = event->state;
+		retval = emit_event (canvas, (GdkEvent *) event);
+		event->state ^= mask;
+		canvas->state = event->state;
+		pick_current_item (canvas, (GdkEvent *) event);
+		event->state ^= mask;
+		break;
+
+	default:
+		g_assert_not_reached ();
+	}
+
+	return retval;
+}
+
+/* Motion event handler for the canvas */
+static gint
+gnome_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+	GnomeCanvas *canvas;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	canvas = GNOME_CANVAS (widget);
+
+	if (event->window != canvas->layout.bin_window)
+		return FALSE;
+
+	canvas->state = event->state;
+	pick_current_item (canvas, (GdkEvent *) event);
+	return emit_event (canvas, (GdkEvent *) event);
+}
+
+/* Key event handler for the canvas */
+static gboolean
+gnome_canvas_key (GtkWidget *widget, GdkEventKey *event)
+{
+	GnomeCanvas *canvas;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	canvas = GNOME_CANVAS (widget);
+
+	if (!emit_event (canvas, (GdkEvent *) event)) {
+		GtkWidgetClass *widget_class;
+
+		widget_class = GTK_WIDGET_CLASS (canvas_parent_class);
+
+		if (event->type == GDK_KEY_PRESS) {
+			if (widget_class->key_press_event)
+				return (* widget_class->key_press_event) (widget, event);
+		} else if (event->type == GDK_KEY_RELEASE) {
+			if (widget_class->key_release_event)
+				return (* widget_class->key_release_event) (widget, event);
+		} else
+			g_assert_not_reached ();
+
+		return FALSE;
+	} else
+		return TRUE;
+}
+
+
+/* Crossing event handler for the canvas */
+static gint
+gnome_canvas_crossing (GtkWidget *widget, GdkEventCrossing *event)
+{
+	GnomeCanvas *canvas;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS (widget), FALSE);
+	g_return_val_if_fail (event != NULL, FALSE);
+
+	canvas = GNOME_CANVAS (widget);
+
+	if (event->window != canvas->layout.bin_window)
+		return FALSE;
+
+	canvas->state = event->state;
+	return pick_current_item (canvas, (GdkEvent *) event);
+}
+
+/* Focus in handler for the canvas */
+static gint
+gnome_canvas_focus_in (GtkWidget *widget, GdkEventFocus *event)
+{
+	GnomeCanvas *canvas;
+
+	GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	canvas = GNOME_CANVAS (widget);
+
+	if (canvas->focused_item)
+		return emit_event (canvas, (GdkEvent *) event);
+	else
+		return FALSE;
+}
+
+/* Focus out handler for the canvas */
+static gint
+gnome_canvas_focus_out (GtkWidget *widget, GdkEventFocus *event)
+{
+	GnomeCanvas *canvas;
+
+	GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+	canvas = GNOME_CANVAS (widget);
+
+	if (canvas->focused_item)
+		return emit_event (canvas, (GdkEvent *) event);
+	else
+		return FALSE;
+}
+
+#define IMAGE_WIDTH 512
+#define IMAGE_HEIGHT 512
+
+#define IMAGE_WIDTH_AA 256
+#define IMAGE_HEIGHT_AA 64
+
+static void
+gnome_canvas_paint_rect (GnomeCanvas *canvas, gint x0, gint y0, gint x1, gint y1)
+{
+	GtkWidget *widget;
+	gint draw_x1, draw_y1;
+	gint draw_x2, draw_y2;
+	gint xblock, yblock;
+	guchar *px;
+	GdkPixmap *pixmap;
+
+	g_return_if_fail (!canvas->need_update);
+
+	widget = GTK_WIDGET (canvas);
+
+	draw_x1 = MAX (x0, canvas->layout.hadjustment->value - canvas->zoom_xofs);
+	draw_y1 = MAX (y0, canvas->layout.vadjustment->value - canvas->zoom_yofs);
+	draw_x2 = MIN (draw_x1 + GTK_WIDGET (canvas)->allocation.width, x1);
+	draw_y2 = MIN (draw_y1 + GTK_WIDGET (canvas)->allocation.height, y1);
+
+	/* As we can come from expose, we have to tile here */
+	xblock = (canvas->aa) ? IMAGE_WIDTH_AA : IMAGE_WIDTH;
+	yblock = (canvas->aa) ? IMAGE_HEIGHT_AA : IMAGE_HEIGHT;
+
+	px = NULL;
+	pixmap = NULL;
+
+	for (y0 = draw_y1; y0 < draw_y2; y0 += yblock) {
+		y1 = MIN (y0 + yblock, draw_y2);
+		for (x0 = draw_x1; x0 < draw_x2; x0 += xblock) {
+			x1 = MIN (x0 + xblock, draw_x2);
+
+			canvas->redraw_x1 = x0;
+			canvas->redraw_y1 = y0;
+			canvas->redraw_x2 = x1;
+			canvas->redraw_y2 = y1;
+			canvas->draw_xofs = x0;
+			canvas->draw_yofs = y0;
+
+			if (canvas->aa) {
+				GnomeCanvasBuf buf;
+				GdkColor *color;
+
+				if (!px) px = g_new (guchar, IMAGE_WIDTH_AA * IMAGE_HEIGHT_AA * 3);
+
+				buf.buf = px;
+				buf.buf_rowstride = IMAGE_WIDTH_AA * 3;
+				buf.rect.x0 = x0;
+				buf.rect.y0 = y0;
+				buf.rect.x1 = x1;
+				buf.rect.y1 = y1;
+				color = &widget->style->bg[GTK_STATE_NORMAL];
+				buf.bg_color = (((color->red & 0xff00) << 8) | (color->green & 0xff00) | (color->blue >> 8));
+				buf.is_bg = 1;
+				buf.is_buf = 0;
+
+				g_signal_emit (G_OBJECT (canvas), canvas_signals[RENDER_BACKGROUND], 0, &buf);
+
+				if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+					(* GNOME_CANVAS_ITEM_GET_CLASS (canvas->root)->render) (canvas->root, &buf);
+
+				if (buf.is_bg) {
+					gdk_rgb_gc_set_foreground (canvas->pixmap_gc, buf.bg_color);
+					gdk_draw_rectangle (canvas->layout.bin_window,
+							    canvas->pixmap_gc,
+							    TRUE,
+							    (x0 + canvas->zoom_xofs),
+							    (y0 + canvas->zoom_yofs),
+							    x1 - x0, y1 - y0);
+				} else {
+					gdk_draw_rgb_image_dithalign (canvas->layout.bin_window,
+								      canvas->pixmap_gc,
+								      (x0 + canvas->zoom_xofs),
+								      (y0 + canvas->zoom_yofs),
+								      x1 - x0, y1 - y0,
+								      canvas->dither,
+								      buf.buf,
+								      IMAGE_WIDTH_AA * 3,
+								      x0, y0);
+				}
+			} else {
+				if (!pixmap) pixmap = gdk_pixmap_new (canvas->layout.bin_window, IMAGE_WIDTH, IMAGE_HEIGHT,
+								      gtk_widget_get_visual (widget)->depth);
+
+				g_signal_emit (G_OBJECT (canvas), canvas_signals[DRAW_BACKGROUND], 0, pixmap,
+					       x0, y0, x1 - x0, y1 - y0);
+
+				if (canvas->root->object.flags & GNOME_CANVAS_ITEM_VISIBLE)
+					(* GNOME_CANVAS_ITEM_GET_CLASS (
+						canvas->root)->draw) (
+							canvas->root, pixmap,
+							x0, y0,
+							x1 - x0, y1 - y0);
+				/* Copy the pixmap to the window and clean up */
+
+				gdk_draw_pixmap (canvas->layout.bin_window,
+						 canvas->pixmap_gc,
+						 pixmap,
+						 0, 0,
+						 x0 + canvas->zoom_xofs,
+						 y0 + canvas->zoom_yofs,
+						 x1 - x0, y1 - y0);
+			}
+		}
+	}
+
+	if (px) g_free (px);
+	if (pixmap) gdk_pixmap_unref (pixmap);
+}
+
+/* Expose handler for the canvas */
+static gint
+gnome_canvas_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+	GnomeCanvas *canvas;
+	GdkRectangle *rects;
+	gint n_rects;
+	int i;
+
+	canvas = GNOME_CANVAS (widget);
+
+	if (!GTK_WIDGET_DRAWABLE (widget) || (event->window != canvas->layout.bin_window))
+		return FALSE;
+
+#ifdef VERBOSE
+	g_print ("Expose\n");
+#endif
+
+	gdk_region_get_rectangles (event->region, &rects, &n_rects);
+
+	for (i = 0; i < n_rects; i++) {
+		ArtIRect rect;
+
+		rect.x0 = rects[i].x - canvas->zoom_xofs;
+		rect.y0 = rects[i].y - canvas->zoom_yofs;
+		rect.x1 = rects[i].x + rects[i].width - canvas->zoom_xofs;
+		rect.y1 = rects[i].y + rects[i].height - canvas->zoom_yofs;
+
+		if (canvas->need_update || canvas->need_redraw) {
+			ArtUta *uta;
+			/* Update or drawing is scheduled, so just mark exposed area as dirty */
+			uta = art_uta_from_irect (&rect);
+			gnome_canvas_request_redraw_uta (canvas, uta);
+		} else {
+			/* No pending updates, draw exposed area immediately */
+			gnome_canvas_paint_rect (canvas, rect.x0, rect.y0, rect.x1, rect.y1);
+
+			/* And call expose on parent container class */
+			if (GTK_WIDGET_CLASS (canvas_parent_class)->expose_event)
+				(* GTK_WIDGET_CLASS (canvas_parent_class)->expose_event) (
+					widget, event);
+		}
+	}
+
+	g_free (rects);
+
+	return FALSE;
+}
+
+/* Repaints the areas in the canvas that need it */
+static void
+paint (GnomeCanvas *canvas)
+{
+	ArtIRect *rects;
+	gint n_rects, i;
+
+	if (canvas->aa)
+		rects = art_rect_list_from_uta (canvas->redraw_area,
+						IMAGE_WIDTH_AA, IMAGE_HEIGHT_AA,
+						&n_rects);
+	else
+		rects = art_rect_list_from_uta (canvas->redraw_area,
+						IMAGE_WIDTH, IMAGE_HEIGHT,
+						&n_rects);
+
+	art_uta_free (canvas->redraw_area);
+	canvas->redraw_area = NULL;
+	canvas->need_redraw = FALSE;
+
+	/* Send synthetic expose events */
+	for (i = 0; i < n_rects; i++) {
+		GdkEventExpose ex;
+		gint x0, y0, x1, y1;
+
+		x0 = MAX (canvas->layout.hadjustment->value - canvas->zoom_xofs, rects[i].x0);
+		y0 = MAX (canvas->layout.vadjustment->value - canvas->zoom_yofs, rects[i].y0);
+		x1 = MIN (x0 + GTK_WIDGET (canvas)->allocation.width, rects[i].x1);
+		y1 = MIN (y0 + GTK_WIDGET (canvas)->allocation.height, rects[i].y1);
+
+		if ((x0 < x1) && (y0 < y1)) {
+			/* Here we are - whatever type is canvas, we have to send synthetic expose to layout (Lauris) */
+			ex.type = GDK_EXPOSE;
+			ex.window = canvas->layout.bin_window;
+			ex.send_event = TRUE;
+			ex.area.x = x0 + canvas->zoom_xofs;
+			ex.area.y = y0 + canvas->zoom_yofs;
+			ex.area.width = x1 - x0;
+			ex.area.height = y1 - y0;
+			ex.region = gdk_region_rectangle (&ex.area);
+			ex.count = 0;
+			gtk_widget_send_expose (GTK_WIDGET (canvas), (GdkEvent *) &ex);
+			gdk_region_destroy (ex.region);
+		}
+	}
+
+	art_free (rects);
+
+	canvas->redraw_x1 = 0;
+	canvas->redraw_y1 = 0;
+	canvas->redraw_x2 = 0;
+	canvas->redraw_y2 = 0;
+}
+
+static void
+gnome_canvas_draw_background (GnomeCanvas *canvas, GdkDrawable *drawable,
+			      int x, int y, int width, int height)
+{
+	/* By default, we use the style background. */
+	gdk_gc_set_foreground (canvas->pixmap_gc,
+			       &GTK_WIDGET (canvas)->style->bg[GTK_STATE_NORMAL]);
+	gdk_draw_rectangle (drawable,
+			    canvas->pixmap_gc,
+			    TRUE,
+			    0, 0,
+			    width, height);
+}
+
+static void
+do_update (GnomeCanvas *canvas)
+{
+	/* Cause the update if necessary */
+
+update_again:
+	if (canvas->need_update) {
+		gdouble w2cpx[6];
+
+		/* We start updating root with w2cpx affine */
+		w2cpx[0] = canvas->pixels_per_unit;
+		w2cpx[1] = 0.0;
+		w2cpx[2] = 0.0;
+		w2cpx[3] = canvas->pixels_per_unit;
+		w2cpx[4] = -canvas->scroll_x1 * canvas->pixels_per_unit;
+		w2cpx[5] = -canvas->scroll_y1 * canvas->pixels_per_unit;
+
+		gnome_canvas_item_invoke_update (canvas->root, w2cpx, NULL, 0);
+
+		canvas->need_update = FALSE;
+	}
+
+	/* Pick new current item */
+
+	while (canvas->need_repick) {
+		canvas->need_repick = FALSE;
+		pick_current_item (canvas, &canvas->pick_event);
+	}
+
+	/* it is possible that during picking we emitted an event in which
+	   the user then called some function which then requested update
+	   of something.  Without this we'd be left in a state where
+	   need_update would have been left TRUE and the canvas would have
+	   been left unpainted. */
+	if (canvas->need_update) {
+		goto update_again;
+	}
+
+	/* Paint if able to */
+
+	if (GTK_WIDGET_DRAWABLE (canvas) && canvas->need_redraw)
+		paint (canvas);
+}
+
+/* Idle handler for the canvas.  It deals with pending updates and redraws. */
+static gboolean
+idle_handler (gpointer data)
+{
+	GnomeCanvas *canvas;
+
+	GDK_THREADS_ENTER ();
+
+	canvas = GNOME_CANVAS (data);
+	do_update (canvas);
+
+	/* Reset idle id */
+	canvas->idle_id = 0;
+
+	GDK_THREADS_LEAVE ();
+
+	return FALSE;
+}
+
+/* Convenience function to add an idle handler to a canvas */
+static void
+add_idle (GnomeCanvas *canvas)
+{
+	g_assert (canvas->need_update || canvas->need_redraw);
+
+	if (!canvas->idle_id)
+		canvas->idle_id = g_idle_add_full (CANVAS_IDLE_PRIORITY,
+						   idle_handler,
+						   canvas,
+						   NULL);
+
+/*  	canvas->idle_id = gtk_idle_add (idle_handler, canvas); */
+}
+
+/**
+ * gnome_canvas_root:
+ * @canvas: A canvas.
+ *
+ * Queries the root group of a canvas.
+ *
+ * Return value: The root group of the specified canvas.
+ **/
+GnomeCanvasGroup *
+gnome_canvas_root (GnomeCanvas *canvas)
+{
+	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
+
+	return GNOME_CANVAS_GROUP (canvas->root);
+}
+
+
+/**
+ * gnome_canvas_set_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region.
+ * @y1: Upper limit of the scrolling region.
+ * @x2: Rightmost limit of the scrolling region.
+ * @y2: Lower limit of the scrolling region.
+ *
+ * Sets the scrolling region of a canvas to the specified rectangle.  The canvas
+ * will then be able to scroll only within this region.  The view of the canvas
+ * is adjusted as appropriate to display as much of the new region as possible.
+ **/
+void
+gnome_canvas_set_scroll_region (GnomeCanvas *canvas, double x1, double y1, double x2, double y2)
+{
+	double wxofs, wyofs;
+	int xofs, yofs;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	/*
+	 * Set the new scrolling region.  If possible, do not move the visible contents of the
+	 * canvas.
+	 */
+
+	gnome_canvas_c2w (canvas,
+			  GTK_LAYOUT (canvas)->hadjustment->value + canvas->zoom_xofs,
+			  GTK_LAYOUT (canvas)->vadjustment->value + canvas->zoom_yofs,
+			  /*canvas->zoom_xofs,
+			  canvas->zoom_yofs,*/
+			  &wxofs, &wyofs);
+
+	canvas->scroll_x1 = x1;
+	canvas->scroll_y1 = y1;
+	canvas->scroll_x2 = x2;
+	canvas->scroll_y2 = y2;
+
+	gnome_canvas_w2c (canvas, wxofs, wyofs, &xofs, &yofs);
+
+	scroll_to (canvas, xofs, yofs);
+
+	canvas->need_repick = TRUE;
+#if 0
+	/* todo: should be requesting update */
+	(* GNOME_CANVAS_ITEM_CLASS (canvas->root->object.klass)->update) (
+		canvas->root, NULL, NULL, 0);
+#endif
+}
+
+
+/**
+ * gnome_canvas_get_scroll_region:
+ * @canvas: A canvas.
+ * @x1: Leftmost limit of the scrolling region (return value).
+ * @y1: Upper limit of the scrolling region (return value).
+ * @x2: Rightmost limit of the scrolling region (return value).
+ * @y2: Lower limit of the scrolling region (return value).
+ *
+ * Queries the scrolling region of a canvas.
+ **/
+void
+gnome_canvas_get_scroll_region (GnomeCanvas *canvas, double *x1, double *y1, double *x2, double *y2)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	if (x1)
+		*x1 = canvas->scroll_x1;
+
+	if (y1)
+		*y1 = canvas->scroll_y1;
+
+	if (x2)
+		*x2 = canvas->scroll_x2;
+
+	if (y2)
+		*y2 = canvas->scroll_y2;
+}
+
+/**
+ * gnome_canvas_set_center_scroll_region:
+ * @canvas: A canvas.
+ * @center_scroll_region: Whether to center the scrolling region in the canvas
+ * window when it is smaller than the canvas' allocation.
+ *
+ * When the scrolling region of the canvas is smaller than the canvas window,
+ * e.g.  the allocation of the canvas, it can be either centered on the window
+ * or simply made to be on the upper-left corner on the window.  This function
+ * lets you configure this property.
+ **/
+void
+gnome_canvas_set_center_scroll_region (GnomeCanvas *canvas, gboolean center_scroll_region)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	canvas->center_scroll_region = center_scroll_region != 0;
+
+	scroll_to (canvas,
+		   canvas->layout.hadjustment->value,
+		   canvas->layout.vadjustment->value);
+}
+
+/**
+ * gnome_canvas_get_center_scroll_region:
+ * @canvas: A canvas.
+ *
+ * Returns whether the canvas is set to center the scrolling region in the window
+ * if the former is smaller than the canvas' allocation.
+ *
+ * Return value: Whether the scroll region is being centered in the canvas window.
+ **/
+gboolean
+gnome_canvas_get_center_scroll_region (GnomeCanvas *canvas)
+{
+	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), FALSE);
+
+	return canvas->center_scroll_region ? TRUE : FALSE;
+}
+
+/**
+ * gnome_canvas_set_pixels_per_unit:
+ * @canvas: A canvas.
+ * @n: The number of pixels that correspond to one canvas unit.
+ *
+ * Sets the zooming factor of a canvas by specifying the number of pixels that
+ * correspond to one canvas unit.
+ *
+ * The anchor point for zooming, i.e. the point that stays fixed and all others
+ * zoom inwards or outwards from it, depends on whether the canvas is set to
+ * center the scrolling region or not.  You can control this using the
+ * gnome_canvas_set_center_scroll_region() function.  If the canvas is set to
+ * center the scroll region, then the center of the canvas window is used as the
+ * anchor point for zooming.  Otherwise, the upper-left corner of the canvas
+ * window is used as the anchor point.
+ **/
+void
+gnome_canvas_set_pixels_per_unit (GnomeCanvas *canvas, double n)
+{
+	double ax, ay;
+	int x1, y1;
+	int anchor_x, anchor_y;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+	g_return_if_fail (n > GNOME_CANVAS_EPSILON);
+
+	if (canvas->center_scroll_region) {
+		anchor_x = GTK_WIDGET (canvas)->allocation.width / 2;
+		anchor_y = GTK_WIDGET (canvas)->allocation.height / 2;
+	} else
+		anchor_x = anchor_y = 0;
+
+	/* Find the coordinates of the anchor point in units. */
+	if(canvas->layout.hadjustment) {
+		ax = (canvas->layout.hadjustment->value + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+	} else {
+		ax = (0.0                               + anchor_x) / canvas->pixels_per_unit + canvas->scroll_x1 + canvas->zoom_xofs;
+	}
+	if(canvas->layout.hadjustment) {
+		ay = (canvas->layout.vadjustment->value + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+	} else {
+		ay = (0.0                               + anchor_y) / canvas->pixels_per_unit + canvas->scroll_y1 + canvas->zoom_yofs;
+	}
+
+	/* Now calculate the new offset of the upper left corner. */
+	x1 = ((ax - canvas->scroll_x1) * n) - anchor_x;
+	y1 = ((ay - canvas->scroll_y1) * n) - anchor_y;
+
+	canvas->pixels_per_unit = n;
+
+	scroll_to (canvas, x1, y1);
+
+	if (!(canvas->root->object.flags & GNOME_CANVAS_ITEM_NEED_AFFINE)) {
+		canvas->root->object.flags |= GNOME_CANVAS_ITEM_NEED_AFFINE;
+		gnome_canvas_request_update (canvas);
+	}
+
+	canvas->need_repick = TRUE;
+}
+
+/**
+ * gnome_canvas_scroll_to:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset in canvas pixel units.
+ * @cy: Vertical scrolling offset in canvas pixel units.
+ *
+ * Makes a canvas scroll to the specified offsets, given in canvas pixel units.
+ * The canvas will adjust the view so that it is not outside the scrolling
+ * region.  This function is typically not used, as it is better to hook
+ * scrollbars to the canvas layout's scrolling adjusments.
+ **/
+void
+gnome_canvas_scroll_to (GnomeCanvas *canvas, int cx, int cy)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	scroll_to (canvas, cx, cy);
+}
+
+/**
+ * gnome_canvas_get_scroll_offsets:
+ * @canvas: A canvas.
+ * @cx: Horizontal scrolling offset (return value).
+ * @cy: Vertical scrolling offset (return value).
+ *
+ * Queries the scrolling offsets of a canvas.  The values are returned in canvas
+ * pixel units.
+ **/
+void
+gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas, int *cx, int *cy)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	if (cx)
+		*cx = canvas->layout.hadjustment->value;
+
+	if (cy)
+		*cy = canvas->layout.vadjustment->value;
+}
+
+/**
+ * gnome_canvas_update_now:
+ * @canvas: A canvas.
+ *
+ * Forces an immediate update and redraw of a canvas.  If the canvas does not
+ * have any pending update or redraw requests, then no action is taken.  This is
+ * typically only used by applications that need explicit control of when the
+ * display is updated, like games.  It is not needed by normal applications.
+ */
+void
+gnome_canvas_update_now (GnomeCanvas *canvas)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	if (!(canvas->need_update || canvas->need_redraw)) {
+		g_assert (canvas->idle_id == 0);
+		g_assert (canvas->redraw_area == NULL);
+		return;
+	}
+
+	remove_idle (canvas);
+	do_update (canvas);
+}
+
+/**
+ * gnome_canvas_get_item_at:
+ * @canvas: A canvas.
+ * @x: X position in world coordinates.
+ * @y: Y position in world coordinates.
+ *
+ * Looks for the item that is under the specified position, which must be
+ * specified in world coordinates.
+ *
+ * Return value: The sought item, or NULL if no item is at the specified
+ * coordinates.
+ **/
+GnomeCanvasItem *
+gnome_canvas_get_item_at (GnomeCanvas *canvas, double x, double y)
+{
+	GnomeCanvasItem *item;
+	double dist;
+	int cx, cy;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), NULL);
+
+	gnome_canvas_w2c (canvas, x, y, &cx, &cy);
+
+	dist = gnome_canvas_item_invoke_point (canvas->root, x, y, cx, cy, &item);
+	if ((int) (dist * canvas->pixels_per_unit + 0.5) <= canvas->close_enough)
+		return item;
+	else
+		return NULL;
+}
+
+/* Queues an update of the canvas */
+static void
+gnome_canvas_request_update (GnomeCanvas *canvas)
+{
+	GNOME_CANVAS_GET_CLASS (canvas)->request_update (canvas);
+}
+
+static void
+gnome_canvas_request_update_real (GnomeCanvas *canvas)
+{
+	if (canvas->need_update)
+		return;
+
+	canvas->need_update = TRUE;
+	if (GTK_WIDGET_MAPPED ((GtkWidget *) canvas))
+		add_idle (canvas);
+}
+
+/* Computes the union of two microtile arrays while clipping the result to the
+ * specified rectangle.  Any of the specified utas can be NULL, in which case it
+ * is taken to be an empty region.
+ */
+static ArtUta *
+uta_union_clip (ArtUta *uta1, ArtUta *uta2, ArtIRect *clip)
+{
+	ArtUta *uta;
+	ArtUtaBbox *utiles;
+	int clip_x1, clip_y1, clip_x2, clip_y2;
+	int union_x1, union_y1, union_x2, union_y2;
+	int new_x1, new_y1, new_x2, new_y2;
+	int x, y;
+	int ofs, ofs1, ofs2;
+
+	g_assert (clip != NULL);
+
+	/* Compute the tile indices for the clipping rectangle */
+
+	clip_x1 = clip->x0 >> ART_UTILE_SHIFT;
+	clip_y1 = clip->y0 >> ART_UTILE_SHIFT;
+	clip_x2 = (clip->x1 >> ART_UTILE_SHIFT) + 1;
+	clip_y2 = (clip->y1 >> ART_UTILE_SHIFT) + 1;
+
+	/* Get the union of the bounds of both utas */
+
+	if (!uta1) {
+		if (!uta2)
+			return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
+
+		union_x1 = uta2->x0;
+		union_y1 = uta2->y0;
+		union_x2 = uta2->x0 + uta2->width;
+		union_y2 = uta2->y0 + uta2->height;
+	} else {
+		if (!uta2) {
+			union_x1 = uta1->x0;
+			union_y1 = uta1->y0;
+			union_x2 = uta1->x0 + uta1->width;
+			union_y2 = uta1->y0 + uta1->height;
+		} else {
+			union_x1 = MIN (uta1->x0, uta2->x0);
+			union_y1 = MIN (uta1->y0, uta2->y0);
+			union_x2 = MAX (uta1->x0 + uta1->width, uta2->x0 + uta2->width);
+			union_y2 = MAX (uta1->y0 + uta1->height, uta2->y0 + uta2->height);
+		}
+	}
+
+	/* Clip the union of the bounds */
+
+	new_x1 = MAX (clip_x1, union_x1);
+	new_y1 = MAX (clip_y1, union_y1);
+	new_x2 = MIN (clip_x2, union_x2);
+	new_y2 = MIN (clip_y2, union_y2);
+
+	if (new_x1 >= new_x2 || new_y1 >= new_y2)
+		return art_uta_new (clip_x1, clip_y1, clip_x1 + 1, clip_y1 + 1);
+
+	/* Make the new clipped union */
+
+	uta = art_new (ArtUta, 1);
+	uta->x0 = new_x1;
+	uta->y0 = new_y1;
+	uta->width = new_x2 - new_x1;
+	uta->height = new_y2 - new_y1;
+	uta->utiles = utiles = art_new (ArtUtaBbox, uta->width * uta->height);
+
+	ofs = 0;
+	ofs1 = ofs2 = 0;
+
+	for (y = new_y1; y < new_y2; y++) {
+		if (uta1)
+			ofs1 = (y - uta1->y0) * uta1->width + new_x1 - uta1->x0;
+
+		if (uta2)
+			ofs2 = (y - uta2->y0) * uta2->width + new_x1 - uta2->x0;
+
+		for (x = new_x1; x < new_x2; x++) {
+			ArtUtaBbox bb1, bb2, bb;
+
+			if (!uta1
+			    || x < uta1->x0 || y < uta1->y0
+			    || x >= uta1->x0 + uta1->width || y >= uta1->y0 + uta1->height)
+				bb1 = 0;
+			else
+				bb1 = uta1->utiles[ofs1];
+
+			if (!uta2
+			    || x < uta2->x0 || y < uta2->y0
+			    || x >= uta2->x0 + uta2->width || y >= uta2->y0 + uta2->height)
+				bb2 = 0;
+			else
+				bb2 = uta2->utiles[ofs2];
+
+			if (bb1 == 0)
+				bb = bb2;
+			else if (bb2 == 0)
+				bb = bb1;
+			else
+				bb = ART_UTA_BBOX_CONS (MIN (ART_UTA_BBOX_X0 (bb1),
+							     ART_UTA_BBOX_X0 (bb2)),
+							MIN (ART_UTA_BBOX_Y0 (bb1),
+							     ART_UTA_BBOX_Y0 (bb2)),
+							MAX (ART_UTA_BBOX_X1 (bb1),
+							     ART_UTA_BBOX_X1 (bb2)),
+							MAX (ART_UTA_BBOX_Y1 (bb1),
+							     ART_UTA_BBOX_Y1 (bb2)));
+
+			utiles[ofs] = bb;
+
+			ofs++;
+			ofs1++;
+			ofs2++;
+		}
+	}
+
+	return uta;
+}
+
+static inline void
+get_visible_region (GnomeCanvas *canvas, ArtIRect *visible)
+{
+	visible->x0 = canvas->layout.hadjustment->value - canvas->zoom_xofs;
+	visible->y0 = canvas->layout.vadjustment->value - canvas->zoom_yofs;
+	visible->x1 = visible->x0 + GTK_WIDGET (canvas)->allocation.width;
+	visible->y1 = visible->y0 + GTK_WIDGET (canvas)->allocation.height;
+}
+
+/**
+ * gnome_canvas_request_redraw_uta:
+ * @canvas: A canvas.
+ * @uta: Microtile array that specifies the area to be redrawn.  It will
+ * be freed by this function, so the argument you pass will be invalid
+ * after you call this function.
+ *
+ * Informs a canvas that the specified area, given as a microtile array, needs
+ * to be repainted.  To be used only by item implementations.
+ **/
+void
+gnome_canvas_request_redraw_uta (GnomeCanvas *canvas,
+                                 ArtUta      *uta)
+{
+	ArtIRect visible;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+	g_return_if_fail (uta != NULL);
+
+	if (!GTK_WIDGET_DRAWABLE (canvas)) {
+		art_uta_free (uta);
+		return;
+	}
+
+	get_visible_region (canvas, &visible);
+
+	if (canvas->need_redraw) {
+		ArtUta *new_uta;
+
+		g_assert (canvas->redraw_area != NULL);
+		/* ALEX: This can fail if e.g. redraw_uta is called by an item
+		   update function and we're called from update_now -> do_update
+		   because update_now sets idle_id == 0. There is also some way
+		   to get it from the expose handler (see bug #102811).
+		   g_assert (canvas->idle_id != 0);  */
+
+		new_uta = uta_union_clip (canvas->redraw_area, uta, &visible);
+		art_uta_free (canvas->redraw_area);
+		art_uta_free (uta);
+		canvas->redraw_area = new_uta;
+		if (canvas->idle_id == 0)
+			add_idle (canvas);
+	} else {
+		ArtUta *new_uta;
+
+		g_assert (canvas->redraw_area == NULL);
+
+		new_uta = uta_union_clip (uta, NULL, &visible);
+		art_uta_free (uta);
+		canvas->redraw_area = new_uta;
+
+		canvas->need_redraw = TRUE;
+		add_idle (canvas);
+	}
+}
+
+
+/**
+ * gnome_canvas_request_redraw:
+ * @canvas: A canvas.
+ * @x1: Leftmost coordinate of the rectangle to be redrawn.
+ * @y1: Upper coordinate of the rectangle to be redrawn.
+ * @x2: Rightmost coordinate of the rectangle to be redrawn, plus 1.
+ * @y2: Lower coordinate of the rectangle to be redrawn, plus 1.
+ *
+ * Convenience function that informs a canvas that the specified rectangle needs
+ * to be repainted.  This function converts the rectangle to a microtile array
+ * and feeds it to gnome_canvas_request_redraw_uta().  The rectangle includes
+ * @x1 and @y1, but not @x2 and @y2.  To be used only by item implementations.
+ **/
+void
+gnome_canvas_request_redraw (GnomeCanvas *canvas, int x1, int y1, int x2, int y2)
+{
+	ArtUta *uta;
+	ArtIRect bbox;
+	ArtIRect visible;
+	ArtIRect clip;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	if (!GTK_WIDGET_DRAWABLE (canvas) || (x1 >= x2) || (y1 >= y2))
+		return;
+
+	bbox.x0 = x1;
+	bbox.y0 = y1;
+	bbox.x1 = x2;
+	bbox.y1 = y2;
+
+	get_visible_region (canvas, &visible);
+
+	art_irect_intersect (&clip, &bbox, &visible);
+
+	if (!art_irect_empty (&clip)) {
+		uta = art_uta_from_irect (&clip);
+		gnome_canvas_request_redraw_uta (canvas, uta);
+	}
+}
+
+
+/**
+ * gnome_canvas_w2c_affine:
+ * @canvas: A canvas.
+ * @affine: An affine transformation matrix (return value).
+ *
+ * Gets the affine transform that converts from world coordinates to canvas
+ * pixel coordinates.
+ **/
+void
+gnome_canvas_w2c_affine (GnomeCanvas *canvas, double affine[6])
+{
+	double zooom;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+	g_return_if_fail (affine != NULL);
+
+	zooom = canvas->pixels_per_unit;
+
+	affine[0] = zooom;
+	affine[1] = 0;
+	affine[2] = 0;
+	affine[3] = zooom;
+	affine[4] = -canvas->scroll_x1 * zooom;
+	affine[5] = -canvas->scroll_y1 * zooom;
+}
+
+/**
+ * gnome_canvas_w2c:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.
+ **/
+void
+gnome_canvas_w2c (GnomeCanvas *canvas, double wx, double wy, int *cx, int *cy)
+{
+	double affine[6];
+	ArtPoint w, c;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	gnome_canvas_w2c_affine (canvas, affine);
+	w.x = wx;
+	w.y = wy;
+	art_affine_point (&c, &w, affine);
+	if (cx)
+		*cx = floor (c.x + 0.5);
+	if (cy)
+		*cy = floor (c.y + 0.5);
+}
+
+/**
+ * gnome_canvas_w2c_d:
+ * @canvas: A canvas.
+ * @wx: World X coordinate.
+ * @wy: World Y coordinate.
+ * @cx: X pixel coordinate (return value).
+ * @cy: Y pixel coordinate (return value).
+ *
+ * Converts world coordinates into canvas pixel coordinates.  This
+ * version returns coordinates in floating point coordinates, for
+ * greater precision.
+ **/
+void
+gnome_canvas_w2c_d (GnomeCanvas *canvas, double wx, double wy, double *cx, double *cy)
+{
+	double affine[6];
+	ArtPoint w, c;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	gnome_canvas_w2c_affine (canvas, affine);
+	w.x = wx;
+	w.y = wy;
+	art_affine_point (&c, &w, affine);
+	if (cx)
+		*cx = c.x;
+	if (cy)
+		*cy = c.y;
+}
+
+
+/**
+ * gnome_canvas_c2w:
+ * @canvas: A canvas.
+ * @cx: Canvas pixel X coordinate.
+ * @cy: Canvas pixel Y coordinate.
+ * @wx: X world coordinate (return value).
+ * @wy: Y world coordinate (return value).
+ *
+ * Converts canvas pixel coordinates to world coordinates.
+ **/
+void
+gnome_canvas_c2w (GnomeCanvas *canvas, int cx, int cy, double *wx, double *wy)
+{
+	double affine[6], inv[6];
+	ArtPoint w, c;
+
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	gnome_canvas_w2c_affine (canvas, affine);
+	art_affine_invert (inv, affine);
+	c.x = cx;
+	c.y = cy;
+	art_affine_point (&w, &c, inv);
+	if (wx)
+		*wx = w.x;
+	if (wy)
+		*wy = w.y;
+}
+
+
+/**
+ * gnome_canvas_window_to_world:
+ * @canvas: A canvas.
+ * @winx: Window-relative X coordinate.
+ * @winy: Window-relative Y coordinate.
+ * @worldx: X world coordinate (return value).
+ * @worldy: Y world coordinate (return value).
+ *
+ * Converts window-relative coordinates into world coordinates.  You can use
+ * this when you need to convert mouse coordinates into world coordinates, for
+ * example.
+ **/
+void
+gnome_canvas_window_to_world (GnomeCanvas *canvas, double winx, double winy,
+			      double *worldx, double *worldy)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	if (worldx)
+		*worldx = canvas->scroll_x1 + ((winx - canvas->zoom_xofs)
+					       / canvas->pixels_per_unit);
+
+	if (worldy)
+		*worldy = canvas->scroll_y1 + ((winy - canvas->zoom_yofs)
+					       / canvas->pixels_per_unit);
+}
+
+
+/**
+ * gnome_canvas_world_to_window:
+ * @canvas: A canvas.
+ * @worldx: World X coordinate.
+ * @worldy: World Y coordinate.
+ * @winx: X window-relative coordinate.
+ * @winy: Y window-relative coordinate.
+ *
+ * Converts world coordinates into window-relative coordinates.
+ **/
+void
+gnome_canvas_world_to_window (GnomeCanvas *canvas, double worldx, double worldy,
+			      double *winx, double *winy)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	if (winx)
+		*winx = (canvas->pixels_per_unit)*(worldx - canvas->scroll_x1) + canvas->zoom_xofs;
+
+	if (winy)
+		*winy = (canvas->pixels_per_unit)*(worldy - canvas->scroll_y1) + canvas->zoom_yofs;
+}
+
+
+
+/**
+ * gnome_canvas_get_color:
+ * @canvas: A canvas.
+ * @spec: X color specification, or NULL for "transparent".
+ * @color: Returns the allocated color.
+ *
+ * Allocates a color based on the specified X color specification.  As a
+ * convenience to item implementations, it returns TRUE if the color was
+ * allocated, or FALSE if the specification was NULL.  A NULL color
+ * specification is considered as "transparent" by the canvas.
+ *
+ * Return value: TRUE if @spec is non-NULL and the color is allocated.  If @spec
+ * is NULL, then returns FALSE.
+ **/
+int
+gnome_canvas_get_color (GnomeCanvas *canvas, const char *spec, GdkColor *color)
+{
+	GdkColormap *colormap;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), FALSE);
+	g_return_val_if_fail (color != NULL, FALSE);
+
+	if (!spec) {
+		color->pixel = 0;
+		color->red = 0;
+		color->green = 0;
+		color->blue = 0;
+		return FALSE;
+	}
+
+	gdk_color_parse (spec, color);
+
+	colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+	gdk_rgb_find_color (colormap, color);
+
+	return TRUE;
+}
+
+/**
+ * gnome_canvas_get_color_pixel:
+ * @canvas: A canvas.
+ * @rgba: RGBA color specification.
+ *
+ * Allocates a color from the RGBA value passed into this function.  The alpha
+ * opacity value is discarded, since normal X colors do not support it.
+ *
+ * Return value: Allocated pixel value corresponding to the specified color.
+ **/
+gulong
+gnome_canvas_get_color_pixel (GnomeCanvas *canvas, guint rgba)
+{
+	GdkColormap *colormap;
+	GdkColor color;
+
+	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), 0);
+
+	color.red = ((rgba & 0xff000000) >> 16) + ((rgba & 0xff000000) >> 24);
+	color.green = ((rgba & 0x00ff0000) >> 8) + ((rgba & 0x00ff0000) >> 16);
+	color.blue = (rgba & 0x0000ff00) + ((rgba & 0x0000ff00) >> 8);
+	color.pixel = 0;
+
+	colormap = gtk_widget_get_colormap (GTK_WIDGET (canvas));
+
+	gdk_rgb_find_color (colormap, &color);
+
+	return color.pixel;
+}
+
+
+/**
+ * gnome_canvas_set_stipple_origin:
+ * @canvas: A canvas.
+ * @gc: GC on which to set the stipple origin.
+ *
+ * Sets the stipple origin of the specified GC as is appropriate for the canvas,
+ * so that it will be aligned with other stipple patterns used by canvas items.
+ * This is typically only needed by item implementations.
+ **/
+void
+gnome_canvas_set_stipple_origin (GnomeCanvas *canvas, GdkGC *gc)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+	g_return_if_fail (GDK_IS_GC (gc));
+
+	gdk_gc_set_ts_origin (gc, -canvas->draw_xofs, -canvas->draw_yofs);
+}
+
+/**
+ * gnome_canvas_set_dither:
+ * @canvas: A canvas.
+ * @dither: Type of dithering used to render an antialiased canvas.
+ *
+ * Controls dithered rendering for antialiased canvases. The value of
+ * dither should be #GDK_RGB_DITHER_NONE, #GDK_RGB_DITHER_NORMAL, or
+ * #GDK_RGB_DITHER_MAX. The default canvas setting is
+ * #GDK_RGB_DITHER_NORMAL.
+ **/
+void
+gnome_canvas_set_dither (GnomeCanvas *canvas, GdkRgbDither dither)
+{
+	g_return_if_fail (GNOME_IS_CANVAS (canvas));
+
+	canvas->dither = dither;
+}
+
+/**
+ * gnome_canvas_get_dither:
+ * @canvas: A canvas.
+ *
+ * Returns the type of dithering used to render an antialiased canvas.
+ *
+ * Return value: The dither setting.
+ **/
+GdkRgbDither
+gnome_canvas_get_dither (GnomeCanvas *canvas)
+{
+	g_return_val_if_fail (GNOME_IS_CANVAS (canvas), GDK_RGB_DITHER_NONE);
+
+	return canvas->dither;
+}
+
+static gboolean
+boolean_handled_accumulator (GSignalInvocationHint *ihint,
+			     GValue                *return_accu,
+			     const GValue          *handler_return,
+			     gpointer               dummy)
+{
+	gboolean continue_emission;
+	gboolean signal_handled;
+
+	signal_handled = g_value_get_boolean (handler_return);
+	g_value_set_boolean (return_accu, signal_handled);
+	continue_emission = !signal_handled;
+
+	return continue_emission;
+}
+
+/* Class initialization function for GnomeCanvasItemClass */
+static void
+gnome_canvas_item_class_init (GnomeCanvasItemClass *class)
+{
+	GObjectClass *gobject_class;
+
+	gobject_class = (GObjectClass *) class;
+
+	item_parent_class = g_type_class_peek_parent (class);
+
+	gobject_class->set_property = gnome_canvas_item_set_property;
+	gobject_class->get_property = gnome_canvas_item_get_property;
+
+	g_object_class_install_property
+		(gobject_class, ITEM_PROP_PARENT,
+		 g_param_spec_object ("parent", NULL, NULL,
+				      GNOME_TYPE_CANVAS_ITEM,
+				      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
+
+	item_signals[ITEM_EVENT] =
+		g_signal_new ("event",
+			      G_TYPE_FROM_CLASS (class),
+			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GnomeCanvasItemClass, event),
+			      boolean_handled_accumulator, NULL,
+			      gnome_canvas_marshal_BOOLEAN__BOXED,
+			      G_TYPE_BOOLEAN, 1,
+			      GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+	gobject_class->dispose = gnome_canvas_item_dispose;
+
+	class->realize = gnome_canvas_item_realize;
+	class->unrealize = gnome_canvas_item_unrealize;
+	class->map = gnome_canvas_item_map;
+	class->unmap = gnome_canvas_item_unmap;
+	class->update = gnome_canvas_item_update;
+}
diff --git a/src/libgnomecanvas/gnome-canvas.h b/src/libgnomecanvas/gnome-canvas.h
new file mode 100644
index 0000000..d4f538a
--- /dev/null
+++ b/src/libgnomecanvas/gnome-canvas.h
@@ -0,0 +1,635 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+/* GnomeCanvas widget - Tk-like canvas widget for Gnome
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas
+ * widget.  Tk is copyrighted by the Regents of the University of California,
+ * Sun Microsystems, and other parties.
+ *
+ *
+ * Authors: Federico Mena <federico nuclecu unam mx>
+ *          Raph Levien <raph gimp org>
+ */
+
+#ifndef GNOME_CANVAS_H
+#define GNOME_CANVAS_H
+
+#include <gtk/gtklayout.h>
+#include <stdarg.h>
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_rect.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_affine.h>
+
+G_BEGIN_DECLS
+
+
+/* "Small" value used by canvas stuff */
+#define GNOME_CANVAS_EPSILON 1e-10
+
+
+/* Macros for building colors that fit in a 32-bit integer.  The values are in
+ * [0, 255].
+ */
+
+#define GNOME_CANVAS_COLOR(r, g, b) ((((unsigned int) (r) & 0xff) << 24)	\
+				     | (((unsigned int) (g) & 0xff) << 16)	\
+				     | (((unsigned int) (b) & 0xff) << 8)	\
+				     | 0xff)
+
+#define GNOME_CANVAS_COLOR_A(r, g, b, a) ((((unsigned int) (r) & 0xff) << 24)	\
+					  | (((unsigned int) (g) & 0xff) << 16)	\
+					  | (((unsigned int) (b) & 0xff) << 8)	\
+					  | ((unsigned int) (a) & 0xff))
+
+
+typedef struct _GnomeCanvas           GnomeCanvas;
+typedef struct _GnomeCanvasClass      GnomeCanvasClass;
+typedef struct _GnomeCanvasItem       GnomeCanvasItem;
+typedef struct _GnomeCanvasItemClass  GnomeCanvasItemClass;
+typedef struct _GnomeCanvasGroup      GnomeCanvasGroup;
+typedef struct _GnomeCanvasGroupClass GnomeCanvasGroupClass;
+
+
+/* GnomeCanvasItem - base item class for canvas items
+ *
+ * All canvas items are derived from GnomeCanvasItem.  The only information a
+ * GnomeCanvasItem contains is its parent canvas, its parent canvas item group,
+ * its bounding box in world coordinates, and its current affine transformation.
+ *
+ * Items inside a canvas are organized in a tree of GnomeCanvasItemGroup nodes
+ * and GnomeCanvasItem leaves.  Each canvas has a single root group, which can
+ * be obtained with the gnome_canvas_get_root() function.
+ *
+ * The abstract GnomeCanvasItem class does not have any configurable or
+ * queryable attributes.
+ */
+
+/* Object flags for items */
+enum {
+	GNOME_CANVAS_ITEM_REALIZED      = 1 << 4,
+	GNOME_CANVAS_ITEM_MAPPED        = 1 << 5,
+	GNOME_CANVAS_ITEM_ALWAYS_REDRAW = 1 << 6,
+	GNOME_CANVAS_ITEM_VISIBLE       = 1 << 7,
+	GNOME_CANVAS_ITEM_NEED_UPDATE	= 1 << 8,
+	GNOME_CANVAS_ITEM_NEED_AFFINE	= 1 << 9,
+	GNOME_CANVAS_ITEM_NEED_CLIP	= 1 << 10,
+	GNOME_CANVAS_ITEM_NEED_VIS	= 1 << 11,
+	GNOME_CANVAS_ITEM_AFFINE_FULL	= 1 << 12
+};
+
+/* Update flags for items */
+enum {
+	GNOME_CANVAS_UPDATE_REQUESTED  = 1 << 0,
+	GNOME_CANVAS_UPDATE_AFFINE     = 1 << 1,
+	GNOME_CANVAS_UPDATE_CLIP       = 1 << 2,
+	GNOME_CANVAS_UPDATE_VISIBILITY = 1 << 3,
+	GNOME_CANVAS_UPDATE_IS_VISIBLE = 1 << 4		/* Deprecated.  FIXME: remove this */
+};
+
+/* Data for rendering in antialiased mode */
+typedef struct {
+	/* 24-bit RGB buffer for rendering */
+	guchar *buf;
+
+	/* Rectangle describing the rendering area */
+	ArtIRect rect;
+
+	/* Rowstride for the buffer */
+	int buf_rowstride;
+
+	/* Background color, given as 0xrrggbb */
+	guint32 bg_color;
+
+	/* Invariant: at least one of the following flags is true. */
+
+	/* Set when the render rectangle area is the solid color bg_color */
+	unsigned int is_bg : 1;
+
+	/* Set when the render rectangle area is represented by the buf */
+	unsigned int is_buf : 1;
+} GnomeCanvasBuf;
+
+
+#define GNOME_TYPE_CANVAS_ITEM            (gnome_canvas_item_get_type ())
+#define GNOME_CANVAS_ITEM(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItem))
+#define GNOME_CANVAS_ITEM_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItemClass))
+#define GNOME_IS_CANVAS_ITEM(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_ITEM))
+#define GNOME_IS_CANVAS_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_ITEM))
+#define GNOME_CANVAS_ITEM_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_ITEM, GnomeCanvasItemClass))
+
+
+struct _GnomeCanvasItem {
+	GtkObject object;
+
+	/* Parent canvas for this item */
+	GnomeCanvas *canvas;
+
+	/* Parent canvas group for this item (a GnomeCanvasGroup) */
+	GnomeCanvasItem *parent;
+
+	/* If NULL, assumed to be the identity tranform.  If flags does not have
+	 * AFFINE_FULL, then a two-element array containing a translation.  If
+	 * flags contains AFFINE_FULL, a six-element array containing an affine
+	 * transformation.
+	 */
+	double *xform;
+
+	/* Bounding box for this item (in canvas coordinates) */
+	double x1, y1, x2, y2;
+};
+
+struct _GnomeCanvasItemClass {
+	GtkObjectClass parent_class;
+
+	/* Tell the item to update itself.  The flags are from the update flags
+	 * defined above.  The item should update its internal state from its
+	 * queued state, and recompute and request its repaint area.  The
+	 * affine, if used, is a pointer to a 6-element array of doubles.  The
+	 * update method also recomputes the bounding box of the item.
+	 */
+	void (* update) (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+
+	/* Realize an item -- create GCs, etc. */
+	void (* realize) (GnomeCanvasItem *item);
+
+	/* Unrealize an item */
+	void (* unrealize) (GnomeCanvasItem *item);
+
+	/* Map an item - normally only need by items with their own GdkWindows */
+	void (* map) (GnomeCanvasItem *item);
+
+	/* Unmap an item */
+	void (* unmap) (GnomeCanvasItem *item);
+
+	/* Return the microtile coverage of the item */
+	ArtUta *(* coverage) (GnomeCanvasItem *item);
+
+	/* Draw an item of this type.  (x, y) are the upper-left canvas pixel
+	 * coordinates of the drawable, a temporary pixmap, where things get
+	 * drawn.  (width, height) are the dimensions of the drawable.
+	 */
+	void (* draw) (GnomeCanvasItem *item, GdkDrawable *drawable,
+		       int x, int y, int width, int height);
+
+	/* Render the item over the buffer given.  The buf data structure
+	 * contains both a pointer to a packed 24-bit RGB array, and the
+	 * coordinates.  This method is only used for antialiased canvases.
+	 *
+	 * TODO: figure out where clip paths fit into the rendering framework.
+	 */
+	void (* render) (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+
+	/* Calculate the distance from an item to the specified point.  It also
+         * returns a canvas item which is the item itself in the case of the
+         * object being an actual leaf item, or a child in case of the object
+         * being a canvas group.  (cx, cy) are the canvas pixel coordinates that
+         * correspond to the item-relative coordinates (x, y).
+	 */
+	double (* point) (GnomeCanvasItem *item, double x, double y, int cx, int cy,
+			  GnomeCanvasItem **actual_item);
+
+	/* Fetch the item's bounding box (need not be exactly tight).  This
+	 * should be in item-relative coordinates.
+	 */
+	void (* bounds) (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+	/* Signal: an event occurred for an item of this type.  The (x, y)
+	 * coordinates are in the canvas world coordinate system.
+	 */
+	gboolean (* event)                (GnomeCanvasItem *item, GdkEvent *event);
+
+	/* Reserved for future expansion */
+	gpointer spare_vmethods [4];
+};
+
+
+GType gnome_canvas_item_get_type (void) G_GNUC_CONST;
+
+/* Create a canvas item using the standard Gtk argument mechanism.  The item is
+ * automatically inserted at the top of the specified canvas group.  The last
+ * argument must be a NULL pointer.
+ */
+GnomeCanvasItem *gnome_canvas_item_new (GnomeCanvasGroup *parent, GType type,
+					const gchar *first_arg_name, ...);
+
+/* Constructors for use in derived classes and language wrappers */
+void gnome_canvas_item_construct (GnomeCanvasItem *item, GnomeCanvasGroup *parent,
+				  const gchar *first_arg_name, va_list args);
+
+/* Configure an item using the standard Gtk argument mechanism.  The last
+ * argument must be a NULL pointer.
+ */
+void gnome_canvas_item_set (GnomeCanvasItem *item, const gchar *first_arg_name, ...);
+
+/* Used only for language wrappers and the like */
+void gnome_canvas_item_set_valist (GnomeCanvasItem *item,
+				   const gchar *first_arg_name, va_list args);
+
+/* Move an item by the specified amount */
+void gnome_canvas_item_move (GnomeCanvasItem *item, double dx, double dy);
+
+/* Apply a relative affine transformation to the item. */
+void gnome_canvas_item_affine_relative (GnomeCanvasItem *item, const double affine[6]);
+
+/* Apply an absolute affine transformation to the item. */
+void gnome_canvas_item_affine_absolute (GnomeCanvasItem *item, const double affine[6]);
+
+/* Raise an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void gnome_canvas_item_raise (GnomeCanvasItem *item, int positions);
+
+/* Lower an item in the z-order of its parent group by the specified number of
+ * positions.
+ */
+void gnome_canvas_item_lower (GnomeCanvasItem *item, int positions);
+
+/* Raise an item to the top of its parent group's z-order. */
+void gnome_canvas_item_raise_to_top (GnomeCanvasItem *item);
+
+/* Lower an item to the bottom of its parent group's z-order */
+void gnome_canvas_item_lower_to_bottom (GnomeCanvasItem *item);
+
+/* Show an item (make it visible).  If the item is already shown, it has no
+ * effect.
+ */
+void gnome_canvas_item_show (GnomeCanvasItem *item);
+
+/* Hide an item (make it invisible).  If the item is already invisible, it has
+ * no effect.
+ */
+void gnome_canvas_item_hide (GnomeCanvasItem *item);
+
+/* Grab the mouse for the specified item.  Only the events in event_mask will be
+ * reported.  If cursor is non-NULL, it will be used during the duration of the
+ * grab.  Time is a proper X event time parameter.  Returns the same values as
+ * XGrabPointer().
+ */
+int gnome_canvas_item_grab (GnomeCanvasItem *item, unsigned int event_mask,
+			    GdkCursor *cursor, guint32 etime);
+
+/* Ungrabs the mouse -- the specified item must be the same that was passed to
+ * gnome_canvas_item_grab().  Time is a proper X event time parameter.
+ */
+void gnome_canvas_item_ungrab (GnomeCanvasItem *item, guint32 etime);
+
+/* These functions convert from a coordinate system to another.  "w" is world
+ * coordinates and "i" is item coordinates.
+ */
+void gnome_canvas_item_w2i (GnomeCanvasItem *item, double *x, double *y);
+void gnome_canvas_item_i2w (GnomeCanvasItem *item, double *x, double *y);
+
+/* Gets the affine transform that converts from item-relative coordinates to
+ * world coordinates.
+ */
+void gnome_canvas_item_i2w_affine (GnomeCanvasItem *item, double affine[6]);
+
+/* Gets the affine transform that converts from item-relative coordinates to
+ * canvas pixel coordinates.
+ */
+void gnome_canvas_item_i2c_affine (GnomeCanvasItem *item, double affine[6]);
+
+/* Remove the item from its parent group and make the new group its parent.  The
+ * item will be put on top of all the items in the new group.  The item's
+ * coordinates relative to its new parent to *not* change -- this means that the
+ * item could potentially move on the screen.
+ * 
+ * The item and the group must be in the same canvas.  An item cannot be
+ * reparented to a group that is the item itself or that is an inferior of the
+ * item.
+ */
+void gnome_canvas_item_reparent (GnomeCanvasItem *item, GnomeCanvasGroup *new_group);
+
+/* Used to send all of the keystroke events to a specific item as well as
+ * GDK_FOCUS_CHANGE events.
+ */
+void gnome_canvas_item_grab_focus (GnomeCanvasItem *item);
+
+/* Fetch the bounding box of the item.  The bounding box may not be exactly
+ * tight, but the canvas items will do the best they can.  The returned bounding
+ * box is in the coordinate system of the item's parent.
+ */
+void gnome_canvas_item_get_bounds (GnomeCanvasItem *item,
+				   double *x1, double *y1, double *x2, double *y2);
+
+/* Request that the update method eventually get called.  This should be used
+ * only by item implementations.
+ */
+void gnome_canvas_item_request_update (GnomeCanvasItem *item);
+
+
+/* GnomeCanvasGroup - a group of canvas items
+ *
+ * A group is a node in the hierarchical tree of groups/items inside a canvas.
+ * Groups serve to give a logical structure to the items.
+ *
+ * Consider a circuit editor application that uses the canvas for its schematic
+ * display.  Hierarchically, there would be canvas groups that contain all the
+ * components needed for an "adder", for example -- this includes some logic
+ * gates as well as wires.  You can move stuff around in a convenient way by
+ * doing a gnome_canvas_item_move() of the hierarchical groups -- to move an
+ * adder, simply move the group that represents the adder.
+ *
+ * The following arguments are available:
+ *
+ * name		type		read/write	description
+ * --------------------------------------------------------------------------------
+ * x		double		RW		X coordinate of group's origin
+ * y		double		RW		Y coordinate of group's origin
+ */
+
+
+#define GNOME_TYPE_CANVAS_GROUP            (gnome_canvas_group_get_type ())
+#define GNOME_CANVAS_GROUP(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroup))
+#define GNOME_CANVAS_GROUP_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroupClass))
+#define GNOME_IS_CANVAS_GROUP(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS_GROUP))
+#define GNOME_IS_CANVAS_GROUP_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS_GROUP))
+#define GNOME_CANVAS_GROUP_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS_GROUP, GnomeCanvasGroupClass))
+
+
+struct _GnomeCanvasGroup {
+	GnomeCanvasItem item;
+
+	/* Children of the group */
+	GList *item_list;
+	GList *item_list_end;
+};
+
+struct _GnomeCanvasGroupClass {
+	GnomeCanvasItemClass parent_class;
+};
+
+
+GType gnome_canvas_group_get_type (void) G_GNUC_CONST;
+
+
+/*** GnomeCanvas ***/
+
+
+#define GNOME_TYPE_CANVAS            (gnome_canvas_get_type ())
+#define GNOME_CANVAS(obj)            (GTK_CHECK_CAST ((obj), GNOME_TYPE_CANVAS, GnomeCanvas))
+#define GNOME_CANVAS_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GNOME_TYPE_CANVAS, GnomeCanvasClass))
+#define GNOME_IS_CANVAS(obj)         (GTK_CHECK_TYPE ((obj), GNOME_TYPE_CANVAS))
+#define GNOME_IS_CANVAS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GNOME_TYPE_CANVAS))
+#define GNOME_CANVAS_GET_CLASS(obj)  (GTK_CHECK_GET_CLASS ((obj), GNOME_TYPE_CANVAS, GnomeCanvasClass))
+
+
+struct _GnomeCanvas {
+	GtkLayout layout;
+
+	/* Root canvas group */
+	GnomeCanvasItem *root;
+
+	/* Area that needs redrawing, stored as a microtile array */
+	ArtUta *redraw_area;
+
+	/* The item containing the mouse pointer, or NULL if none */
+	GnomeCanvasItem *current_item;
+
+	/* Item that is about to become current (used to track deletions and such) */
+	GnomeCanvasItem *new_current_item;
+
+	/* Item that holds a pointer grab, or NULL if none */
+	GnomeCanvasItem *grabbed_item;
+
+	/* If non-NULL, the currently focused item */
+	GnomeCanvasItem *focused_item;
+
+	/* GC for temporary draw pixmap */
+	GdkGC *pixmap_gc;
+
+	/* Event on which selection of current item is based */
+	GdkEvent pick_event;
+
+	/* Scrolling region */
+	double scroll_x1, scroll_y1;
+	double scroll_x2, scroll_y2;
+
+	/* Scaling factor to be used for display */
+	double pixels_per_unit;
+
+	/* Idle handler ID */
+	guint idle_id;
+
+	/* Signal handler ID for destruction of the root item */
+	guint root_destroy_id;
+
+	/* Area that is being redrawn.  Contains (x1, y1) but not (x2, y2).
+	 * Specified in canvas pixel coordinates.
+	 */
+	int redraw_x1, redraw_y1;
+	int redraw_x2, redraw_y2;
+
+	/* Offsets of the temprary drawing pixmap */
+	int draw_xofs, draw_yofs;
+
+	/* Internal pixel offsets when zoomed out */
+	int zoom_xofs, zoom_yofs;
+
+	/* Last known modifier state, for deferred repick when a button is down */
+	int state;
+
+	/* Event mask specified when grabbing an item */
+	guint grabbed_event_mask;
+
+	/* Tolerance distance for picking items */
+	int close_enough;
+
+	/* Whether the canvas should center the scroll region in the middle of
+	 * the window if the scroll region is smaller than the window.
+	 */
+	unsigned int center_scroll_region : 1;
+
+	/* Whether items need update at next idle loop iteration */
+	unsigned int need_update : 1;
+
+	/* Whether the canvas needs redrawing at the next idle loop iteration */
+	unsigned int need_redraw : 1;
+
+	/* Whether current item will be repicked at next idle loop iteration */
+	unsigned int need_repick : 1;
+
+	/* For use by internal pick_current_item() function */
+	unsigned int left_grabbed_item : 1;
+
+	/* For use by internal pick_current_item() function */
+	unsigned int in_repick : 1;
+
+	/* Whether the canvas is in antialiased mode or not */
+	unsigned int aa : 1;
+
+	/* Which dither mode to use for antialiased mode drawing */
+	GdkRgbDither dither;
+};
+
+struct _GnomeCanvasClass {
+	GtkLayoutClass parent_class;
+
+	/* Draw the background for the area given. This method is only used
+	 * for non-antialiased canvases.
+	 */
+	void (* draw_background) (GnomeCanvas *canvas, GdkDrawable *drawable,
+				  int x, int y, int width, int height);
+
+	/* Render the background for the buffer given. The buf data structure
+	 * contains both a pointer to a packed 24-bit RGB array, and the
+	 * coordinates. This method is only used for antialiased canvases.
+	 */
+	void (* render_background) (GnomeCanvas *canvas, GnomeCanvasBuf *buf);
+
+	/* Private Virtual methods for groping the canvas inside bonobo */
+	void (* request_update) (GnomeCanvas *canvas);
+
+	/* Reserved for future expansion */
+	gpointer spare_vmethods [4];
+};
+
+
+GType gnome_canvas_get_type (void) G_GNUC_CONST;
+
+/* Creates a new canvas.  You should check that the canvas is created with the
+ * proper visual and colormap.  Any visual will do unless you intend to insert
+ * gdk_imlib images into it, in which case you should use the gdk_imlib visual.
+ *
+ * You should call gnome_canvas_set_scroll_region() soon after calling this
+ * function to set the desired scrolling limits for the canvas.
+ */
+GtkWidget *gnome_canvas_new (void);
+
+/* Creates a new antialiased empty canvas.  You should push the GdkRgb colormap
+ * and visual for this.
+ */
+#ifndef GNOME_EXCLUDE_EXPERIMENTAL
+GtkWidget *gnome_canvas_new_aa (void);
+#endif
+
+/* Returns the root canvas item group of the canvas */
+GnomeCanvasGroup *gnome_canvas_root (GnomeCanvas *canvas);
+
+/* Sets the limits of the scrolling region, in world coordinates */
+void gnome_canvas_set_scroll_region (GnomeCanvas *canvas,
+				     double x1, double y1, double x2, double y2);
+
+/* Gets the limits of the scrolling region, in world coordinates */
+void gnome_canvas_get_scroll_region (GnomeCanvas *canvas,
+				     double *x1, double *y1, double *x2, double *y2);
+
+/* Whether the canvas centers the scroll region if it is smaller than the window */
+void gnome_canvas_set_center_scroll_region (GnomeCanvas *canvas, gboolean center_scroll_region);
+
+/* Returns whether the canvas is set to center the scroll region if it is smaller than the window */
+gboolean gnome_canvas_get_center_scroll_region (GnomeCanvas *canvas);
+
+/* Sets the number of pixels that correspond to one unit in world coordinates */
+void gnome_canvas_set_pixels_per_unit (GnomeCanvas *canvas, double n);
+
+/* Scrolls the canvas to the specified offsets, given in canvas pixel coordinates */
+void gnome_canvas_scroll_to (GnomeCanvas *canvas, int cx, int cy);
+
+/* Returns the scroll offsets of the canvas in canvas pixel coordinates.  You
+ * can specify NULL for any of the values, in which case that value will not be
+ * queried.
+ */
+void gnome_canvas_get_scroll_offsets (GnomeCanvas *canvas, int *cx, int *cy);
+
+/* Requests that the canvas be repainted immediately instead of in the idle
+ * loop.
+ */
+void gnome_canvas_update_now (GnomeCanvas *canvas);
+
+/* Returns the item that is at the specified position in world coordinates, or
+ * NULL if no item is there.
+ */
+GnomeCanvasItem *gnome_canvas_get_item_at (GnomeCanvas *canvas, double x, double y);
+
+/* For use only by item type implementations. Request that the canvas eventually
+ * redraw the specified region. The region is specified as a microtile
+ * array. This function takes over responsibility for freeing the uta argument.
+ */
+void gnome_canvas_request_redraw_uta (GnomeCanvas *canvas, ArtUta *uta);
+
+/* For use only by item type implementations.  Request that the canvas
+ * eventually redraw the specified region, specified in canvas pixel
+ * coordinates.  The region contains (x1, y1) but not (x2, y2).
+ */
+void gnome_canvas_request_redraw (GnomeCanvas *canvas, int x1, int y1, int x2, int y2);
+
+/* Gets the affine transform that converts world coordinates into canvas pixel
+ * coordinates.
+ */
+void gnome_canvas_w2c_affine (GnomeCanvas *canvas, double affine[6]);
+
+/* These functions convert from a coordinate system to another.  "w" is world
+ * coordinates, "c" is canvas pixel coordinates (pixel coordinates that are
+ * (0,0) for the upper-left scrolling limit and something else for the
+ * lower-left scrolling limit).
+ */
+void gnome_canvas_w2c (GnomeCanvas *canvas, double wx, double wy, int *cx, int *cy);
+void gnome_canvas_w2c_d (GnomeCanvas *canvas, double wx, double wy, double *cx, double *cy);
+void gnome_canvas_c2w (GnomeCanvas *canvas, int cx, int cy, double *wx, double *wy);
+
+/* This function takes in coordinates relative to the GTK_LAYOUT
+ * (canvas)->bin_window and converts them to world coordinates.
+ */
+void gnome_canvas_window_to_world (GnomeCanvas *canvas,
+				   double winx, double winy, double *worldx, double *worldy);
+
+/* This is the inverse of gnome_canvas_window_to_world() */
+void gnome_canvas_world_to_window (GnomeCanvas *canvas,
+				   double worldx, double worldy, double *winx, double *winy);
+
+/* Takes a string specification for a color and allocates it into the specified
+ * GdkColor.  If the string is null, then it returns FALSE. Otherwise, it
+ * returns TRUE.
+ */
+int gnome_canvas_get_color (GnomeCanvas *canvas, const char *spec, GdkColor *color);
+
+/* Allocates a color from the RGB value passed into this function. */
+gulong gnome_canvas_get_color_pixel (GnomeCanvas *canvas,
+				     guint        rgba);
+     
+
+/* Sets the stipple origin of the specified gc so that it will be aligned with
+ * all the stipples used in the specified canvas.  This is intended for use only
+ * by canvas item implementations.
+ */
+void gnome_canvas_set_stipple_origin (GnomeCanvas *canvas, GdkGC *gc);
+
+/* Controls the dithering used when the canvas renders.
+ * Only applicable to antialiased canvases - ignored by non-antialiased canvases.
+ */
+void gnome_canvas_set_dither (GnomeCanvas *canvas, GdkRgbDither dither);
+
+/* Returns the dither mode of an antialiased canvas.
+ * Only applicable to antialiased canvases - ignored by non-antialiased canvases.
+ */
+GdkRgbDither gnome_canvas_get_dither (GnomeCanvas *canvas);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgnomecanvas/libgnomecanvas.h b/src/libgnomecanvas/libgnomecanvas.h
new file mode 100644
index 0000000..93e21fa
--- /dev/null
+++ b/src/libgnomecanvas/libgnomecanvas.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+#ifndef LIBGNOMECANVAS_H
+#define LIBGNOMECANVAS_H
+
+#include <libgnomecanvas/gnome-canvas.h>
+#include <libgnomecanvas/gnome-canvas-line.h>
+#include <libgnomecanvas/gnome-canvas-text.h>
+#include <libgnomecanvas/gnome-canvas-rich-text.h>
+#include <libgnomecanvas/gnome-canvas-polygon.h>
+#include <libgnomecanvas/gnome-canvas-pixbuf.h>
+#include <libgnomecanvas/gnome-canvas-widget.h>
+#include <libgnomecanvas/gnome-canvas-rect-ellipse.h>
+#include <libgnomecanvas/gnome-canvas-bpath.h>
+#include <libgnomecanvas/gnome-canvas-util.h>
+#include <libgnomecanvas/gnome-canvas-clipgroup.h>
+
+G_BEGIN_DECLS
+
+GType gnome_canvas_points_get_type (void);
+#define GNOME_TYPE_CANVAS_POINTS gnome_canvas_points_get_type()
+
+G_END_DECLS
+
+#endif /* LIBGNOMECANVAS_H */
diff --git a/src/libgnomecanvas/libgnomecanvastypes.c b/src/libgnomecanvas/libgnomecanvastypes.c
new file mode 100644
index 0000000..9ea5966
--- /dev/null
+++ b/src/libgnomecanvas/libgnomecanvastypes.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 1999, 2000 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+  @NOTATION@
+ */
+
+#include <config.h>
+#include <glib-object.h>
+
+#include <libgnomecanvas/libgnomecanvas.h>
+
+GType
+gnome_canvas_points_get_type (void)
+{
+    static GType type_canvas_points = 0;
+
+    if (!type_canvas_points)
+	type_canvas_points = g_boxed_type_register_static
+	    ("GnomeCanvasPoints", 
+	     (GBoxedCopyFunc) gnome_canvas_points_ref,
+	     (GBoxedFreeFunc) gnome_canvas_points_unref);
+
+    return type_canvas_points;
+}



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