[goffice] Add support for svg images.



commit 6924d264ae12c9ed4c76c1039e376e616759d9ab
Author: Jean Brefort <jean brefort normalesup org>
Date:   Wed Oct 26 12:06:48 2011 +0200

    Add support for svg images.

 ChangeLog                        |    4 +
 goffice/app/go-doc.c             |   24 ++-
 goffice/app/go-doc.h             |    2 +-
 goffice/canvas/goc-arc.c         |    4 +-
 goffice/canvas/goc-circle.c      |    3 +-
 goffice/canvas/goc-ellipse.c     |    5 +-
 goffice/canvas/goc-path.c        |    4 +-
 goffice/canvas/goc-pixbuf.c      |   17 +--
 goffice/canvas/goc-polygon.c     |    8 +-
 goffice/canvas/goc-rectangle.c   |    5 +-
 goffice/component/go-component.c |   30 ++--
 goffice/goffice.c                |   24 +--
 goffice/graph/gog-error-bar.c    |   46 -----
 goffice/graph/gog-object-xml.c   |   37 +----
 goffice/graph/gog-renderer.c     |   22 +--
 goffice/gtk/goffice-gtk.c        |   13 ++
 goffice/utils/Makefile.am        |    2 +
 goffice/utils/go-image.c         |  393 ++++++++-----------------------------
 goffice/utils/go-image.h         |   36 +++-
 goffice/utils/go-persist.c       |    7 -
 goffice/utils/go-persist.h       |    2 -
 goffice/utils/go-pixbuf.c        |  270 +++++++++++++++++++++++++
 goffice/utils/go-pixbuf.h        |   10 +
 goffice/utils/go-style-prefs.ui  |    3 +
 goffice/utils/go-style.c         |  402 ++++++++------------------------------
 goffice/utils/go-style.h         |    7 +-
 goffice/utils/go-styled-object.c |   22 +--
 goffice/utils/go-styled-object.h |    3 +-
 goffice/utils/go-svg.c           |  182 +++++++++++++++++
 goffice/utils/go-svg.h           |   40 ++++
 goffice/utils/goffice-utils.h    |    3 +
 31 files changed, 788 insertions(+), 842 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index eed51a8..44f890b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2011-10-26  Jean Brefort  <jean brefort normalesup org>
+
+	* all files: add support for svg images using librsvg.
+
 2011-10-25  Morten Welinder  <terra gnome org>
 
 	* goffice/gtk/goffice-gtk.c (apply_ui_from_file): Add filename
diff --git a/goffice/app/go-doc.c b/goffice/app/go-doc.c
index a2b9111..3d44e88 100644
--- a/goffice/app/go-doc.c
+++ b/goffice/app/go-doc.c
@@ -324,7 +324,7 @@ go_doc_update_meta_data (GODoc *doc)
  *
  * Returns: the #GOImage is one exist with name @id. The caller does not own a
  * reference.
- */
+ **/
 GOImage *
 go_doc_get_image (GODoc *doc, char const *id)
 {
@@ -342,7 +342,7 @@ go_doc_get_image (GODoc *doc, char const *id)
  * the returned image might be different from @id, even if given.
  * Returns: either @image, in which case the document adds a reference on it, or
  * an identical image for which the owner does not own a reference.
- */
+ **/
 GOImage *
 go_doc_add_image (GODoc *doc, char const *id, GOImage *image)
 {
@@ -359,7 +359,7 @@ go_doc_add_image (GODoc *doc, char const *id, GOImage *image)
 	/* check if the image is already there */
 	g_hash_table_iter_init (&iter, doc->images);
 	while (g_hash_table_iter_next (&iter, (void**) &key, (void**) &img))
-		if (go_image_same_pixbuf (image, img))
+		if (!go_image_differ (image, img))
 			return img;
 
 	if (!id || !*id)
@@ -445,15 +445,19 @@ static void
 load_image (GsfXMLIn *xin, xmlChar const **attrs)
 {
 	GODoc *doc = GO_DOC (xin->user_state);
-	GOImage *image;
+	GOImage *image = NULL;
 	xmlChar const **attr = 	attrs;
+	GType type = 0;
 	if (!*attr)
 		return;
-	while (*attr && strcmp (*attr, "name"))
-		attr += 2;
-	image = (GOImage *) g_hash_table_lookup (doc->imagebuf, attr[1]);
+	for (attr = attrs; *attr; attr += 2)
+		if (!strcmp (*attr, "name"))
+			image = (GOImage *) g_hash_table_lookup (doc->imagebuf, attr[1]);
+		else if (!strcmp (*attr, "type"))
+			type = g_type_from_name (attr[1]);
 	if (!image) /* this should not occur, but if it does, we might want to load the image? */
 		return;
+	g_return_if_fail (type == 0 || G_OBJECT_TYPE (image) == type);
 	go_image_load_attrs (image, xin, attrs);
 	g_object_set_data (G_OBJECT (doc), "new image", image);
 }
@@ -522,15 +526,15 @@ go_doc_end_read	(GODoc *doc)
  * This function must be called after a call to go_doc_init_read(), otherwise
  * it will emit a critical and return NULL.
  * Returns: the found or created #GOImage.
- */
+ **/
 GOImage *
-go_doc_image_fetch (GODoc *doc, char const *id)
+go_doc_image_fetch (GODoc *doc, char const *id, GType type)
 {
 	GOImage *image;
 	g_return_val_if_fail (doc && doc->imagebuf, NULL);
 	image = g_hash_table_lookup (doc->imagebuf, id);
 	if (!image) {
-		image = g_object_new (GO_TYPE_IMAGE, NULL);
+		image = g_object_new (type, NULL);
 		go_image_set_name (image, id);
 		g_hash_table_replace (doc->imagebuf,
 				      g_strdup (go_image_get_name (image)),
diff --git a/goffice/app/go-doc.h b/goffice/app/go-doc.h
index 69e5d01..a9c10ef 100644
--- a/goffice/app/go-doc.h
+++ b/goffice/app/go-doc.h
@@ -71,7 +71,7 @@ void		 go_doc_save_image	(GODoc *doc, char const *id);
 void		 go_doc_init_read	(GODoc *doc, GsfInput *input);
 void		 go_doc_read		(GODoc *doc, GsfXMLIn *xin, xmlChar const **attrs);
 void		 go_doc_end_read	(GODoc *doc);
-GOImage		*go_doc_image_fetch 	(GODoc *doc, char const *id);
+GOImage		*go_doc_image_fetch 	(GODoc *doc, char const *id, GType type);
 
 G_END_DECLS
 
diff --git a/goffice/canvas/goc-arc.c b/goffice/canvas/goc-arc.c
index 300270e..f8e1c56 100644
--- a/goffice/canvas/goc-arc.c
+++ b/goffice/canvas/goc-arc.c
@@ -389,8 +389,8 @@ goc_arc_draw (GocItem const *item, cairo_t *cr)
 
 	cairo_save(cr);
 	if (goc_arc_prepare_draw (item, cr, 1)) {
-		if (arc->type > 0 && go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
-			cairo_fill_preserve (cr);
+		if (arc->type > 0)
+			go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
 		if (goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr)) {
 			cairo_stroke (cr);
 		} else {
diff --git a/goffice/canvas/goc-circle.c b/goffice/canvas/goc-circle.c
index 8c58ea5..47e8395 100644
--- a/goffice/canvas/goc-circle.c
+++ b/goffice/canvas/goc-circle.c
@@ -117,8 +117,7 @@ goc_circle_draw (GocItem const *item, cairo_t *cr)
 	cairo_arc (cr, 0., 0., 1., 0., 2 * M_PI);
 	cairo_restore (cr);
 	/* Fill the shape */
-	if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
-		cairo_fill_preserve (cr);
+	go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
 	/* Draw the line */
 	if (goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr))
 		cairo_stroke (cr);
diff --git a/goffice/canvas/goc-ellipse.c b/goffice/canvas/goc-ellipse.c
index ec3b5d4..f1d6a7a 100644
--- a/goffice/canvas/goc-ellipse.c
+++ b/goffice/canvas/goc-ellipse.c
@@ -141,7 +141,7 @@ goc_ellipse_update_bounds (GocItem *item)
 	if (goc_ellipse_prepare_draw (item, cr, 0)) {
 		if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
 			cairo_stroke_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
-		else if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
+		else if (go_style_is_fill_visible (go_styled_object_get_style (GO_STYLED_OBJECT (item))))
 			cairo_fill_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
 		else {
 			item->x0 = item->y0 = G_MAXDOUBLE;
@@ -199,8 +199,7 @@ goc_ellipse_draw (GocItem const *item, cairo_t *cr)
 {
 	cairo_save(cr);
 	if (goc_ellipse_prepare_draw (item, cr, 1)) {
-		if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
-			cairo_fill_preserve (cr);
+		go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
 		if (goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr)) {
 			cairo_stroke (cr);
 		} else {
diff --git a/goffice/canvas/goc-path.c b/goffice/canvas/goc-path.c
index e7d310d..570ff83 100644
--- a/goffice/canvas/goc-path.c
+++ b/goffice/canvas/goc-path.c
@@ -191,8 +191,8 @@ goc_path_draw (GocItem const *item, cairo_t *cr)
 
 	cairo_save(cr);
 	if (goc_path_prepare_draw (item, cr, 1)) {
-		if (path->closed && go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
-			cairo_fill_preserve (cr);
+		if (path->closed)
+			go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
 		if (goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr)) {
 			cairo_stroke (cr);
 		} else {
diff --git a/goffice/canvas/goc-pixbuf.c b/goffice/canvas/goc-pixbuf.c
index 14338a1..22a1422 100644
--- a/goffice/canvas/goc-pixbuf.c
+++ b/goffice/canvas/goc-pixbuf.c
@@ -177,17 +177,14 @@ goc_pixbuf_draw (GocItem const *item, cairo_t *cr)
 {
 	GocPixbuf *pixbuf = GOC_PIXBUF (item);
 	GOImage * image;
-	cairo_pattern_t *pat;
 	double height, width;
 	double scalex = 1., scaley = 1.;
 	int x;
-	cairo_matrix_t mat;
 
 	if (pixbuf->pixbuf == NULL || pixbuf->width == 0. || pixbuf->height == 0.)
 		return;
 
-	image = go_image_new_from_pixbuf (pixbuf->pixbuf);
-	pat = go_image_create_cairo_pattern (image);
+	image = GO_IMAGE (go_pixbuf_new_from_pixbuf (pixbuf->pixbuf));
 	if (pixbuf->width < 0.)
 		width = gdk_pixbuf_get_width (pixbuf->pixbuf);
 	else {
@@ -205,14 +202,10 @@ goc_pixbuf_draw (GocItem const *item, cairo_t *cr)
 		pixbuf->x + pixbuf->width: pixbuf->x;
 	goc_group_cairo_transform (item->parent, cr, x, (int) pixbuf->y);
 	cairo_rotate (cr, pixbuf->rotation);
-	if (scalex != 1. || scaley != 1.) {
-		cairo_matrix_init_scale (&mat, 1. / scalex, 1. /scaley);
-		cairo_pattern_set_matrix (pat, &mat);
-	}
-	cairo_rectangle (cr, 0., 0., ceil (width), ceil (height));
-	cairo_set_source (cr, pat);
-	cairo_pattern_destroy (pat);
-        cairo_fill (cr);
+	if (scalex != 1. || scaley != 1.)
+		cairo_scale (cr, scalex, scaley);
+	cairo_move_to (cr, 0, 0);
+	go_image_draw (image, cr);
 	cairo_restore (cr);
 	g_object_unref (image);
 }
diff --git a/goffice/canvas/goc-polygon.c b/goffice/canvas/goc-polygon.c
index 7c8ac08..cfcc52b 100644
--- a/goffice/canvas/goc-polygon.c
+++ b/goffice/canvas/goc-polygon.c
@@ -220,7 +220,7 @@ goc_polygon_update_bounds (GocItem *item)
 	cr = cairo_create (surface);
 	if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
 		mode = 1;
-	else if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
+	else if (go_style_is_fill_visible (go_styled_object_get_style (GO_STYLED_OBJECT (item))))
 		mode = 2;
 	if (mode && goc_polygon_prepare_path (item, cr, 0)) {
 		if (mode == 1)
@@ -258,8 +258,7 @@ goc_polygon_distance (GocItem *item, double x, double y, GocItem **near_item)
 	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
 	cr = cairo_create (surface);
 	goc_polygon_prepare_path (item, cr, 0);
-	if (style->fill.type != GO_STYLE_FILL_NONE) {
-		go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr);
+	if (go_style_is_fill_visible (style)) {
 		if (cairo_in_fill (cr, x, y))
 			res = 0;
 		else if ((item->x1 - item->x0 < 5 || item->y1 - item->y0 < 5) && style->line.dash_type == GO_LINE_NONE) {
@@ -285,8 +284,7 @@ goc_polygon_draw (GocItem const *item, cairo_t *cr)
 {
 	cairo_save (cr);
 	if (goc_polygon_prepare_path (item, cr, 1)) {
-		if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
-			cairo_fill_preserve (cr);
+		go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
 
 		if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
 			cairo_stroke (cr);
diff --git a/goffice/canvas/goc-rectangle.c b/goffice/canvas/goc-rectangle.c
index 285ec5a..decff6b 100644
--- a/goffice/canvas/goc-rectangle.c
+++ b/goffice/canvas/goc-rectangle.c
@@ -215,7 +215,7 @@ goc_rectangle_update_bounds (GocItem *item)
 	if (goc_rectangle_prepare_draw (item, cr, 0)) {
 		if (go_styled_object_set_cairo_line (GO_STYLED_OBJECT (item), cr))
 			cairo_stroke_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
-		else if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
+		else if (go_style_is_fill_visible (go_styled_object_get_style (GO_STYLED_OBJECT (item))))
 			cairo_fill_extents (cr, &item->x0, &item->y0, &item->x1, &item->y1);
 		else {
 			item->x0 = item->y0 = G_MAXDOUBLE;
@@ -273,8 +273,7 @@ goc_rectangle_draw (GocItem const *item, cairo_t *cr)
 {
 	cairo_save(cr);
 	if (goc_rectangle_prepare_draw (item, cr, 1)) {
-		if (go_styled_object_set_cairo_fill (GO_STYLED_OBJECT (item), cr))
-			cairo_fill_preserve (cr);
+		go_styled_object_fill (GO_STYLED_OBJECT (item), cr, TRUE);
 		if (goc_styled_item_set_cairo_line (GOC_STYLED_ITEM (item), cr)) {
 			cairo_stroke (cr);
 		} else {
diff --git a/goffice/component/go-component.c b/goffice/component/go-component.c
index 77df27c..3b93371 100644
--- a/goffice/component/go-component.c
+++ b/goffice/component/go-component.c
@@ -249,7 +249,7 @@ go_component_snapshot_render (GOComponent *component, cairo_t *cr,
 	GOComponentSnapshot *snapshot = (GOComponentSnapshot *) component;
 	switch (component->snapshot_type) {
 	case GO_SNAPSHOT_SVG:
-#if defined(GOFFICE_WITH_RSVG)
+		/* NOTE: we might use lasem here, and also use a GOSvg image */ 
 		if (snapshot->image == NULL) {
 			GError *err = NULL;
 			snapshot->image = (void *) rsvg_handle_new_from_data (
@@ -275,11 +275,7 @@ go_component_snapshot_render (GOComponent *component, cairo_t *cr,
 			cairo_restore (cr);
 		}
 		break;
-#elif defined(GOFFICE_WITH_LASEM)
-		/* TODO: implement a Lasem based svg rendering when possible */
-#endif
 	case GO_SNAPSHOT_PNG: {
-		cairo_pattern_t *pattern;
 		if (snapshot->image == NULL) {
 			GInputStream *in = g_memory_input_stream_new_from_data (
 						component->snapshot_data,
@@ -290,28 +286,26 @@ go_component_snapshot_render (GOComponent *component, cairo_t *cr,
 			if (err) {
 				g_error_free (err);
 			} else
-				snapshot->image = (void *) go_image_new_from_pixbuf (pixbuf);
+				snapshot->image = (void *) go_pixbuf_new_from_pixbuf (pixbuf);
 			if (pixbuf)
 				g_object_unref (pixbuf);
 
 		}
-		cairo_rectangle (cr, 0, 0, width, height);
 		if (snapshot->image != NULL) {
 			int w, h;
 			double scalex = 1., scaley = 1.;
-			cairo_matrix_t matrix;
-			pattern = go_image_create_cairo_pattern (GO_IMAGE (snapshot->image));
+			cairo_save (cr);
 			g_object_get (snapshot->image, "width", &w, "height", &h, NULL);
 			cairo_user_to_device_distance (cr, &scalex, &scaley);
-			cairo_matrix_init_scale (&matrix,
-						 w / width * scalex,
-						 h / height * scaley);
-			cairo_pattern_set_matrix (pattern, &matrix);
-		} else
-			pattern = cairo_pattern_create_rgba (1, 1, 1, 1);
-		cairo_set_source (cr, pattern);
-		cairo_pattern_destroy (pattern);
-		cairo_fill (cr);
+			cairo_scale (cr, width / w / scalex, height / h / scaley);
+			cairo_move_to (cr, 0, 0);
+			go_image_draw (GO_IMAGE (snapshot->image), cr);
+			cairo_restore (cr);
+		} else {
+			cairo_rectangle (cr, 0, 0, width, height);
+			cairo_set_source_rgb (cr, 1., 1., 1.);
+			cairo_fill (cr);
+		}
 		break;
 	}
 	default:
diff --git a/goffice/goffice.c b/goffice/goffice.c
index df9f3a8..ccef860 100644
--- a/goffice/goffice.c
+++ b/goffice/goffice.c
@@ -22,28 +22,6 @@
 #include <goffice/goffice-config.h>
 #include <goffice/goffice.h>
 #include <goffice/goffice-priv.h>
-#include <goffice/graph/gog-series.h>
-#include <goffice/graph/gog-plot.h>
-#include <goffice/graph/gog-plot-engine.h>
-#include <goffice/graph/gog-chart.h>
-#include <goffice/graph/gog-graph.h>
-#include <goffice/graph/gog-axis.h>
-#include <goffice/graph/gog-legend.h>
-#include <goffice/graph/gog-label.h>
-#include <goffice/graph/gog-grid.h>
-#include <goffice/graph/gog-grid-line.h>
-#include <goffice/graph/gog-theme.h>
-#include <goffice/graph/gog-error-bar.h>
-#include <goffice/graph/gog-series-lines.h>
-#include <goffice/graph/gog-3d-box.h>
-#include <goffice/data/go-data-simple.h>
-#include <goffice/math/go-distribution.h>
-#include <goffice/math/go-math.h>
-#include <goffice/utils/go-format.h>
-#include <goffice/utils/go-font.h>
-#include <goffice/app/go-conf.h>
-#include <goffice/app/go-plugin-service.h>
-#include <goffice/component/go-component-factory.h>
 #include <gsf/gsf-utils.h>
 
 #include "goffice-paths.h"
@@ -235,6 +213,8 @@ libgoffice_init (void)
 	(void) GO_TYPE_DATA_SCALAR_VAL;
 	(void) GO_TYPE_DATA_SCALAR_STR;
 	(void) GOG_3D_BOX_TYPE;
+	(void) GO_TYPE_PIXBUF;
+	(void) GO_TYPE_SVG;
 	_gog_themes_init ();
 	_go_number_format_init ();
 	_go_currency_date_format_init ();
diff --git a/goffice/graph/gog-error-bar.c b/goffice/graph/gog-error-bar.c
index 3b34923..5a4ab33 100644
--- a/goffice/graph/gog-error-bar.c
+++ b/goffice/graph/gog-error-bar.c
@@ -359,51 +359,6 @@ gog_error_bar_class_init (GogErrorBarClass *klass)
 	gobject_klass->finalize		= gog_error_bar_finalize;
 }
 
-static gboolean
-gog_error_bar_persist_dom_load (GOPersist *gp, xmlNode *node)
-{
-	GogErrorBar *bar = GOG_ERROR_BAR (gp);
-
-	gchar* str;
-	str = xmlGetProp (node, CC2XML ("error_type"));
-	if (str) {
-		if (!strcmp (str, "absolute"))
-			bar->type = GOG_ERROR_BAR_TYPE_ABSOLUTE;
-		else if (!strcmp (str, "relative"))
-			bar->type = GOG_ERROR_BAR_TYPE_RELATIVE;
-		else if (!strcmp (str, "percent"))
-			bar->type = GOG_ERROR_BAR_TYPE_PERCENT;
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, CC2XML ("display"));
-	if (str) {
-		if (!strcmp (str, "none"))
-			bar->display = GOG_ERROR_BAR_DISPLAY_NONE;
-		else if (!strcmp (str, "positive"))
-			bar->display = GOG_ERROR_BAR_DISPLAY_POSITIVE;
-		else if (!strcmp (str, "negative"))
-			bar->display = GOG_ERROR_BAR_DISPLAY_NEGATIVE;
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, CC2XML ("width"));
-	if (str) {
-		bar->width = g_strtod (str, NULL);
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, CC2XML ("line_width"));
-	if (str) {
-		bar->style->line.width = g_strtod (str, NULL);
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, CC2XML ("color"));
-	if (str != NULL) {
-		go_color_from_str (str, &bar->style->line.color);
-		xmlFree (str);
-	}
-
-	return TRUE;
-}
-
 static void
 gog_error_bar_persist_sax_save (GOPersist const *gp, GsfXMLOut *output)
 {
@@ -472,7 +427,6 @@ gog_error_bar_persist_prep_sax (GOPersist *gp, GsfXMLIn *xin, xmlChar const **at
 static void
 gog_error_bar_persist_init (GOPersistClass *iface)
 {
-	iface->dom_load = gog_error_bar_persist_dom_load;
 	iface->prep_sax = gog_error_bar_persist_prep_sax;
 	iface->sax_save = gog_error_bar_persist_sax_save;
 }
diff --git a/goffice/graph/gog-object-xml.c b/goffice/graph/gog-object-xml.c
index 7997b45..8f72ed6 100644
--- a/goffice/graph/gog-object-xml.c
+++ b/goffice/graph/gog-object-xml.c
@@ -39,8 +39,8 @@
 #define GOG_BACKPLANE_OLD_ROLE_NAME	"Grid"
 #define GOG_BACKPLANE_NEW_ROLE_NAME	"Backplane"
 
-static void
-gog_object_set_arg_full (char const *name, char const *val, GogObject *obj, xmlNode *xml_node)
+void
+gog_object_set_arg (char const *name, char const *val, GogObject *obj)
 {
 	GParamSpec *pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name);
 	GType prop_type;
@@ -60,32 +60,7 @@ gog_object_set_arg_full (char const *name, char const *val, GogObject *obj, xmlN
 			   g_type_name (prop_type), pspec->name);
 		return;
 	}
-
-	if (G_TYPE_FUNDAMENTAL (prop_type) == G_TYPE_OBJECT) {
-		g_value_init (&res, prop_type);
-		if (g_type_is_a (prop_type, G_TYPE_OBJECT)) {
-			xmlChar *type_name;
-			GType    type = 0;
-			GObject *val_obj;
-
-			success = FALSE;
-			type_name = xmlGetProp (xml_node,
-						(xmlChar const *) "type");
-			if (type_name != NULL) {
-				type = g_type_from_name (type_name);
-			}
-			xmlFree (type_name);
-			if (type != 0) {
-				val_obj = g_object_new (type, NULL);
-				if (GO_IS_PERSIST (val_obj) &&
-				    go_persist_dom_load (GO_PERSIST (val_obj), xml_node)) {
-					g_value_set_object (&res, val_obj);
-					success = TRUE;
-				}
-				g_object_unref (val_obj);
-			}
-		}
-	} else if (!gsf_xml_gvalue_from_str (&res, prop_type, val))
+	if (!gsf_xml_gvalue_from_str (&res, prop_type, val))
 		success = FALSE;
 
 	if (!success) {
@@ -96,12 +71,6 @@ gog_object_set_arg_full (char const *name, char const *val, GogObject *obj, xmlN
 	g_value_unset (&res);
 }
 
-void
-gog_object_set_arg (char const *name, char const *val, GogObject *obj)
-{
-	gog_object_set_arg_full (name, val, obj, NULL);
-}
-
 static void
 gog_object_write_property_sax (GogObject const *obj, GParamSpec *pspec, GsfXMLOut *output)
 {
diff --git a/goffice/graph/gog-renderer.c b/goffice/graph/gog-renderer.c
index bc2ee2f..4b19214 100644
--- a/goffice/graph/gog-renderer.c
+++ b/goffice/graph/gog-renderer.c
@@ -221,23 +221,7 @@ emit_line (GogRenderer *rend, gboolean preserve, GOPathOptions options)
 static void
 emit_fill (GogRenderer *rend, gboolean preserve)
 {
-	GOStyle const *style = rend->cur_style;
-	cairo_t *cr = rend->cairo;
-	cairo_pattern_t *cr_pattern = NULL;
-
-	cr_pattern = go_style_create_cairo_pattern (style, cr);
-	if (cr_pattern == NULL) {
-		if (!preserve)
-			cairo_new_path (cr);
-		return;
-	}
-	cairo_set_source (cr, cr_pattern);
-	cairo_pattern_destroy (cr_pattern);
-
-	if (preserve)
-		cairo_fill_preserve (cr);
-	else
-		cairo_fill (cr);
+	go_style_fill (rend->cur_style, rend->cairo, preserve);
 }
 
 static void
@@ -1524,8 +1508,10 @@ gog_renderer_export_image (GogRenderer *rend, GOImageFormat format,
 	cairo_surface_t *surface = NULL;
 	gboolean status;
 	GdkPixbuf *pixbuf;
+#ifdef GOFFICE_WITH_GTK
 	GdkPixbuf *output_pixbuf;
 	gboolean result;
+#endif
 	double width_in_pts, height_in_pts;
 
 	g_return_val_if_fail (GOG_IS_RENDERER (rend), FALSE);
@@ -1592,6 +1578,7 @@ do_export_vectorial:
 			if (pixbuf == NULL)
 				return FALSE;
 			format_info = go_image_get_format_info (format);
+#ifdef GOFFICE_WITH_GTK
 			if (!format_info->alpha_support)
 				output_pixbuf = gdk_pixbuf_composite_color_simple
 					(pixbuf,
@@ -1609,6 +1596,7 @@ do_export_vectorial:
 			if (!format_info->alpha_support)
 				g_object_unref (output_pixbuf);
 			return result;
+#endif
 	}
 
 	return FALSE;
diff --git a/goffice/gtk/goffice-gtk.c b/goffice/gtk/goffice-gtk.c
index 578ec90..4015b33 100644
--- a/goffice/gtk/goffice-gtk.c
+++ b/goffice/gtk/goffice-gtk.c
@@ -161,6 +161,7 @@ go_gtk_builder_new (char const *uifile,
 	GtkBuilder *gui;
 	GError *error = NULL;
 	gboolean ok = FALSE;
+	gboolean need_grid_update = (gtk_check_version (3, 2, 0) != NULL); /* remove when we require 3.2.0 or later */
 
 	g_return_val_if_fail (uifile != NULL, NULL);
 
@@ -202,6 +203,18 @@ go_gtk_builder_new (char const *uifile,
 	} else if (error)
 		g_error_free (error);
 
+	if (need_grid_update && gui) { /* remove when we require gtk+-3.2.0 or later */
+		GSList *l = gtk_builder_get_objects (gui), *ptr;
+		int rsep, csep;
+		ptr = l;
+		while (ptr) {
+			if (GTK_IS_GRID (ptr->data)) {
+				g_object_get (ptr->data, "row-spacing", &csep, "column-spacing", &rsep, NULL);
+				g_object_set (ptr->data, "row-spacing", rsep, "column-spacing", csep, NULL);
+			}
+			ptr = ptr->next;
+		}
+	}
 	return gui;
 }
 
diff --git a/goffice/utils/Makefile.am b/goffice/utils/Makefile.am
index c193e6a..6142123 100644
--- a/goffice/utils/Makefile.am
+++ b/goffice/utils/Makefile.am
@@ -13,6 +13,7 @@ libgoffice_utils_la_SOURCES =	\
 	go-gradient.c		\
 	go-image.c		\
 	go-pixbuf.c		\
+	go-svg.c		\
 	go-line.c		\
 	go-locale.c		\
 	go-marker.c		\
@@ -46,6 +47,7 @@ libgoffice_utils_la_HEADERS = 	\
 	go-gradient.h		\
 	go-image.h		\
 	go-pixbuf.h		\
+	go-svg.h		\
 	go-line.h		\
 	go-locale.h		\
 	go-marker.h		\
diff --git a/goffice/utils/go-image.c b/goffice/utils/go-image.c
index ff16327..929a7e4 100644
--- a/goffice/utils/go-image.c
+++ b/goffice/utils/go-image.c
@@ -80,9 +80,7 @@ go_image_format_to_mime (char const *format)
 {
 	char *ret = NULL;
  	guint i;
-#ifdef GOFFICE_WITH_GTK
 	GSList *ptr, *pixbuf_fmts;
-#endif
 	static const char* const formats[] = {
 		"svg", "image/svg,image/svg+xml",
 		"wmf", "image/x-wmf",
@@ -99,7 +97,6 @@ go_image_format_to_mime (char const *format)
 		if (strcmp (format, formats[i]) == 0)
 			return g_strdup (formats[i + 1]);
 
-#ifdef GOFFICE_WITH_GTK
 	/* Not a format we have special knowledge about - ask gdk-pixbuf */
 	pixbuf_fmts = gdk_pixbuf_get_formats ();
 	for (ptr = pixbuf_fmts; ptr != NULL; ptr = ptr->next) {
@@ -116,7 +113,6 @@ go_image_format_to_mime (char const *format)
 		}
 	}
 	g_slist_free (pixbuf_fmts);
-#endif
 
 	return ret;
 }
@@ -143,7 +139,6 @@ static GOImageFormatInfo const image_format_infos[GO_IMAGE_FORMAT_UNKNOWN] = {
 static void
 go_image_build_pixbuf_format_infos (void)
 {
-#ifdef GOFFICE_WITH_GTK
 	GdkPixbufFormat *fmt;
 	GSList *l, *pixbuf_fmts;
 	GOImageFormatInfo *format_info;
@@ -190,7 +185,6 @@ go_image_build_pixbuf_format_infos (void)
 	}
 
 	g_slist_free (pixbuf_fmts);
-#endif /* GOFFICE_WITH_GTK */
 	pixbuf_format_done = TRUE;
 }
 
@@ -281,64 +275,13 @@ go_image_get_formats_with_pixbuf_saver (void)
 
 static GObjectClass *parent_klass;
 
-struct _GOImage {
-	GObject parent;
-	guint8 *data;
-	guint width, height, rowstride;
-	gboolean target_cairo;
-	cairo_t *cairo;
-#ifdef GOFFICE_WITH_GTK
-	GdkPixbuf *pixbuf, *thumbnail;
-#else
-	void *pixbuf;
-#endif
-	char *name;
-};
-
 enum {
 	IMAGE_PROP_0,
 	IMAGE_PROP_WIDTH,
-	IMAGE_PROP_HEIGHT,
-#ifdef GOFFICE_WITH_GTK
-	IMAGE_PROP_PIXBUF,
-#endif
+	IMAGE_PROP_HEIGHT
 };
 
 static void
-pixbuf_to_cairo (GOImage *image)
-{
-#ifdef GOFFICE_WITH_GTK
-	unsigned char *src, *dst;
-
-	g_return_if_fail (GO_IS_IMAGE (image) && image->data && image->pixbuf);
-
-	src = gdk_pixbuf_get_pixels (image->pixbuf);
-	dst = image->data;
-
-	g_return_if_fail (gdk_pixbuf_get_rowstride (image->pixbuf) == (int) image->rowstride);
-
-	go_cairo_convert_data_from_pixbuf (dst, src, image->width, image->height, image->rowstride);
-#endif
-}
-
-#ifdef GOFFICE_WITH_GTK
-static void
-cairo_to_pixbuf (GOImage *image)
-{
-	unsigned char *src, *dst;
-
-	g_return_if_fail (GO_IS_IMAGE (image) && image->data && image->pixbuf);
-
-	dst = gdk_pixbuf_get_pixels (image->pixbuf);
-	src = image->data;
-
-	g_return_if_fail (gdk_pixbuf_get_rowstride (image->pixbuf) == (int) image->rowstride);
-
-	go_cairo_convert_data_to_pixbuf (dst, src, image->width, image->height, image->rowstride);
-}
-#endif
-
-static void
 go_image_set_property (GObject *obj, guint param_id,
 		       GValue const *value, GParamSpec *pspec)
 {
@@ -361,46 +304,14 @@ go_image_set_property (GObject *obj, guint param_id,
 			size_changed = TRUE;
 		}
 		break;
-#ifdef GOFFICE_WITH_GTK
-	case IMAGE_PROP_PIXBUF: {
-		GdkPixbuf *pixbuf = GDK_PIXBUF (g_value_get_object (value));
-		if (!GDK_IS_PIXBUF (pixbuf))
-			break;
-		if (!gdk_pixbuf_get_has_alpha (pixbuf))
-			pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
-		else
-			g_object_ref (pixbuf);
-		if (image->pixbuf)
-			g_object_unref (image->pixbuf);
-		image->pixbuf = pixbuf;
-		g_free (image->data);
-		image->data = NULL;
-		image->width = gdk_pixbuf_get_width (pixbuf);
-		image->height = gdk_pixbuf_get_height (pixbuf);
-		image->rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-		image->target_cairo = FALSE;
-		if (image->thumbnail) {
-			g_object_unref (image->thumbnail);
-			image->thumbnail = NULL;
-		}
-	}
-		break;
-#endif
 
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
 		return; /* NOTE : RETURN */
 	}
 
 	if (size_changed) {
-		if (image->pixbuf) {
-			g_object_unref (image->pixbuf);
-			image->pixbuf = NULL;
-		}
 		g_free (image->data);
-		/* GOImage only supports pixbuf with alpha values at the moment */
-		image->rowstride = image->width * 4;
-		image->data = g_new0 (guint8, image->height * image->rowstride);
-		image->target_cairo = TRUE;
+		image->data = NULL;
 	}
 }
 
@@ -417,15 +328,6 @@ go_image_get_property (GObject *obj, guint param_id,
 	case IMAGE_PROP_HEIGHT:
 		g_value_set_uint (value, image->height);
 		break;
-#ifdef GOFFICE_WITH_GTK
-	case IMAGE_PROP_PIXBUF:
-		if (image->target_cairo && image->pixbuf) {
-			cairo_to_pixbuf (image);
-			image->target_cairo = FALSE;
-		}
-		g_value_set_object (value, image->pixbuf);
-		break;
-#endif
 
 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
 		return; /* NOTE : RETURN */
@@ -437,20 +339,14 @@ go_image_finalize (GObject *obj)
 {
 	GOImage *image = GO_IMAGE (obj);
 	g_free (image->data);
-#ifdef GOFFICE_WITH_GTK
-	if (image->pixbuf)
-		g_object_unref (image->pixbuf);
 	if (image->thumbnail)
 		g_object_unref (image->thumbnail);
-#endif
 	g_free (image->name);
 	(parent_klass->finalize) (obj);
 }
 
-typedef GObjectClass GOImageClass;
-
 static void
-go_image_class_init (GOImageClass *klass)
+go_image_class_init (GObjectClass *klass)
 {
 	klass->finalize = go_image_finalize;
 	klass->set_property = go_image_set_property;
@@ -464,164 +360,90 @@ go_image_class_init (GOImageClass *klass)
 					 g_param_spec_uint ("height", _("Height"),
 							    _("Image height in pixels"),
 							    0, G_MAXUINT16, 0, G_PARAM_READWRITE));
-#ifdef GOFFICE_WITH_GTK
-	g_object_class_install_property (klass, IMAGE_PROP_PIXBUF,
-					 g_param_spec_object ("pixbuf", _("Pixbuf"),
-							      _("GdkPixbuf object from which the GOImage is built"),
-							      GDK_TYPE_PIXBUF, G_PARAM_READWRITE));
-#endif
 }
 
-GSF_CLASS (GOImage, go_image,
-	   go_image_class_init, NULL,
-	   G_TYPE_OBJECT)
+GSF_CLASS_FULL (GOImage, go_image,
+		NULL, NULL,
+		go_image_class_init, NULL, NULL,
+		G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
 
-cairo_t *
-go_image_get_cairo (GOImage *image)
+void
+go_image_draw (GOImage *image, cairo_t *cr)
 {
-	cairo_surface_t *surface ;
-	cairo_t *cairo;
-
-	g_return_val_if_fail (GO_IS_IMAGE (image), NULL);
-	if (image->data == NULL && image->pixbuf == NULL)
-		return NULL;
-	if (image->data == NULL) {
-		/* image built from a pixbuf */
-		image->data = g_new0 (guint8, image->height * image->rowstride);
-	}
-	if (!image->target_cairo) {
-		pixbuf_to_cairo (image);
-		image->target_cairo = TRUE;
-	}
-	surface = cairo_image_surface_create_for_data (
-						       image->data,
-						       CAIRO_FORMAT_ARGB32,
-						       image->width, image->height,
-						       image->rowstride);
-	cairo = cairo_create (surface);
-	cairo_surface_destroy (surface);
-	image->target_cairo = TRUE;
-	return cairo;
+	g_return_if_fail (GO_IS_IMAGE (image));
+	((GOImageClass *) G_OBJECT_GET_CLASS (image))->draw (image, cr);
 }
 
-static void
-cb_surface_destroyed (void *data)
-{
-	GOImage *image = GO_IMAGE (data);
-	/* We no longer need image->data.  */
-	g_object_unref (image);
-}
+#define GO_THUMBNAIL_SIZE 64
 
-/**
- * go_image_create_cairo_pattern:
- * @image: a GOImage.
- *
- * returns: a cairo_pattern usable for cairo_set_source.
- *
- * Note: this function has lifespan issues.  The resulting pattern in only
- * valid until (a) a pixbuf is set for the, or (b) a pixbuf is _read_ from
- * the image.  In either of these cases, the pattern must have been
- * destroyed beforehand.  In particular, if the pattern has been attached
- * to a surface, that surface must either be finished itself, or have had
- * a new pattern attached.  See #632439.
- */
-cairo_pattern_t *
-go_image_create_cairo_pattern (GOImage *image)
-{
-	cairo_surface_t *surface ;
-	cairo_pattern_t *pat;
-	static const cairo_user_data_key_t key;
-
-	g_return_val_if_fail (GO_IS_IMAGE (image), NULL);
-	if (image->data == NULL && image->pixbuf == NULL)
-		return NULL;
-	if (image->data == NULL) {
-		/* image built from a pixbuf */
-		image->data = g_new0 (guint8, image->height * image->rowstride);
-	}
-	if (!image->target_cairo) {
-		pixbuf_to_cairo (image);
-		image->target_cairo = TRUE;
-	}
-	surface = cairo_image_surface_create_for_data
-		(image->data,
-		 CAIRO_FORMAT_ARGB32,
-		 image->width, image->height,
-		 image->rowstride);
-	g_object_ref (image);
-	cairo_surface_set_user_data (surface, &key,
-				     image, cb_surface_destroyed);
-	pat = cairo_pattern_create_for_surface (surface);
-	cairo_surface_destroy (surface);
-	return pat;
-}
-
-#ifdef GOFFICE_WITH_GTK
-GOImage *
-go_image_new_from_pixbuf (GdkPixbuf *pixbuf)
+GdkPixbuf const *
+go_image_get_thumbnail (GOImage *image)
 {
-	return g_object_new (GO_TYPE_IMAGE, "pixbuf", pixbuf, NULL);
+	g_return_val_if_fail (image != NULL, NULL);
+	if (image->thumbnail == NULL)
+		image->thumbnail = go_image_get_scaled_pixbuf (image, GO_THUMBNAIL_SIZE, GO_THUMBNAIL_SIZE);
+	return image->thumbnail;
 }
-
 GdkPixbuf *
 go_image_get_pixbuf (GOImage *image)
 {
-	g_return_val_if_fail (image != NULL, NULL);
-	if (!image->pixbuf) {
-		if (image->width == 0 || image->height == 0 || image->data == NULL)
-			return NULL;
-		image->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
-								image->width, image->height);
-	}
-	if (image->target_cairo) {
-		cairo_to_pixbuf (image);
-		image->target_cairo = FALSE;
-	}
-	return image->pixbuf;
+	return ((GOImageClass *) G_OBJECT_GET_CLASS (image))->get_pixbuf (image);
 }
 
-#define THUMBNAIL_SIZE 64
 GdkPixbuf *
-go_image_get_thumbnail (GOImage *image)
+go_image_get_scaled_pixbuf (GOImage *image, int width, int height)
 {
-	g_return_val_if_fail (image != NULL, NULL);
-	if (!image->pixbuf)
-		go_image_get_pixbuf (image);
-	if (!image->pixbuf)
-		return NULL;
-	if (!image->thumbnail) {
-		int w, h;
-		if (image->width <= THUMBNAIL_SIZE && image->height <= THUMBNAIL_SIZE)
-			return image->pixbuf;
-		if (image->width >= image->height) {
-			w = THUMBNAIL_SIZE;
-			h = THUMBNAIL_SIZE * image->height / image->width;
+	if (image->width > width || image->height > height) {
+		if (image->width * height > image->height * width) {
+			height = width * image->height / image->width;
 		} else {
-			h = THUMBNAIL_SIZE;
-			w = THUMBNAIL_SIZE * image->width / image->height;
+			width = height * image->width / image->height;
 		}
-		image->thumbnail = gdk_pixbuf_scale_simple (image->pixbuf, w, h, GDK_INTERP_HYPER);
-	}
-	return image->thumbnail;
+	} else
+		return ((GOImageClass *) G_OBJECT_GET_CLASS (image))->get_pixbuf (image);
+	return ((GOImageClass *) G_OBJECT_GET_CLASS (image))->get_scaled_pixbuf (image, width, height);
 }
-#endif
 
 GOImage *
 go_image_new_from_file (const char *filename, GError **error)
 {
-#ifdef GOFFICE_WITH_GTK
-	GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, error);
-	if (pixbuf) {
-		GOImage *image = g_object_new (GO_TYPE_IMAGE,
-					       "pixbuf", pixbuf,
-					       NULL);
-		g_object_unref (pixbuf);
-		image->target_cairo = FALSE;
-		return image;
+	char *mime, *name;
+	GOImageFormat format;
+
+	if (!filename) {
+		return NULL;
+	}
+	mime = go_get_mime_type (filename);
+	if (!mime) {
+		return NULL;
+	}
+	name = go_mime_to_image_format (mime);
+	g_free (mime);
+	if (!name) {
+		return NULL;
+	}
+	format = go_image_get_format_from_name (name);
+	g_free (name);
+	switch (format) {
+	case GO_IMAGE_FORMAT_SVG:
+		return GO_IMAGE (go_svg_new_from_file (filename, error));
+	case GO_IMAGE_FORMAT_PDF:
+	case GO_IMAGE_FORMAT_PS:
+	case GO_IMAGE_FORMAT_EMF:
+	case GO_IMAGE_FORMAT_WMF:
+	case GO_IMAGE_FORMAT_EPS:
+		break;
+	case GO_IMAGE_FORMAT_UNKNOWN:
+		break;
+	default: {
+		GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, error);
+		if (pixbuf) {
+			GOImage *image = (GOImage *) go_pixbuf_new_from_pixbuf (pixbuf);
+			g_object_unref (pixbuf);
+			return image;
+		}
+	}
 	}
-#else
-	g_warning ("go_image_new_from_file not implemented!");
-#endif
 	return NULL;
 }
 
@@ -632,34 +454,6 @@ go_image_get_pixels (GOImage *image)
 	return image->data;
 }
 
-int
-go_image_get_rowstride (GOImage *image)
-{
-	g_return_val_if_fail (image, 0);
-	return image->rowstride;
-}
-
-void
-go_image_fill (GOImage *image, GOColor color)
-{
-	guint32 val;
-	guint8 *dst;
-	unsigned i, j;
-	g_return_if_fail (image);
-
-	dst = image->data;
-	if (image->target_cairo)
-		val = (GO_COLOR_UINT_R (color) << 8) + (GO_COLOR_UINT_G (color) << 16)
-			+ (GO_COLOR_UINT_B (color) << 24) + GO_COLOR_UINT_A (color);
-	else
-		val = color;
-	for (i = 0; i < image->height; i++) {
-		for (j = 0; j < image->width; j++)
-			*((guint32*) dst) = val;
-		dst += image->rowstride - image->width * 4;
-	}
-}
-
 void
 go_image_set_name (GOImage *image, char const *name)
 {
@@ -675,40 +469,15 @@ go_image_get_name (GOImage *image)
 }
 
 gboolean
-go_image_same_pixbuf (GOImage *first, GOImage *second)
+go_image_differ (GOImage *first, GOImage *second)
 {
-#ifdef GOFFICE_WITH_GTK
-	void *pixels1, *pixels2;
-	int size;
 	g_return_val_if_fail (GO_IS_IMAGE (first), FALSE);
 	g_return_val_if_fail (GO_IS_IMAGE (second), FALSE);
-	if (!first->pixbuf)
-		go_image_get_pixbuf (first);
-	if (!second->pixbuf)
-		go_image_get_pixbuf (second);
-	if (!first->pixbuf || !second->pixbuf)
-		return FALSE;
-	if (gdk_pixbuf_get_n_channels (first->pixbuf) != gdk_pixbuf_get_n_channels (second->pixbuf))
-		return FALSE;
-	if (gdk_pixbuf_get_colorspace (first->pixbuf) != gdk_pixbuf_get_colorspace (second->pixbuf))
-		return FALSE;
-	if (gdk_pixbuf_get_bits_per_sample (first->pixbuf) != gdk_pixbuf_get_bits_per_sample (second->pixbuf))
-		return FALSE;
-	if (gdk_pixbuf_get_has_alpha (first->pixbuf) != gdk_pixbuf_get_has_alpha (second->pixbuf))
-		return FALSE;
-	if (gdk_pixbuf_get_width (first->pixbuf) != gdk_pixbuf_get_width (second->pixbuf))
-		return FALSE;
-	if (gdk_pixbuf_get_height (first->pixbuf) != gdk_pixbuf_get_height (second->pixbuf))
-		return FALSE;
-	if (gdk_pixbuf_get_rowstride (first->pixbuf) != gdk_pixbuf_get_rowstride (second->pixbuf))
-		return FALSE;
-	pixels1 = gdk_pixbuf_get_pixels (first->pixbuf);
-	pixels2 = gdk_pixbuf_get_pixels (second->pixbuf);
-	size = gdk_pixbuf_get_rowstride (first->pixbuf) * gdk_pixbuf_get_height (first->pixbuf);
-	return !memcmp (pixels1, pixels2, size);
-#else
-	return FALSE;
-#endif
+	if (G_OBJECT_TYPE (first) != G_OBJECT_TYPE (second))
+		return TRUE;
+	if (first->width != second->width || first->height != second->height)
+		return TRUE;
+	return ((GOImageClass *) G_OBJECT_GET_CLASS (first))->differ (first, second);
 }
 
 void
@@ -717,11 +486,10 @@ go_image_save (GOImage *image, GsfXMLOut *output)
 	g_return_if_fail (GO_IS_IMAGE (image) && image->name);
 	gsf_xml_out_start_element (output, "GOImage");
 	gsf_xml_out_add_cstr (output, "name", image->name);
+	gsf_xml_out_add_cstr (output, "type", G_OBJECT_TYPE_NAME (image));
 	gsf_xml_out_add_int (output, "width", image->width);
 	gsf_xml_out_add_int (output, "height", image->height);
-	gsf_xml_out_add_int (output, "rowstride", image->rowstride);
-	gsf_xml_out_add_base64 (output, NULL,
-			go_image_get_pixels (image), image->height * image->rowstride);
+	((GOImageClass *) G_OBJECT_GET_CLASS (image))->save (image, output);
 	gsf_xml_out_end_element (output);
 }
 
@@ -735,16 +503,23 @@ go_image_load_attrs (GOImage *image, GsfXMLIn *xin, xmlChar const **attrs)
 			image->width = strtol (attr[1], NULL, 10);
 		else if (0 == strcmp (attr[0], "height"))
 			image->height= strtol (attr[1], NULL, 10);
-		else if (0 == strcmp (attr[0], "rowstride"))
-			image->rowstride = strtol (attr[1], NULL, 10);
+		else
+			((GOImageClass *) G_OBJECT_GET_CLASS (image))->load_attr (image, attr[0], attr[1]);
 }
 
 void
 go_image_load_data (GOImage *image, GsfXMLIn *xin)
 {
-	int length;
-	length = gsf_base64_decode_simple (xin->content->str, strlen(xin->content->str));
-	image->data = g_memdup (xin->content->str, length);
-	image->target_cairo = TRUE;
+	((GOImageClass *) G_OBJECT_GET_CLASS (image))->load_data (image, xin);
 }
 
+void
+_go_image_changed (GOImage *image, double width, double height)
+{
+	image->width = width;
+	image->height = height;
+	if (image->thumbnail) {
+		g_object_unref (image->thumbnail);
+		image->thumbnail = NULL;
+	}
+}
diff --git a/goffice/utils/go-image.h b/goffice/utils/go-image.h
index 466109b..b44619b 100644
--- a/goffice/utils/go-image.h
+++ b/goffice/utils/go-image.h
@@ -68,29 +68,49 @@ GSList 			*go_image_get_formats_with_pixbuf_saver (void);
 
 GType go_image_get_type (void);
 
-cairo_t 	*go_image_get_cairo 		(GOImage *image);
-cairo_pattern_t *go_image_create_cairo_pattern 	(GOImage *image);
+struct _GOImage {
+	GObject parent;
+	guint8 *data;
+	double width, height;
+	GdkPixbuf *thumbnail;
+	char *name;
+};
+
+typedef struct {
+	GObjectClass parent_klass;
+
+	GdkPixbuf *(*get_pixbuf) (GOImage *image);
+	GdkPixbuf *(*get_scaled_pixbuf) (GOImage *image, int width, int height);
+	gboolean (*same_content) (GOImage *img1, GOImage *img2);
+	void (*save) (GOImage *image, GsfXMLOut *output);
+	void (*load_attr) (GOImage *image, xmlChar const *attr_name, xmlChar const *attr_value);
+	void (*load_data) (GOImage *image, GsfXMLIn *xin);
+	void (*draw) (GOImage *image, cairo_t *cr);
+	gboolean (*differ) (GOImage *first, GOImage *second);
+} GOImageClass;
 
-#ifdef GOFFICE_WITH_GTK
 GOImage 	*go_image_new_from_pixbuf 	(GdkPixbuf *pixbuf);
-GdkPixbuf 	*go_image_get_pixbuf 		(GOImage *image);
-GdkPixbuf 	*go_image_get_thumbnail		(GOImage *image);
-#endif
+GdkPixbuf const *go_image_get_thumbnail		(GOImage *image);
+GdkPixbuf       *go_image_get_pixbuf		(GOImage *image);
+GdkPixbuf       *go_image_get_scaled_pixbuf	(GOImage *image, int width, int height);
+void		 go_image_draw			(GOImage *image, cairo_t *cr);
 
 GOImage 	*go_image_new_from_file 	(const char *filename, GError **error);
 guint8 		*go_image_get_pixels 		(GOImage *image);
-int 		 go_image_get_rowstride 	(GOImage *image);
 void 		 go_image_fill 			(GOImage *image, GOColor color);
 
 void		 go_image_set_name		(GOImage *image, char const *name);
 char const	*go_image_get_name 		(GOImage *image);
 
-gboolean	 go_image_same_pixbuf		(GOImage *first, GOImage *second);
+gboolean	 go_image_differ		(GOImage *first, GOImage *second);
 
 void		 go_image_save			(GOImage *image, GsfXMLOut *output);
 void		 go_image_load_attrs		(GOImage *image, GsfXMLIn *xin, xmlChar const **attrs);
 void		 go_image_load_data		(GOImage *image, GsfXMLIn *xin);
 
+/* Protected */
+void		 _go_image_changed		(GOImage *image, double width, double height);
+
 G_END_DECLS
 
 #endif /* GO_IMAGE_H */
diff --git a/goffice/utils/go-persist.c b/goffice/utils/go-persist.c
index 422ce07..246be4b 100644
--- a/goffice/utils/go-persist.c
+++ b/goffice/utils/go-persist.c
@@ -41,13 +41,6 @@ go_persist_get_type (void)
 	return go_persist_type;
 }
 
-gboolean
-go_persist_dom_load (GOPersist *gp, xmlNode *node)
-{
-	g_return_val_if_fail (GO_IS_PERSIST (gp), FALSE);
-	return GO_PERSIST_GET_CLASS (gp)->dom_load (gp, node);
-}
-
 void
 go_persist_sax_save (GOPersist const *gp, GsfXMLOut *output)
 {
diff --git a/goffice/utils/go-persist.h b/goffice/utils/go-persist.h
index 556d260..e85d3ee 100644
--- a/goffice/utils/go-persist.h
+++ b/goffice/utils/go-persist.h
@@ -32,7 +32,6 @@ typedef struct _GOPersist GOPersist;
 typedef struct {
 	GTypeInterface base;
 
-	gboolean (*dom_load) (GOPersist *gp, xmlNode *node);
 	void	 (*prep_sax) (GOPersist *gp, GsfXMLIn *xin, xmlChar const **attrs);
 	void     (*sax_save) (GOPersist const *gp, GsfXMLOut *output);
 } GOPersistClass;
@@ -46,7 +45,6 @@ typedef struct {
 
 GType go_persist_get_type (void);
 
-gboolean go_persist_dom_load (GOPersist *gp, xmlNode *node);
 void     go_persist_sax_save (GOPersist const *gp, GsfXMLOut *output);
 void	 go_persist_prep_sax (GOPersist *gp,
 			       GsfXMLIn *xin, xmlChar const **attrs);
diff --git a/goffice/utils/go-pixbuf.c b/goffice/utils/go-pixbuf.c
index 44c228a..6a9a234 100644
--- a/goffice/utils/go-pixbuf.c
+++ b/goffice/utils/go-pixbuf.c
@@ -3,6 +3,7 @@
  * go-pixbuf.c
  *
  * Copyright (C) 2000-2004 Jody Goldberg (jody gnome org)
+ * Copyright (C) 2011 Jean Brefort (jean brefort normalesup org)
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -23,6 +24,10 @@
 #include "go-pixbuf.h"
 
 #include <goffice/goffice-priv.h>
+#include <string.h>
+#include <gsf/gsf-utils.h>
+#include <gsf/gsf-impl-utils.h>
+#include <glib/gi18n-lib.h>
 
 /**
  * go_gdk_pixbuf_intelligent_scale:
@@ -206,3 +211,268 @@ go_gdk_pixbuf_tile (GdkPixbuf const *src, guint w, guint h)
 
 	return dst;
 }
+
+/* GOPixbuf implementation */
+
+
+struct _GOPixbuf {
+	GOImage parent;
+	unsigned rowstride;
+	GdkPixbuf *pixbuf;
+	cairo_surface_t *surface;
+};
+
+typedef GOImageClass GOPixbufClass;
+
+static GObjectClass *parent_klass;
+
+enum {
+	PIXBUF_PROP_0,
+	PIXBUF_PROP_PIXBUF
+};
+
+static void
+pixbuf_to_cairo (GOPixbuf *pixbuf)
+{
+	unsigned char *src, *dst;
+	GOImage *image = GO_IMAGE (pixbuf);
+
+	g_return_if_fail (GO_IS_PIXBUF (pixbuf) && image->data && pixbuf->pixbuf);
+
+	src = gdk_pixbuf_get_pixels (pixbuf->pixbuf);
+	dst = image->data;
+
+	g_return_if_fail (gdk_pixbuf_get_rowstride (pixbuf->pixbuf) == (int) pixbuf->rowstride);
+
+	go_cairo_convert_data_from_pixbuf (dst, src, image->width, image->height, pixbuf->rowstride);
+}
+
+static void
+cairo_to_pixbuf (GOPixbuf *pixbuf)
+{
+	unsigned char *src, *dst;
+	GOImage *image = GO_IMAGE (pixbuf);
+
+	g_return_if_fail (GO_IS_PIXBUF (pixbuf) && image->data && pixbuf->pixbuf);
+
+	dst = gdk_pixbuf_get_pixels (pixbuf->pixbuf);
+	src = image->data;
+
+	g_return_if_fail (gdk_pixbuf_get_rowstride (pixbuf->pixbuf) == (int) pixbuf->rowstride);
+
+	go_cairo_convert_data_to_pixbuf (dst, src, image->width, image->height, pixbuf->rowstride);
+}
+
+static void
+go_pixbuf_save (GOImage *image, GsfXMLOut *output)
+{
+	GOPixbuf *pixbuf;
+	g_return_if_fail (GO_IS_PIXBUF (image));
+	pixbuf = GO_PIXBUF (image);
+	gsf_xml_out_add_int (output, "rowstride", pixbuf->rowstride);
+	gsf_xml_out_add_base64 (output, NULL,
+			image->data, image->height * pixbuf->rowstride);
+}
+
+static void
+go_pixbuf_load_attr (GOImage *image, xmlChar const *attr_name, xmlChar const *attr_value)
+{
+	GOPixbuf *pixbuf = GO_PIXBUF (image);
+	g_return_if_fail (pixbuf);
+	if (!strcmp (attr_name, "rowstride"))
+		pixbuf->rowstride = strtol (attr_value, NULL, 10);
+}
+
+static void
+go_pixbuf_load_data (GOImage *image, GsfXMLIn *xin)
+{
+	int length;
+	length = gsf_base64_decode_simple (xin->content->str, strlen(xin->content->str));
+	image->data = g_memdup (xin->content->str, length);
+}
+
+static void
+go_pixbuf_draw (GOImage *image, cairo_t *cr)
+{
+	GOPixbuf *pixbuf = GO_PIXBUF (image);
+	g_return_if_fail (pixbuf);
+	if (pixbuf->surface == NULL) {
+		if (image->data == NULL) {
+			/* image built from a pixbuf */
+			image->data = g_new0 (guint8, image->height * pixbuf->rowstride);
+			pixbuf_to_cairo (pixbuf);
+		}
+		pixbuf->surface = cairo_image_surface_create_for_data (image->data,
+			                                               CAIRO_FORMAT_ARGB32,
+			                                               image->width,
+			                                               image->height,
+			                                               pixbuf->rowstride);
+	}
+	cairo_save (cr);
+	cairo_set_source_surface (cr, pixbuf->surface, 0., 0.);
+	cairo_rectangle (cr, 0., 0., image->width, image->height);
+	cairo_fill (cr);
+	cairo_restore (cr);
+}
+
+static GdkPixbuf *
+go_pixbuf_get_pixbuf (GOImage *image)
+{
+	GOPixbuf *pixbuf = GO_PIXBUF (image);
+	g_return_val_if_fail (pixbuf, NULL);
+	if (!pixbuf->pixbuf) {
+		if (image->width == 0 || image->height == 0 || image->data == NULL)
+			return NULL;
+		pixbuf->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+								image->width, image->height);
+		cairo_to_pixbuf (pixbuf);
+	}
+	return g_object_ref (pixbuf->pixbuf);
+}
+
+static GdkPixbuf *
+go_pixbuf_get_scaled_pixbuf (GOImage *image, int width, int height)
+{
+	GOPixbuf *pixbuf = GO_PIXBUF (image);
+	g_return_val_if_fail (pixbuf, NULL);
+	if (!pixbuf->pixbuf) {
+		if (image->width == 0 || image->height == 0 || image->data == NULL)
+			return NULL;
+		pixbuf->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
+								image->width, image->height);
+		cairo_to_pixbuf (pixbuf);
+	}
+	return gdk_pixbuf_scale_simple (pixbuf->pixbuf, width, height, GDK_INTERP_HYPER);
+}
+
+static gboolean
+go_pixbuf_differ (GOImage *first, GOImage *second)
+{
+	void *pixels1, *pixels2;
+	int size;
+	GOPixbuf *pfirst = GO_PIXBUF (first), *psecond = GO_PIXBUF (second);
+	if (!pfirst->pixbuf)
+		go_pixbuf_get_pixbuf (first);
+	if (!psecond->pixbuf)
+		go_pixbuf_get_pixbuf (second);
+	if (!pfirst->pixbuf || !psecond->pixbuf)
+		return TRUE; /* this should not happen */
+	if (gdk_pixbuf_get_n_channels (pfirst->pixbuf) != gdk_pixbuf_get_n_channels (psecond->pixbuf))
+		return TRUE;
+	if (gdk_pixbuf_get_colorspace (pfirst->pixbuf) != gdk_pixbuf_get_colorspace (psecond->pixbuf))
+		return TRUE;
+	if (gdk_pixbuf_get_bits_per_sample (pfirst->pixbuf) != gdk_pixbuf_get_bits_per_sample (psecond->pixbuf))
+		return TRUE;
+	if (gdk_pixbuf_get_has_alpha (pfirst->pixbuf) != gdk_pixbuf_get_has_alpha (psecond->pixbuf))
+		return TRUE;
+	if (gdk_pixbuf_get_width (pfirst->pixbuf) != gdk_pixbuf_get_width (psecond->pixbuf))
+		return TRUE;
+	if (gdk_pixbuf_get_height (pfirst->pixbuf) != gdk_pixbuf_get_height (psecond->pixbuf))
+		return TRUE;
+	if (gdk_pixbuf_get_rowstride (pfirst->pixbuf) != gdk_pixbuf_get_rowstride (psecond->pixbuf))
+		return TRUE;
+	pixels1 = gdk_pixbuf_get_pixels (pfirst->pixbuf);
+	pixels2 = gdk_pixbuf_get_pixels (psecond->pixbuf);
+	size = gdk_pixbuf_get_rowstride (pfirst->pixbuf) * gdk_pixbuf_get_height (pfirst->pixbuf);
+	return memcmp (pixels1, pixels2, size);
+}
+
+static void
+go_pixbuf_set_property (GObject *obj, guint param_id,
+		       GValue const *value, GParamSpec *pspec)
+{
+	GOPixbuf *pixbuf = GO_PIXBUF (obj);
+	GOImage *image = GO_IMAGE (obj);
+
+	switch (param_id) {
+	case PIXBUF_PROP_PIXBUF: {
+		GdkPixbuf *pix = GDK_PIXBUF (g_value_get_object (value));
+		if (!GDK_IS_PIXBUF (pix))
+			break;
+		if (!gdk_pixbuf_get_has_alpha (pix))
+			pix = gdk_pixbuf_add_alpha (pix, FALSE, 0, 0, 0);
+		else
+			g_object_ref (pix);
+		if (pixbuf->pixbuf)
+			g_object_unref (pixbuf->pixbuf);
+		pixbuf->pixbuf = pix;
+		g_free (image->data); /* this should be in GOPixbuf */
+		image->data = NULL; /* this should be in GOPixbuf */
+		_go_image_changed (image, gdk_pixbuf_get_width (pix), gdk_pixbuf_get_height (pix));
+		pixbuf->rowstride = gdk_pixbuf_get_rowstride (pix); /* this should be in GOPixbuf */
+	}
+		break;
+
+	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+		return; /* NOTE : RETURN */
+	}
+}
+
+static void
+go_pixbuf_get_property (GObject *obj, guint param_id,
+		       GValue *value, GParamSpec *pspec)
+{
+	GOPixbuf *pixbuf = GO_PIXBUF (obj);
+
+	switch (param_id) {
+	case PIXBUF_PROP_PIXBUF:
+		g_value_set_object (value, pixbuf->pixbuf);
+		break;
+
+	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
+		return; /* NOTE : RETURN */
+	}
+}
+
+static void
+go_pixbuf_finalize (GObject *obj)
+{
+	GOPixbuf *pixbuf = GO_PIXBUF (obj);
+	if (pixbuf->pixbuf)
+		g_object_unref (pixbuf->pixbuf);
+	if (pixbuf->surface)
+		cairo_surface_destroy (pixbuf->surface);
+	(parent_klass->finalize) (obj);
+}
+
+static void
+go_pixbuf_class_init (GObjectClass *klass)
+{
+	GOImageClass *image_klass = (GOImageClass *) klass;
+
+	klass->finalize = go_pixbuf_finalize;
+	klass->set_property = go_pixbuf_set_property;
+	klass->get_property = go_pixbuf_get_property;
+	parent_klass = g_type_class_peek_parent (klass);
+
+	image_klass->save = go_pixbuf_save;
+	image_klass->load_attr = go_pixbuf_load_attr;
+	image_klass->load_data = go_pixbuf_load_data;
+	image_klass->get_pixbuf = go_pixbuf_get_pixbuf;
+	image_klass->get_scaled_pixbuf = go_pixbuf_get_scaled_pixbuf;
+	image_klass->draw = go_pixbuf_draw;
+	image_klass->differ = go_pixbuf_differ;
+
+	g_object_class_install_property (klass, PIXBUF_PROP_PIXBUF,
+					 g_param_spec_object ("pixbuf", _("Pixbuf"),
+							      _("GdkPixbuf object from which the GOPixbuf is built"),
+							      GDK_TYPE_PIXBUF, G_PARAM_READWRITE));
+}
+
+GSF_CLASS (GOPixbuf, go_pixbuf,
+	   go_pixbuf_class_init, NULL,
+	   GO_TYPE_IMAGE)
+
+
+GOPixbuf *
+go_pixbuf_new_from_pixbuf (GdkPixbuf *pixbuf)
+{
+	return g_object_new (GO_TYPE_PIXBUF, "pixbuf", pixbuf, NULL);
+}
+
+int
+go_pixbuf_get_rowstride (GOPixbuf *pixbuf)
+{
+	g_return_val_if_fail (GO_IS_PIXBUF (pixbuf), 0);
+	return pixbuf->rowstride;
+}
diff --git a/goffice/utils/go-pixbuf.h b/goffice/utils/go-pixbuf.h
index 3f8452f..524969d 100644
--- a/goffice/utils/go-pixbuf.h
+++ b/goffice/utils/go-pixbuf.h
@@ -20,6 +20,7 @@
 #ifndef GO_PIXBUF_H
 #define GO_PIXBUF_H
 
+#include <goffice/goffice.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 
 G_BEGIN_DECLS
@@ -31,6 +32,15 @@ GdkPixbuf 	*go_gdk_pixbuf_get_from_cache 	(char const *filename);
 GdkPixbuf	*go_gdk_pixbuf_tile		(GdkPixbuf const *src,
 						 guint w, guint h);
 
+#define GO_TYPE_PIXBUF	(go_pixbuf_get_type ())
+#define GO_PIXBUF(o)	(G_TYPE_CHECK_INSTANCE_CAST ((o), GO_TYPE_PIXBUF, GOPixbuf))
+#define GO_IS_PIXBUF(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_TYPE_PIXBUF))
+
+GType go_pixbuf_get_type (void);
+
+GOPixbuf	*go_pixbuf_new_from_pixbuf      (GdkPixbuf *pixbuf);
+int 		 go_pixbuf_get_rowstride 	(GOPixbuf *pixbuf);
+
 G_END_DECLS
 
 #endif
diff --git a/goffice/utils/go-style-prefs.ui b/goffice/utils/go-style-prefs.ui
index b065094..b457617 100644
--- a/goffice/utils/go-style-prefs.ui
+++ b/goffice/utils/go-style-prefs.ui
@@ -547,6 +547,7 @@
               <object class="GtkBox" id="vbox1">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
+                <property name="valign">start</property>
                 <property name="orientation">vertical</property>
                 <property name="spacing">6</property>
                 <child>
@@ -859,6 +860,7 @@
                         </child>
                         <child>
                           <object class="GtkButton" id="fill_image_select_picture">
+                            <property name="use_action_appearance">False</property>
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="receives_default">True</property>
@@ -933,6 +935,7 @@
                               <item translatable="yes">stretched</item>
                               <item translatable="yes">wallpaper</item>
                               <item translatable="yes">centered</item>
+                              <item translatable="yes">centered wallpaper</item>
                             </items>
                           </object>
                           <packing>
diff --git a/goffice/utils/go-style.c b/goffice/utils/go-style.c
index 6551a21..dc7b109 100644
--- a/goffice/utils/go-style.c
+++ b/goffice/utils/go-style.c
@@ -142,7 +142,7 @@ go_style_set_image_preview (GOImage *pix, StylePrefState *state)
 
 	w = go_gtk_builder_get_widget (state->gui, "fill_image_sample");
 
-	scaled = go_gdk_pixbuf_intelligent_scale (go_image_get_pixbuf (pix), HSCALE, VSCALE);
+	scaled = go_image_get_scaled_pixbuf (pix, HSCALE, VSCALE);
 	gtk_image_set_from_pixbuf (GTK_IMAGE (w), scaled);
 	g_object_unref (scaled);
 
@@ -1273,20 +1273,6 @@ static struct {
 };
 
 static gboolean
-bool_prop (xmlNode *node, char const *name, gboolean *res)
-{
-	char *str = xmlGetProp (node, name);
-	if (str != NULL) {
-		*res = g_ascii_tolower (*str) == 't' ||
-			g_ascii_tolower (*str) == 'y' ||
-			strtol (str, NULL, 0);
-		xmlFree (str);
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean
 bool_sax_prop (char const *name, char const *id, char const *val, gboolean *res)
 {
 	if (0 == strcmp (name, id)) {
@@ -1340,39 +1326,6 @@ image_tiling_as_str (GOImageType fstyle)
 }
 
 static void
-go_style_line_load (xmlNode *node, GOStyleLine *line)
-{
-	char *str;
-	gboolean tmp;
-
-	str = xmlGetProp (node, "dash");
-	if (str != NULL) {
-		line->dash_type = go_line_dash_from_str (str);
-		xmlFree (str);
-	}
-	if (bool_prop (node, "auto-dash", &tmp))
-		line->auto_dash = tmp;
-	str = xmlGetProp (node, "width");
-	if (str != NULL) {
-		line->width = g_strtod (str, NULL);
-		/* For compatibility with older graphs, when dash_type
-		 * didn't exist */
-		if (line->width < 0.) {
-			line->width = 0.;
-			line->dash_type = GO_LINE_NONE;
-		}
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, "color");
-	if (str != NULL) {
-		go_color_from_str (str, &line->color);
-		xmlFree (str);
-	}
-	if (bool_prop (node, "auto-color", &tmp))
-		line->auto_color = tmp;
-}
-
-static void
 go_style_line_sax_save (GsfXMLOut *output, char const *name,
 			 GOStyleLine const *line)
 {
@@ -1445,6 +1398,7 @@ go_style_fill_sax_save (GsfXMLOut *output, GOStyle const *style)
 		gsf_xml_out_add_cstr_unchecked (output, "type",
 			image_tiling_as_str (style->fill.image.type));
 		gsf_xml_out_add_cstr (output, "name", go_image_get_name (style->fill.image.image));
+		gsf_xml_out_add_cstr (output, "type-name", G_OBJECT_TYPE_NAME (style->fill.image.image));
 		go_doc_save_image ((GODoc *) g_object_get_data (G_OBJECT (gsf_xml_out_get_output (output)), "document"), go_image_get_name (style->fill.image.image));
 		gsf_xml_out_end_element (output);
 		break;
@@ -1455,153 +1409,6 @@ go_style_fill_sax_save (GsfXMLOut *output, GOStyle const *style)
 }
 
 static void
-go_style_gradient_load (xmlNode *node, GOStyle *style)
-{
-	char    *str = xmlGetProp (node, "direction");
-	if (str != NULL) {
-		style->fill.gradient.dir
-			= go_gradient_dir_from_str (str);
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, "start-color");
-	if (str != NULL) {
-		go_color_from_str (str, &style->fill.pattern.back);
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, "brightness");
-	if (str != NULL) {
-		go_style_set_fill_brightness (style, g_strtod (str, NULL));
-		xmlFree (str);
-	} else {
-		str = xmlGetProp (node, "end-color");
-		if (str != NULL) {
-			go_color_from_str (str, &style->fill.pattern.fore);
-			xmlFree (str);
-		}
-	}
-}
-
-static void
-go_style_image_load (xmlNode *node, GOStyle *style)
-{
-	char *str = xmlGetProp (node, "type");
-	if (str != NULL) {
-		style->fill.image.type = str_as_image_tiling (str);
-		xmlFree (str);
-	}
-	/* TODO: load the pixels */
-}
-
-static void
-go_style_fill_load (xmlNode *node, GOStyle *style)
-{
-	xmlNode *ptr;
-	gboolean tmp;
-	char    *str = xmlGetProp (node, "type");
-
-	if (str == NULL)
-		return;
-	style->fill.type = str_as_fill_style (str);
-	xmlFree (str);
-
-	style->fill.auto_type = FALSE;
-
-	if (bool_prop (node, "auto-type", &tmp))
-		style->fill.auto_type = tmp;
-	if (bool_prop (node, "is-auto", &tmp))
-		style->fill.auto_back = tmp;
-	if (bool_prop (node, "auto-fore", &tmp))
-		style->fill.auto_fore = tmp;
-
-	switch (style->fill.type) {
-	case GO_STYLE_FILL_PATTERN:
-		for (ptr = node->xmlChildrenNode ;
-		     ptr != NULL ; ptr = ptr->next) {
-			if (xmlIsBlankNode (ptr) || ptr->name == NULL)
-				continue;
-			if (strcmp (ptr->name, "pattern") == 0) {
-				str = xmlGetProp (ptr, "type");
-				if (str != NULL) {
-					style->fill.pattern.pattern
-						= go_pattern_from_str (str);
-					xmlFree (str);
-				}
-				str = xmlGetProp (ptr, "fore");
-				if (str != NULL) {
-					go_color_from_str (str, &style->fill.pattern.fore);
-					xmlFree (str);
-				}
-				str = xmlGetProp (ptr, "back");
-				if (str != NULL) {
-					go_color_from_str (str, &style->fill.pattern.back);
-					xmlFree (str);
-				}
-			}
-		}
-		break;
-	case GO_STYLE_FILL_GRADIENT:
-		for (ptr = node->xmlChildrenNode ;
-		     ptr != NULL ; ptr = ptr->next) {
-			if (xmlIsBlankNode (ptr) || ptr->name == NULL)
-				continue;
-			if (strcmp (ptr->name, "gradient") == 0)
-				go_style_gradient_load (ptr, style);
-		}
-		break;
-	case GO_STYLE_FILL_IMAGE:
-		for (ptr = node->xmlChildrenNode ;
-		     ptr != NULL ; ptr = ptr->next) {
-			if (xmlIsBlankNode (ptr) || ptr->name == NULL)
-				continue;
-			if (strcmp (ptr->name, "image") == 0) {
-				go_style_image_load (ptr, style);
-			}
-		}
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-go_style_marker_load (xmlNode *node, GOStyle *style)
-{
-	char *str;
-	GOColor c;
-	GOMarker *marker = go_marker_dup (style->marker.mark);
-
-	str = xmlGetProp (node, "shape");
-	if (str != NULL) {
-		style->marker.auto_shape = TRUE;
-		bool_prop (node, "auto-shape", &style->marker.auto_shape);
-		go_marker_set_shape (marker, go_marker_shape_from_str (str));
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, "outline-color");
-	if (str != NULL) {
-		style->marker.auto_outline_color = TRUE;
-		bool_prop (node, "auto-outline", &style->marker.auto_outline_color);
-		if (go_color_from_str (str, &c))
-			go_marker_set_outline_color (marker, c);
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, "fill-color");
-	if (str != NULL) {
-		style->marker.auto_fill_color = TRUE;
-		bool_prop (node, "auto-fill", &style->marker.auto_fill_color);
-		if (go_color_from_str (str, &c))
-			go_marker_set_fill_color (marker, c);
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, "size");
-	if (str != NULL) {
-		go_marker_set_size (marker, g_strtod (str, NULL));
-		xmlFree (str);
-	}
-	go_style_set_marker (style, marker);
-}
-
-static void
 go_style_marker_sax_save (GsfXMLOut *output, GOStyle const *style)
 {
 	gsf_xml_out_start_element (output, "marker");
@@ -1621,30 +1428,6 @@ go_style_marker_sax_save (GsfXMLOut *output, GOStyle const *style)
 }
 
 static void
-go_style_font_load (xmlNode *node, GOStyle *style)
-{
-	char *str;
-	gboolean tmp;
-
-	str = xmlGetProp (node, "color");
-	if (str != NULL) {
-		go_color_from_str (str, &style->font.color);
-		xmlFree (str);
-	}
-	str = xmlGetProp (node, "font");
-	if (str != NULL) {
-		PangoFontDescription *desc;
-
-		desc = pango_font_description_from_string (str);
-		if (desc != NULL)
-			go_style_set_font_desc (style, desc);
-		xmlFree (str);
-	}
-	if (bool_prop (node, "auto-scale", &tmp))
-		style->font.auto_scale = tmp;
-}
-
-static void
 go_style_font_sax_save (GsfXMLOut *output, GOStyle const *style)
 {
 	char *str;
@@ -1658,18 +1441,6 @@ go_style_font_sax_save (GsfXMLOut *output, GOStyle const *style)
 }
 
 static void
-go_style_text_layout_load (xmlNode *node, GOStyle *style)
-{
-	char *str;
-
-	str = xmlGetProp (node, "angle");
-	if (str != NULL) {
-		go_style_set_text_angle (style, g_strtod (str, NULL));
-		xmlFree (str);
-	}
-}
-
-static void
 go_style_text_layout_sax_save (GsfXMLOut *output, GOStyle const *style)
 {
 	gsf_xml_out_start_element (output, "text_layout");
@@ -1678,32 +1449,6 @@ go_style_text_layout_sax_save (GsfXMLOut *output, GOStyle const *style)
 	gsf_xml_out_end_element (output);
 }
 
-static gboolean
-go_style_persist_dom_load (GOPersist *gp, xmlNode *node)
-{
-	GOStyle *style = GO_STYLE (gp);
-	xmlNode *ptr;
-
-	/* while reloading no need to reapply settings */
-	for (ptr = node->xmlChildrenNode ; ptr != NULL ; ptr = ptr->next) {
-		if (xmlIsBlankNode (ptr) || ptr->name == NULL)
-			continue;
-		if (strcmp (ptr->name, "outline") == 0)
-			go_style_line_load (ptr, &style->line);
-		else if (strcmp (ptr->name, "line") == 0)
-			go_style_line_load (ptr, &style->line);
-		else if (strcmp (ptr->name, "fill") == 0)
-			go_style_fill_load (ptr, style);
-		else if (strcmp (ptr->name, "marker") == 0)
-			go_style_marker_load (ptr, style);
-		else if (strcmp (ptr->name, "font") == 0)
-			go_style_font_load (ptr, style);
-		else if (strcmp (ptr->name, "text_layout") == 0)
-			go_style_text_layout_load (ptr, style);
-	}
-	return TRUE;
-}
-
 static void
 go_style_sax_load_line (GsfXMLIn *xin, xmlChar const **attrs)
 {
@@ -1764,14 +1509,21 @@ go_style_sax_load_fill_image (GsfXMLIn *xin, xmlChar const **attrs)
 {
 	GOStyle *style = GO_STYLE (xin->user_state);
 	GODoc *doc = (GODoc *) g_object_get_data (G_OBJECT (gsf_xml_in_get_input (xin)), "document");
+	xmlChar const *name = NULL, *type_name = NULL;
+	GType type;
 	g_return_if_fail (style->fill.type == GO_STYLE_FILL_NONE);
 	g_return_if_fail (GO_IS_DOC (doc));
 	/* TODO: load the pixels */
 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
-		if (0 == strcmp (attrs[0], "type")) {
+		if (0 == strcmp (attrs[0], "type"))
 			style->fill.image.type = str_as_image_tiling (attrs[1]);
-		} else if (0 == strcmp (attrs[0], "name"))
-			style->fill.image.image = g_object_ref (go_doc_image_fetch (doc, attrs[1]));
+		else if (0 == strcmp (attrs[0], "name"))
+			name = attrs[1];
+		else if (0 == strcmp (attrs[0], "type-name"))
+			type_name = attrs[1];
+	type = type_name? g_type_from_name (type_name): GO_TYPE_PIXBUF;
+	if (name && type_name)
+		style->fill.image.image = g_object_ref (go_doc_image_fetch (doc, name, type));
 	if (style->fill.image.image != NULL)
 		style->fill.type = GO_STYLE_FILL_IMAGE;
 }
@@ -1925,7 +1677,6 @@ go_style_persist_prep_sax (GOPersist *gp, GsfXMLIn *xin, xmlChar const **attrs)
 static void
 go_style_persist_init (GOPersistClass *iface)
 {
-	iface->dom_load = go_style_persist_dom_load;
 	iface->prep_sax = go_style_persist_prep_sax;
 	iface->sax_save = go_style_persist_sax_save;
 }
@@ -2109,22 +1860,10 @@ go_style_set_text_angle (GOStyle *style, double angle)
 	style->text_layout.auto_angle = FALSE;
 }
 
-/**
- * go_style_create_cairo_pattern:
- * @style : #GOStyle
- * @cr: a cairo context
- *
- * Create a cairo_patern_t using the current style settings for filling.
- * A pattern will be created only if the style has the corresponding field
- * and if it is not set to a none constant.
- *
- * Returns: the pattern or NULL if it could not be created.
- **/
-cairo_pattern_t *
-go_style_create_cairo_pattern (GOStyle const *style, cairo_t *cr)
+void
+go_style_fill (GOStyle const *style, cairo_t *cr, gboolean preserve)
 {
-	cairo_pattern_t *cr_pattern;
-	cairo_matrix_t cr_matrix;
+	cairo_pattern_t *cr_pattern = NULL;
 	double x[3], y[3];
 	int w, h;
 
@@ -2147,19 +1886,19 @@ go_style_create_cairo_pattern (GOStyle const *style, cairo_t *cr)
 		{2, 2, 0, 1}
 	};
 
-	g_return_val_if_fail (GO_IS_STYLE (style), NULL);
-
-	if (style->fill.type == GO_STYLE_FILL_NONE)
-		return NULL;
-
 	cairo_fill_extents (cr, &x[0], &y[0], &x[1], &y[1]);
-	if (go_sub_epsilon (fabs (x[0] - x[1])) <=0.0 ||
-	    go_sub_epsilon (fabs (y[0] - y[1])) <=0.0)
-		return NULL;
+	if (!GO_IS_STYLE (style) ||
+	    go_sub_epsilon (fabs (x[0] - x[1])) <=0.0 ||
+	    go_sub_epsilon (fabs (y[0] - y[1])) <=0.0) {
+		if (!preserve)
+			cairo_new_path (cr);
+		return;
+	    }
 
 	switch (style->fill.type) {
 		case GO_STYLE_FILL_PATTERN:
-			return go_pattern_create_cairo_pattern (&style->fill.pattern, cr);
+			cr_pattern = go_pattern_create_cairo_pattern (&style->fill.pattern, cr);
+			break;
 
 		case GO_STYLE_FILL_GRADIENT:
 			x[2] = (x[1] - x[0]) / 2.0 + x[0];
@@ -2174,48 +1913,71 @@ go_style_create_cairo_pattern (GOStyle const *style, cairo_t *cr)
 				GO_COLOR_TO_CAIRO (style->fill.pattern.back));
 			cairo_pattern_add_color_stop_rgba (cr_pattern, 1,
 				GO_COLOR_TO_CAIRO (style->fill.pattern.fore));
-			return cr_pattern;
+			break;
 
 		case GO_STYLE_FILL_IMAGE:
-			if (style->fill.image.image == NULL)
-				return cairo_pattern_create_rgba (1, 1, 1, 1);
-
-			cr_pattern = go_image_create_cairo_pattern (style->fill.image.image);
-			if (cr_pattern == NULL) {
-				/* don't reference anymore an invalid image */
-				((GOStyle *) style)->fill.image.image = NULL;
-				return cairo_pattern_create_rgba (1, 1, 1, 1);
-			}
-			g_object_get (style->fill.image.image, "width", &w, "height", &h, NULL);
-			switch (style->fill.image.type) {
-				case GO_IMAGE_CENTERED:
-					cairo_pattern_set_extend (cr_pattern, CAIRO_EXTEND_NONE);
-					cairo_matrix_init_translate (&cr_matrix,
-								     -(x[1] - x[0] - w) / 2 - x[0],
-								     -(y[1] - y[0] - h) / 2 - y[0]);
-					cairo_pattern_set_matrix (cr_pattern, &cr_matrix);
-					break;
-				case GO_IMAGE_STRETCHED:
-					cairo_pattern_set_extend (cr_pattern, CAIRO_EXTEND_NONE);
-					cairo_matrix_init_scale (&cr_matrix,
-								 w / (x[1] - x[0]),
-								 h / (y[1] - y[0]));
-					cairo_matrix_translate (&cr_matrix, -x[0], -y[0]);
-					cairo_pattern_set_matrix (cr_pattern, &cr_matrix);
-					break;
-				case GO_IMAGE_WALLPAPER:
-					cairo_pattern_set_extend (cr_pattern, CAIRO_EXTEND_REPEAT);
-					cairo_matrix_init_translate (&cr_matrix, -x[0], -y[0]);
-					cairo_pattern_set_matrix (cr_pattern, &cr_matrix);
-					break;
+			if (!GO_IS_IMAGE (style->fill.image.image))
+				cr_pattern = cairo_pattern_create_rgba (1, 1, 1, 1);
+			else {
+				cairo_save (cr);
+				if (preserve)
+					cairo_clip_preserve (cr);
+				else
+					cairo_clip (cr);
+				g_object_get (style->fill.image.image, "width", &w, "height", &h, NULL);
+				switch (style->fill.image.type) {
+					case GO_IMAGE_CENTERED:
+						cairo_translate (cr,
+						                 (x[1] - x[0] - w) / 2 + x[0],
+						                 (y[1] - y[0] - h) / 2 + y[0]);
+						go_image_draw (style->fill.image.image, cr);
+						break;
+					case GO_IMAGE_STRETCHED:
+						cairo_translate (cr, x[0], y[0]);
+						cairo_scale (cr, (x[1] - x[0]) / w, (y[1] - y[0]) / h);
+						go_image_draw (style->fill.image.image, cr);
+						break;
+					case GO_IMAGE_CENTERED_WALLPAPER: {
+						int n = go_fake_floor ((x[1] - x[0]) / w);
+						x[0] -= w - (x[1] - x[0] - n * w) / 2.;
+						n = go_fake_floor ((y[1] - y[0]) / h);
+						y[0] -= h - (y[1] - y[0] - n * h) / 2.;
+					}
+					case GO_IMAGE_WALLPAPER: {
+						double cx = x[0], cy;
+						while (cx < x[1]) {
+							cy = y[0];
+							while (cy < y[1]) {
+								cairo_save (cr);
+								cairo_translate (cr, cx, cy);
+								go_image_draw (style->fill.image.image, cr);
+								cairo_restore (cr);
+								cy += h;
+							}
+							cx += w;
+						}
+						break;
+					}
+				}
+				cairo_restore (cr);
+				return;
 			}
-			return cr_pattern;
 
 		case GO_STYLE_FILL_NONE:
-			return NULL;
+			if (!preserve)
+				cairo_new_path (cr);
+			break;
 	}
 
-	return NULL;
+	if (cr_pattern) {
+		cairo_set_source (cr, cr_pattern);
+		cairo_pattern_destroy (cr_pattern);
+
+		if (preserve)
+			cairo_fill_preserve (cr);
+		else
+			cairo_fill (cr);
+	}
 }
 
 gboolean
diff --git a/goffice/utils/go-style.h b/goffice/utils/go-style.h
index 419509d..17e9347 100644
--- a/goffice/utils/go-style.h
+++ b/goffice/utils/go-style.h
@@ -54,7 +54,8 @@ typedef enum {
 typedef enum {
 	GO_IMAGE_STRETCHED,
 	GO_IMAGE_WALLPAPER,
-	GO_IMAGE_CENTERED
+	GO_IMAGE_CENTERED,
+	GO_IMAGE_CENTERED_WALLPAPER
 } GOImageType;
 
 typedef struct {
@@ -154,8 +155,8 @@ gpointer   go_style_get_editor	     	(GOStyle *style,
 					 GObject *object_with_style);
 #endif
 
-cairo_pattern_t *go_style_create_cairo_pattern (GOStyle const *style,
-						cairo_t *cr);
+void     go_style_fill			(GOStyle const *style, cairo_t *cr,
+			                 gboolean preserve);
 gboolean go_style_set_cairo_line (GOStyle const *style, cairo_t *cr);
 
 G_END_DECLS
diff --git a/goffice/utils/go-styled-object.c b/goffice/utils/go-styled-object.c
index 6aee6b3..edfadc0 100644
--- a/goffice/utils/go-styled-object.c
+++ b/goffice/utils/go-styled-object.c
@@ -223,30 +223,22 @@ go_styled_object_set_cairo_line (GOStyledObject const *so, cairo_t *cr)
 }
 
 /**
- * go_styled_object_set_cairo_fill :
+ * go_styled_object_fill :
  * @so: #GOStyledObject
  * @cr: #cairo_t
+ * @preserve: whether the current path should be preserved
  *
- * Prepares the cairo context @cr to fill a shape according to the
+ * fills the current path according to the
  * item style and canvas scale.
  *
- * Returns: %TRUE if the filling is not invisible.
  **/
-gboolean
-go_styled_object_set_cairo_fill (GOStyledObject const *so, cairo_t *cr)
+void
+go_styled_object_fill (GOStyledObject const *so, cairo_t *cr, gboolean preserve)
 {
 	GOStyle const *style;
-	cairo_pattern_t *pat = NULL;
 
-	g_return_val_if_fail (GO_IS_STYLED_OBJECT (so), FALSE);
+	g_return_if_fail (GO_IS_STYLED_OBJECT (so));
 	style = go_styled_object_get_style (GO_STYLED_OBJECT (so));
-	if (style->fill.type == GO_STYLE_FILL_NONE)
-		return FALSE;
-	pat = go_style_create_cairo_pattern (style, cr);
-	if (pat) {
-		cairo_set_source (cr, pat);
-		cairo_pattern_destroy (pat);
-	}
-	return TRUE;
+	go_style_fill (style, cr, preserve);
 }
 
diff --git a/goffice/utils/go-styled-object.h b/goffice/utils/go-styled-object.h
index 53fb62d..7ad3402 100644
--- a/goffice/utils/go-styled-object.h
+++ b/goffice/utils/go-styled-object.h
@@ -54,7 +54,8 @@ void	  go_styled_object_style_changed   (GOStyledObject *gso);
 void	  go_styled_object_apply_theme	   (GOStyledObject *gso, GOStyle *style);
 GODoc	 *go_styled_object_get_document	   (GOStyledObject *gso);
 gboolean  go_styled_object_set_cairo_line  (GOStyledObject const *so, cairo_t *cr);
-gboolean  go_styled_object_set_cairo_fill  (GOStyledObject const *so, cairo_t *cr);
+void      go_styled_object_fill		   (GOStyledObject const *so, cairo_t *cr, gboolean preserve);
+gboolean go_styled_object_is_filled	   (GOStyledObject *gso);
 G_END_DECLS
 
 #endif
diff --git a/goffice/utils/go-svg.c b/goffice/utils/go-svg.c
new file mode 100644
index 0000000..c2c84a3
--- /dev/null
+++ b/goffice/utils/go-svg.c
@@ -0,0 +1,182 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-svg.c - SVG image support
+ *
+ * Copyright (C) 2011 Jean Brefort (jean brefort normalesup org)
+ *
+ * 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ */
+
+#include <goffice/goffice-config.h>
+#include "go-svg.h"
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+#include <gsf/gsf-utils.h>
+#include <gsf/gsf-impl-utils.h>
+#include <gsf/gsf-input-stdio.h>
+#include <string.h>
+
+struct _GOSvg {
+	GOImage parent;
+	RsvgHandle *handle;
+	gsize data_length;
+};
+
+typedef GOImageClass GOSvgClass;
+
+static GObjectClass *parent_klass;
+
+static void
+go_svg_save (GOImage *image, GsfXMLOut *output)
+{
+	GOSvg *svg = GO_SVG (image);
+	g_return_if_fail (svg);
+	gsf_xml_out_add_base64 (output, NULL,
+			image->data, svg->data_length);
+}
+
+static void
+go_svg_load_attr (G_GNUC_UNUSED GOImage *image, G_GNUC_UNUSED xmlChar const *attr_name, G_GNUC_UNUSED xmlChar const *attr_value)
+{
+	/* nothing to do */
+}
+
+static void
+go_svg_load_data (GOImage *image, GsfXMLIn *xin)
+{
+	GOSvg *svg = GO_SVG (image);
+	svg->data_length = gsf_base64_decode_simple (xin->content->str, strlen(xin->content->str));
+	image->data = g_malloc (svg->data_length);
+	memcpy (image->data, xin->content->str, svg->data_length);
+	svg->handle = rsvg_handle_new_from_data (image->data, svg->data_length, NULL);
+}
+
+static void
+go_svg_draw (GOImage *image, cairo_t *cr)
+{
+	GOSvg *svg = GO_SVG (image);
+	rsvg_handle_render_cairo (svg->handle, cr);
+}
+
+static GdkPixbuf *
+go_svg_get_pixbuf (GOImage *image)
+{
+	GOSvg *svg = GO_SVG (image);
+	cairo_surface_t *surface;
+	cairo_t *cr;
+	GdkPixbuf *res = NULL;
+	g_return_val_if_fail (svg != NULL, NULL);
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, image->width, image->height);
+	cr = cairo_create (surface);
+	rsvg_handle_render_cairo (svg->handle, cr);
+	cairo_destroy (cr);
+	res = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, image->width, image->height);
+	go_cairo_convert_data_from_pixbuf (gdk_pixbuf_get_pixels (res),
+	                                   cairo_image_surface_get_data (surface),
+	                                   image->width, image->height,
+	                                   cairo_image_surface_get_stride (surface));
+	return res;
+}
+
+static GdkPixbuf *
+go_svg_get_scaled_pixbuf (GOImage *image, int width, int height)
+{
+	GOSvg *svg = GO_SVG (image);
+	cairo_surface_t *surface;
+	cairo_t *cr;
+	GdkPixbuf *res = NULL;
+	g_return_val_if_fail (svg != NULL, NULL);
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+	cr = cairo_create (surface);
+	cairo_scale (cr, width / image->width, height / image->height);
+	rsvg_handle_render_cairo (svg->handle, cr);
+	cairo_destroy (cr);
+	res = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+	go_cairo_convert_data_from_pixbuf (gdk_pixbuf_get_pixels (res),
+	                                   cairo_image_surface_get_data (surface),
+	                                   width, height,
+	                                   cairo_image_surface_get_stride (surface));
+	return res;
+}
+
+static gboolean
+go_svg_differ (GOImage *first, GOImage *second)
+{
+	GOSvg *sfirst = GO_SVG (first), *ssecond = GO_SVG (second);
+	if (sfirst->data_length != ssecond->data_length)
+		return TRUE;
+	return memcmp (first->data, second->data, sfirst->data_length);
+}
+
+static void
+go_svg_finalize (GObject *obj)
+{
+	GOSvg *svg = GO_SVG (obj);
+	if (svg->handle)
+		g_object_unref (svg->handle);
+	(parent_klass->finalize) (obj);
+}
+
+static void
+go_svg_class_init (GObjectClass *klass)
+{
+	GOImageClass *image_klass = (GOImageClass *) klass;
+
+	klass->finalize = go_svg_finalize;
+	parent_klass = g_type_class_peek_parent (klass);
+
+	image_klass->save = go_svg_save;
+	image_klass->load_attr = go_svg_load_attr;
+	image_klass->load_data = go_svg_load_data;
+	image_klass->get_pixbuf = go_svg_get_pixbuf;
+	image_klass->get_scaled_pixbuf = go_svg_get_scaled_pixbuf;
+	image_klass->draw = go_svg_draw;
+	image_klass->differ = go_svg_differ;
+}
+
+GSF_CLASS (GOSvg, go_svg,
+	   go_svg_class_init, NULL,
+	   GO_TYPE_IMAGE)
+
+
+GOSvg *
+go_svg_new_from_file (char const *filename, GError **error)
+{
+	GOSvg *svg = g_object_new (GO_TYPE_SVG, NULL);
+	guint8 *data;
+	GsfInput *input = gsf_input_stdio_new (filename, error);
+	RsvgDimensionData dim;
+	GOImage *image;
+	if (!input)
+		return NULL;
+	svg->data_length = gsf_input_size (input);
+	data = g_malloc (svg->data_length);
+	if (!data || !gsf_input_read (input, svg->data_length, data)) {
+		g_object_unref (svg);
+		return NULL;
+	}
+	image = GO_IMAGE (svg);
+	image->data = data;
+	svg->handle = rsvg_handle_new_from_data (data, svg->data_length, error);
+	if (svg->handle == NULL) {
+		g_object_unref (svg);
+		return NULL;
+	}
+	rsvg_handle_get_dimensions (svg->handle, &dim);
+	image->width = dim.width;
+	image->height = dim.height;
+	return svg;
+}
diff --git a/goffice/utils/go-svg.h b/goffice/utils/go-svg.h
new file mode 100644
index 0000000..baa8395
--- /dev/null
+++ b/goffice/utils/go-svg.h
@@ -0,0 +1,40 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * go-svg.h - SVG image support
+ *
+ * Copyright (C) 2011 Jean Brefort (jean brefort normalesup org)
+ *
+ * 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 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ */
+ 
+#ifndef GO_SVG_H
+#define GO_SVG_H
+
+#include <goffice/goffice.h>
+
+G_BEGIN_DECLS
+
+#define GO_TYPE_SVG	(go_svg_get_type ())
+#define GO_SVG(o)	(G_TYPE_CHECK_INSTANCE_CAST ((o), GO_TYPE_SVG, GOSvg))
+#define GO_IS_SVG(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GO_TYPE_SVG))
+
+GType go_svg_get_type (void);
+
+GOSvg *go_svg_new_from_file (char const *filename, GError **error);
+
+G_END_DECLS
+
+#endif /* GO_SVG_H */
diff --git a/goffice/utils/goffice-utils.h b/goffice/utils/goffice-utils.h
index bbd819c..58208f2 100644
--- a/goffice/utils/goffice-utils.h
+++ b/goffice/utils/goffice-utils.h
@@ -35,6 +35,8 @@ typedef struct _GOMarker		GOMarker;
 typedef struct _GOFormat		GOFormat;
 typedef struct _GODateConventions	GODateConventions;
 typedef struct _GOImage			GOImage;
+typedef struct _GOPixbuf		GOPixbuf;
+typedef struct _GOSvg			GOSvg;
 typedef struct _GOPath GOPath;
 typedef struct _GOString GOString;
 typedef struct _GOStyle			GOStyle;
@@ -114,6 +116,7 @@ G_END_DECLS
 #include <goffice/utils/go-gradient.h>
 #include <goffice/utils/go-image.h>
 #include <goffice/utils/go-pixbuf.h>
+#include <goffice/utils/go-svg.h>
 #include <goffice/utils/go-libxml-extras.h>
 #include <goffice/utils/go-line.h>
 #include <goffice/utils/go-locale.h>



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