[dia/cairo-port] Copy the cairo plugin into Dia core



commit eb83dadd8522206f3ed5c503de7c4445c42e0806
Author: Zander Brown <zbrown gnome org>
Date:   Thu Dec 6 18:32:19 2018 +0000

    Copy the cairo plugin into Dia core
    
    Currently segfaults on launch but it should provide a good base to hack on

 app/Makefile.am                     |    7 +
 app/display.c                       |    7 +-
 app/renderer/diacairo-interactive.c |  675 +++++++++++++++++++
 app/renderer/diacairo-print.c       |  252 +++++++
 app/renderer/diacairo-print.h       |   13 +
 app/renderer/diacairo-renderer.c    | 1242 +++++++++++++++++++++++++++++++++++
 app/renderer/diacairo.c             |  539 +++++++++++++++
 app/renderer/diacairo.h             |   99 +++
 plug-ins/cairo/diacairo-renderer.c  |    1 -
 plug-ins/cairo/diacairo.c           |    1 -
 10 files changed, 2831 insertions(+), 5 deletions(-)
---
diff --git a/app/Makefile.am b/app/Makefile.am
index e924a263..d8b1af6e 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -10,6 +10,7 @@ AM_CPPFLAGS = \
        $(GTK_CFLAGS) \
        $(GTK_MAC_CFLAGS) \
        $(LIBART_CFLAGS) \
+       $(CAIRO_CFLAGS) \
        -DPREFIX=\""$(prefix)"\" \
        -DSYSCONFDIR=\""$(sysconfdir)"\" \
        -DDATADIR=\""$(datadir)"\" \
@@ -156,6 +157,12 @@ dia_core_files = \
        cut_n_paste.h \
        render_gdk.c \
        render_gdk.h \
+       renderer/diacairo.c \
+       renderer/diacairo.h \
+       renderer/diacairo-interactive.c \
+       renderer/diacairo-renderer.c \
+       renderer/diacairo-print.c \
+       renderer/diacairo-print.h \
        ruler.c \
        ruler.h \
        tool.c \
diff --git a/app/display.c b/app/display.c
index 076d0468..a67a261c 100644
--- a/app/display.c
+++ b/app/display.c
@@ -45,6 +45,7 @@
 #include "load_save.h"
 #include "dia-props.h"
 #include "render_gdk.h"
+#include "renderer/diacairo.h"
 #include "diatransform.h"
 #include "recent_files.h"
 #include "filedlg.h"
@@ -1126,7 +1127,7 @@ new_aa_renderer (DDisplay *ddisp)
   /* we really should not come here but instead disable the menu command earlier */
   message_warning (_("No antialiased renderer found"));
   /* fallback: built-in libart renderer */
-  return new_gdk_renderer (ddisp); 
+  return dia_cairo_interactive_renderer_new (ddisp); 
 }
 
 void
@@ -1152,7 +1153,7 @@ ddisplay_set_renderer(DDisplay *ddisp, int aa_renderer)
   if (ddisp->aa_renderer){
     ddisp->renderer = new_aa_renderer (ddisp);
   } else {
-    ddisp->renderer = new_gdk_renderer(ddisp);
+    ddisp->renderer = dia_cairo_interactive_renderer_new(ddisp);
   }
 
   if (window)
@@ -1167,7 +1168,7 @@ ddisplay_resize_canvas(DDisplay *ddisp,
     if (ddisp->aa_renderer)
       ddisp->renderer = new_aa_renderer (ddisp);
     else
-      ddisp->renderer = new_gdk_renderer(ddisp);
+      ddisp->renderer = dia_cairo_interactive_renderer_new(ddisp);
   }
 
   dia_renderer_set_size(ddisp->renderer, gtk_widget_get_window(ddisp->canvas), width, height);
diff --git a/app/renderer/diacairo-interactive.c b/app/renderer/diacairo-interactive.c
new file mode 100644
index 00000000..7162c40d
--- /dev/null
+++ b/app/renderer/diacairo-interactive.c
@@ -0,0 +1,675 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diacairo-interactive.c -- Cairo based interactive renderer
+ * Copyright (C) 2006, Nguyen Thai Ngoc Duy
+ * Copyright (C) 2007, Hans Breuer, <Hans Breuer 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "diacairo.h"
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h> /* GTK_CHECK_VERSION */
+
+#include "intl.h"
+#include "color.h"
+#include "diatransform.h"
+#include "object.h"
+#include "textline.h"
+
+/* There is a variant prepared for GTK+3 
+ * but it seems to be sligly slower than the original version.
+ */
+#if GTK_CHECK_VERSION(3,0,0)
+#define DIA_CAIRO_WITH_PIXMAP 0
+#else
+#define DIA_CAIRO_WITH_PIXMAP 1
+#endif
+
+#define DIA_TYPE_CAIRO_INTERACTIVE_RENDERER           (dia_cairo_interactive_renderer_get_type ())
+#define DIA_CAIRO_INTERACTIVE_RENDERER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
DIA_TYPE_CAIRO_INTERACTIVE_RENDERER, DiaCairoInteractiveRenderer))
+#define DIA_CAIRO_INTERACTIVE_RENDERER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), 
DIA_TYPE_CAIRO_INTERACTIVE_RENDERER, DiaCairoInteractiveRendererClass))
+#define DIA_CAIRO_IS_INTERACTIVE_RENDERER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
DIA_TYPE_CAIRO_INTERACTIVE_RENDERER))
+#define DIA_CAIRO_INTERACTIVE_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), 
DIA_TYPE_CAIRO_INTERACTIVE_RENDERER, DiaCairoInteractiveRendererClass))
+
+GType dia_cairo_interactive_renderer_get_type (void) G_GNUC_CONST;
+
+typedef struct _DiaCairoInteractiveRenderer DiaCairoInteractiveRenderer;
+typedef struct _DiaCairoInteractiveRendererClass DiaCairoInteractiveRendererClass;
+
+struct _DiaCairoInteractiveRenderer
+{
+  DiaCairoRenderer parent_instance;
+
+  /*< private >*/
+  Rectangle *visible;
+  real *zoom_factor;
+
+#if DIA_CAIRO_WITH_PIXMAP
+  GdkPixmap *pixmap;              /* The pixmap shown in this display  */
+  GdkGC *gc;
+#else
+  cairo_surface_t *pixmap;        /* The pixmap shown in this display  */
+#endif
+  guint32 width;                  /* The width of the pixmap in pixels */
+  guint32 height;                 /* The height of the pixmap in pixels */
+  GdkRegion *clip_region;
+
+  /** If non-NULL, this rendering is a highlighting with the given color. */
+  Color *highlight_color;
+};
+
+struct _DiaCairoInteractiveRendererClass
+{
+  DiaRendererClass parent_class;
+};
+
+static void clip_region_clear(DiaRenderer *renderer);
+static void clip_region_add_rect(DiaRenderer *renderer,
+                                 Rectangle *rect);
+
+static void draw_pixel_line(DiaRenderer *renderer,
+                            int x1, int y1,
+                            int x2, int y2,
+                            Color *color);
+static void draw_pixel_rect(DiaRenderer *renderer,
+                            int x, int y,
+                            int width, int height,
+                            Color *color);
+static void fill_pixel_rect(DiaRenderer *renderer,
+                            int x, int y,
+                            int width, int height,
+                            Color *color);
+static void set_size (DiaRenderer *renderer, 
+                      gpointer window,
+                      int width, int height);
+static void copy_to_window (DiaRenderer *renderer, 
+                gpointer window,
+                int x, int y, int width, int height);
+
+static void cairo_interactive_renderer_get_property (GObject         *object,
+                        guint            prop_id,
+                        GValue          *value,
+                        GParamSpec      *pspec);
+static void cairo_interactive_renderer_set_property (GObject         *object,
+                        guint            prop_id,
+                        const GValue    *value,
+                        GParamSpec      *pspec);
+
+enum {
+  PROP_0,
+  PROP_ZOOM,
+  PROP_RECT
+};
+
+
+static int
+get_width_pixels (DiaRenderer *object)
+{ 
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+  return renderer->width;
+}
+
+static int
+get_height_pixels (DiaRenderer *object)
+{ 
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+  return renderer->height;
+}
+
+/* gobject boiler plate */
+static void cairo_interactive_renderer_init (DiaCairoInteractiveRenderer *r, void *p);
+static void cairo_interactive_renderer_class_init (DiaCairoInteractiveRendererClass *klass);
+
+static gpointer parent_class = NULL;
+
+static void
+cairo_interactive_renderer_init (DiaCairoInteractiveRenderer *object, void *p)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+  DiaRenderer *dia_renderer = DIA_RENDERER(object);
+  
+  dia_renderer->is_interactive = 1;
+  
+  renderer->pixmap = NULL;
+
+  renderer->highlight_color = NULL;
+}
+
+static void
+cairo_interactive_renderer_finalize (GObject *object)
+{
+#if !DIA_CAIRO_WITH_PIXMAP
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+#endif
+  DiaCairoRenderer *base_renderer = DIA_CAIRO_RENDERER (object);
+
+  if (base_renderer->cr)
+    cairo_destroy (base_renderer->cr);
+  base_renderer->cr = NULL;
+#if !DIA_CAIRO_WITH_PIXMAP
+  if (renderer->pixmap)
+    cairo_surface_destroy (renderer->pixmap);
+#endif
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/* Get the width of the given text in cm */
+static real
+get_text_width(DiaRenderer *object,
+               const gchar *text, int length)
+{
+  real result;
+  TextLine *text_line;
+
+  if (length != g_utf8_strlen(text, -1)) {
+    char *shorter;
+    int ulen;
+    ulen = g_utf8_offset_to_pointer(text, length)-text;
+    if (!g_utf8_validate(text, ulen, NULL)) {
+      g_warning ("Text at char %d not valid\n", length);
+    }
+    shorter = g_strndup(text, ulen);
+    text_line = text_line_new(shorter, object->font, object->font_height);
+    g_free (shorter);
+  } else {
+    text_line = text_line_new(text, object->font, object->font_height);
+  }
+  result = text_line_get_width(text_line);
+  text_line_destroy(text_line);
+  return result;
+}
+/** Used as background? for text editing */
+static Color text_edit_color = {1.0, 1.0, 0.7 };
+
+static real
+calculate_relative_luminance (const Color *c)
+{
+  real R, G, B;
+
+  R = (c->red <= 0.03928) ? c->red / 12.92 : pow((c->red+0.055)/1.055, 2.4);
+  G = (c->green <= 0.03928) ? c->green / 12.92 : pow((c->green+0.055)/1.055, 2.4);
+  B = (c->blue <= 0.03928) ? c->blue / 12.92 : pow((c->blue+0.055)/1.055, 2.4);
+
+  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
+}
+static void 
+draw_text_line (DiaRenderer *self, TextLine *text_line,
+               Point *pos, Alignment alignment, Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  DiaCairoInteractiveRenderer *interactive = DIA_CAIRO_INTERACTIVE_RENDERER (self);
+
+  if (interactive->highlight_color) {
+    /* the high_light color is just taken as a hint, alternative needs
+     * to have some contrast to cursor color (curently hard coded black)
+     */
+    static Color alternate_color = { 0.5, 0.5, 0.4 };
+    real rl, cr1, cr2;
+
+    /* just draw the box */
+    real h = text_line_get_height (text_line);
+    real w = text_line_get_width (text_line);
+    real x = pos->x;
+    real y = pos->y;
+
+    y -= text_line_get_ascent(text_line);    
+    x -= text_line_get_alignment_adjustment (text_line, alignment);
+
+    rl = calculate_relative_luminance (color) + 0.05;
+    cr1 = calculate_relative_luminance (interactive->highlight_color) + 0.05;
+    cr1 = (cr1 > rl) ? cr1 / rl : rl / cr1;
+    cr2 = calculate_relative_luminance (&alternate_color) + 0.05;
+    cr2 = (cr2 > rl) ? cr2 / rl : rl / cr2;
+
+    /* use color giving the better contrast ratio, if necessary 
+     * http://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast-contrast
+     */
+    if (cr1 > cr2)
+      cairo_set_source_rgba (renderer->cr, 
+                          interactive->highlight_color->red,
+                          interactive->highlight_color->green,
+                          interactive->highlight_color->blue,
+                          1.0);
+    else
+      cairo_set_source_rgba (renderer->cr, 
+                          alternate_color.red,
+                          alternate_color.green,
+                          alternate_color.blue,
+                          1.0);
+
+    cairo_rectangle (renderer->cr, x, y, w, h);
+    cairo_fill (renderer->cr);
+  }
+  DIA_RENDERER_CLASS (parent_class)->draw_text_line (self, text_line, pos, alignment, color);
+}
+static void
+draw_object_highlighted (DiaRenderer     *self, 
+                        DiaObject       *object,
+                        DiaHighlightType type)
+{
+  DiaCairoInteractiveRenderer *interactive = DIA_CAIRO_INTERACTIVE_RENDERER (self);
+
+  switch (type) {
+  case DIA_HIGHLIGHT_TEXT_EDIT:
+    interactive->highlight_color = &text_edit_color;
+    break;
+  case DIA_HIGHLIGHT_CONNECTIONPOINT_MAIN:
+  case DIA_HIGHLIGHT_CONNECTIONPOINT:
+  case DIA_HIGHLIGHT_NONE:
+    interactive->highlight_color = NULL;
+    break;
+  }
+  /* usually this method would need to draw the object twice,
+   * once with highlight and once without. But due to our
+   * draw_text_line implementation we only need one run */
+  object->ops->draw(object, self);
+  /* always reset when done with this object */
+  interactive->highlight_color = NULL;
+}
+static void 
+dia_cairo_interactive_renderer_iface_init (DiaInteractiveRendererInterface* iface)
+{
+  iface->clip_region_clear = clip_region_clear;
+  iface->clip_region_add_rect = clip_region_add_rect;
+  iface->draw_pixel_line = draw_pixel_line;
+  iface->draw_pixel_rect = draw_pixel_rect;
+  iface->fill_pixel_rect = fill_pixel_rect;
+  iface->copy_to_window = copy_to_window;
+  iface->set_size = set_size;
+  iface->draw_object_highlighted = draw_object_highlighted;
+}
+
+GType
+dia_cairo_interactive_renderer_get_type (void)
+{
+  static GType object_type = 0;
+
+  if (!object_type)
+    {
+      static const GTypeInfo object_info =
+      {
+        sizeof (DiaCairoInteractiveRendererClass),
+        (GBaseInitFunc) NULL,
+        (GBaseFinalizeFunc) NULL,
+        (GClassInitFunc) cairo_interactive_renderer_class_init,
+        NULL,           /* class_finalize */
+        NULL,           /* class_data */
+        sizeof (DiaCairoInteractiveRenderer),
+        0,              /* n_preallocs */
+       (GInstanceInitFunc) cairo_interactive_renderer_init /* init */
+      };
+
+      static const GInterfaceInfo irenderer_iface_info = 
+      {
+       (GInterfaceInitFunc) dia_cairo_interactive_renderer_iface_init,
+       NULL,           /* iface_finalize */
+       NULL            /* iface_data     */
+      };
+
+      object_type = g_type_register_static (DIA_TYPE_CAIRO_RENDERER,
+                                            "DiaCairoInteractiveRenderer",
+                                            &object_info, 0);
+
+      /* register the interactive renderer interface */
+      g_type_add_interface_static (object_type,
+                                   DIA_TYPE_INTERACTIVE_RENDERER_INTERFACE,
+                                   &irenderer_iface_info);
+
+    }
+  
+  return object_type;
+}
+
+static void
+begin_render(DiaRenderer *self, const Rectangle *update)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (self);
+  DiaCairoRenderer *base_renderer = DIA_CAIRO_RENDERER (self);
+
+  g_return_if_fail (base_renderer->cr == NULL);
+#if DIA_CAIRO_WITH_PIXMAP
+  base_renderer->cr = gdk_cairo_create(renderer->pixmap);
+#else
+  base_renderer->cr = cairo_create(renderer->pixmap);
+#endif
+
+  /* Setup clipping for this sequence of render operations */
+  /* Must be done before the scaling because the clip is in pixel coords */
+  gdk_cairo_region (base_renderer->cr, renderer->clip_region);
+  cairo_clip(base_renderer->cr); 
+
+  cairo_scale (base_renderer->cr, *renderer->zoom_factor, *renderer->zoom_factor);
+  cairo_translate (base_renderer->cr, -renderer->visible->left, -renderer->visible->top);
+
+  /* second clipping */
+  if (update) {
+    real width = update->right - update->left;
+    real height = update->bottom - update->top;
+    cairo_rectangle (base_renderer->cr, update->left, update->top, width, height);
+    cairo_clip (base_renderer->cr);
+  }
+#ifdef HAVE_PANGOCAIRO_H
+  base_renderer->layout = pango_cairo_create_layout (base_renderer->cr);
+#endif
+
+  cairo_set_fill_rule (base_renderer->cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+#if !DIA_CAIRO_WITH_PIXMAP
+  /* should we set the background color? Or do nothing at all? */
+  /* if this is drawn you can see 'clipping in action', outside of the clip it gets yellow ;) */
+  cairo_set_source_rgba (base_renderer->cr, 1.0, 1.0, .8, 1.0);
+  cairo_set_operator (base_renderer->cr, CAIRO_OPERATOR_OVER);
+  cairo_rectangle (base_renderer->cr, 0, 0, renderer->width, renderer->height);
+  cairo_fill (base_renderer->cr);
+#endif
+}
+
+static void
+end_render(DiaRenderer *self)
+{
+  DiaCairoRenderer *base_renderer = DIA_CAIRO_RENDERER (self);
+
+  cairo_show_page (base_renderer->cr);
+  cairo_destroy (base_renderer->cr);
+  base_renderer->cr = NULL;
+}
+
+static void
+cairo_interactive_renderer_class_init (DiaCairoInteractiveRendererClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize = cairo_interactive_renderer_finalize;
+
+  gobject_class->set_property = cairo_interactive_renderer_set_property;
+  gobject_class->get_property = cairo_interactive_renderer_get_property;
+
+  g_object_class_install_property (gobject_class,
+                                  PROP_ZOOM,
+                                  g_param_spec_pointer ("zoom",
+                                                       _("Zoom pointer"),
+                                                       _("Zoom pointer"),
+                                                       G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+                                  PROP_RECT,
+                                  g_param_spec_pointer ("rect",
+                                                       _("Visible rect pointer"),
+                                                       _("Visible rect pointer"),
+                                                       G_PARAM_READWRITE));
+                                                       
+  /* renderer members */
+  renderer_class->get_width_pixels  = get_width_pixels;
+  renderer_class->get_height_pixels = get_height_pixels;
+
+  renderer_class->begin_render = begin_render;
+  renderer_class->end_render   = end_render;
+
+  /* mostly for cursor placement */
+  renderer_class->get_text_width = get_text_width;
+  /* highlight for text editing is special */
+  renderer_class->draw_text_line = draw_text_line;
+}
+
+static void
+cairo_interactive_renderer_set_property (GObject         *object,
+                        guint            prop_id,
+                        const GValue    *value,
+                        GParamSpec      *pspec)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+
+  switch (prop_id) {
+    case PROP_ZOOM:
+      renderer->zoom_factor = g_value_get_pointer(value);
+      break;
+    case PROP_RECT:
+      renderer->visible = g_value_get_pointer(value);
+      break;
+    default:
+      break;
+    }
+}
+
+static void
+cairo_interactive_renderer_get_property (GObject         *object,
+                        guint            prop_id,
+                        GValue          *value,
+                        GParamSpec      *pspec)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+  
+  switch (prop_id) {
+    case PROP_ZOOM:
+      g_value_set_pointer (value, renderer->zoom_factor);
+      break;
+    case PROP_RECT:
+      g_value_set_pointer (value, renderer->visible);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+set_size(DiaRenderer *object, gpointer window,
+                     int width, int height)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+  DiaCairoRenderer *base_renderer = DIA_CAIRO_RENDERER (object);
+
+  renderer->width = width;
+  renderer->height = height;
+#if DIA_CAIRO_WITH_PIXMAP
+  if (renderer->pixmap != NULL)
+    g_object_unref(renderer->pixmap);
+
+  /* TODO: we can probably get rid of this extra pixmap and just draw directly
+   * to what gdk_cairo_create() gives us for the window
+   */
+  renderer->pixmap = gdk_pixmap_new(GDK_WINDOW(window),  width, height, -1);
+#else
+# if GTK_CHECK_VERSION(2,22,0)
+  renderer->pixmap = gdk_window_create_similar_surface (GDK_WINDOW (window),
+                                                       CAIRO_CONTENT_COLOR,
+                                                       width, height);
+# else
+  {
+    cairo_rectangle_t extents;
+
+    extents.x = 0;
+    extents.y = 0;
+    extents.width = width;
+    extents.height = height;
+    renderer->pixmap = cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, &extents);
+  }
+# endif
+#endif
+
+  if (base_renderer->surface != NULL)
+    cairo_surface_destroy(base_renderer->surface);
+#if DIA_CAIRO_WITH_PIXMAP
+  if (renderer->gc == NULL)
+    renderer->gc = gdk_gc_new(renderer->pixmap);
+#endif
+}
+
+static void
+copy_to_window (DiaRenderer *object, gpointer window,
+                int x, int y, int width, int height)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+#if DIA_CAIRO_WITH_PIXMAP
+  static GdkGC *copy_gc = NULL;
+  
+  if (!copy_gc)
+    copy_gc = gdk_gc_new(window);
+
+  gdk_draw_drawable (GDK_WINDOW(window),
+                     copy_gc,
+                     renderer->pixmap,
+                     x, y,
+                     x, y,
+                     width > 0 ? width : -width, height > 0 ? height : -height);
+#else
+  cairo_t *cr;
+
+  cr = gdk_cairo_create (GDK_WINDOW(window));
+  cairo_set_source_surface (cr, renderer->pixmap, 0.0, 0.0);
+  cairo_rectangle (cr, x, y, width > 0 ? width : -width, height > 0 ? height : -height);
+  cairo_clip (cr);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+#endif
+}
+
+static void
+clip_region_clear(DiaRenderer *object)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+
+  if (renderer->clip_region != NULL)
+    gdk_region_destroy(renderer->clip_region);
+
+  renderer->clip_region =  gdk_region_new();
+#if DIA_CAIRO_WITH_PIXMAP
+  gdk_gc_set_clip_region(renderer->gc, renderer->clip_region);
+#endif
+}
+
+static void
+clip_region_add_rect(DiaRenderer *object,
+                Rectangle *rect)
+{
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+  GdkRectangle clip_rect;
+  int x1,y1;
+  int x2,y2;
+
+  DiaTransform *transform;        /* Our link to the display settings */
+
+  transform = dia_transform_new(renderer->visible,renderer->zoom_factor);
+  dia_transform_coords(transform, rect->left, rect->top,  &x1, &y1);
+  dia_transform_coords(transform, rect->right, rect->bottom,  &x2, &y2);
+  g_object_unref(transform);
+  
+  clip_rect.x = x1;
+  clip_rect.y = y1;
+  clip_rect.width = x2 - x1 + 1;
+  clip_rect.height = y2 - y1 + 1;
+
+  gdk_region_union_with_rect(renderer->clip_region, &clip_rect);
+#if DIA_CAIRO_WITH_PIXMAP
+  gdk_gc_set_clip_region(renderer->gc, renderer->clip_region);
+#endif
+}
+
+static void
+draw_pixel_line(DiaRenderer *object,
+               int x1, int y1,
+               int x2, int y2,
+               Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (object);
+  double x1u = x1 + .5, y1u = y1 + .5, x2u = x2 + .5, y2u = y2 + .5;
+  double lw[2];
+  lw[0] = 1; lw[1] = 0;
+  
+  cairo_device_to_user_distance (renderer->cr, &lw[0], &lw[1]);
+  cairo_set_line_width (renderer->cr, lw[0]);
+
+  cairo_device_to_user (renderer->cr, &x1u, &y1u);
+  cairo_device_to_user (renderer->cr, &x2u, &y2u);
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+  cairo_move_to (renderer->cr, x1u, y1u);
+  cairo_line_to (renderer->cr, x2u, y2u);
+  cairo_stroke (renderer->cr);
+}
+
+static void
+draw_pixel_rect(DiaRenderer *object,
+               int x, int y,
+               int width, int height,
+               Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (object);
+  double x1u = x + .5, y1u = y + .5, x2u = x + width + .5, y2u = y + height + .5;
+  double lw[2];
+  lw[0] = 1; lw[1] = 0;
+  
+  cairo_device_to_user_distance (renderer->cr, &lw[0], &lw[1]);
+  cairo_set_line_width (renderer->cr, lw[0]);
+
+  cairo_device_to_user (renderer->cr, &x1u, &y1u);
+  cairo_device_to_user (renderer->cr, &x2u, &y2u);
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+  cairo_rectangle (renderer->cr, x1u, y1u, x2u - x1u, y2u - y1u);
+  cairo_stroke (renderer->cr);
+}
+
+static void
+fill_pixel_rect(DiaRenderer *object,
+               int x, int y,
+               int width, int height,
+               Color *color)
+{
+#if DIA_CAIRO_WITH_PIXMAP
+  /* if we do it with cairo there is something wrong with the clipping? */
+  DiaCairoInteractiveRenderer *renderer = DIA_CAIRO_INTERACTIVE_RENDERER (object);
+  GdkGC *gc = renderer->gc;
+  GdkColor gdkcolor;
+    
+  color_convert(color, &gdkcolor);
+  gdk_gc_set_foreground(gc, &gdkcolor);
+
+  gdk_draw_rectangle (renderer->pixmap, gc, TRUE,
+                     x, y,  width, height);
+#else
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (object);
+  double x1u = x + .5, y1u = y + .5, x2u = x + width + .5, y2u = y + height + .5;
+  double lw[2];
+  lw[0] = 1; lw[1] = 0;
+  
+  cairo_device_to_user_distance (renderer->cr, &lw[0], &lw[1]);
+  cairo_set_line_width (renderer->cr, lw[0]);
+
+  cairo_device_to_user (renderer->cr, &x1u, &y1u);
+  cairo_device_to_user (renderer->cr, &x2u, &y2u);
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+  cairo_rectangle (renderer->cr, x1u, y1u, x2u - x1u, y2u - y1u);
+  cairo_fill (renderer->cr);
+#endif
+}
+
+DiaRenderer *
+dia_cairo_interactive_renderer_new (DDisplay *ddisp)
+{
+  DiaCairoRenderer *renderer;
+
+  renderer = g_object_new (DIA_TYPE_CAIRO_INTERACTIVE_RENDERER, NULL);
+  /* CP: renderer->transform = dia_transform_new (&ddisp->visible, &ddisp->zoom_factor); */
+
+  return DIA_RENDERER(renderer);
+}
\ No newline at end of file
diff --git a/app/renderer/diacairo-print.c b/app/renderer/diacairo-print.c
new file mode 100644
index 00000000..94102621
--- /dev/null
+++ b/app/renderer/diacairo-print.c
@@ -0,0 +1,252 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diacairo-print.c -- Cairo/gtk+ based printing for dia
+ * Copyright (C) 2008, Hans Breuer, <Hans Breuer 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "diacairo.h"
+#include "diacairo-print.h"
+
+#include "message.h"
+
+typedef struct _PrintData
+{
+  DiagramData *data;
+  DiaRenderer *renderer;
+} PrintData;
+
+G_GNUC_UNUSED static void
+count_objs(DiaObject *obj, DiaRenderer *renderer, int active_layer, guint *nobjs)
+{
+  (*nobjs)++;
+}
+
+/* Dia has it's own thing */
+static void
+_dia_to_gtk_page_setup (const DiagramData *data, GtkPageSetup *setup)
+{
+  GtkPaperSize *paper_size;
+  const double points_per_cm = 28.346457;
+  const PaperInfo *paper = &(data->paper);
+  int index = find_paper (paper->name);
+  if (index < 0)
+    index = get_default_paper ();
+  paper_size = gtk_paper_size_new_from_ppd (
+                paper->name, paper->name, 
+                 get_paper_pswidth (index) * points_per_cm, 
+                get_paper_psheight (index) * points_per_cm);
+
+  gtk_page_setup_set_orientation (setup, data->paper.is_portrait ?
+    GTK_PAGE_ORIENTATION_PORTRAIT : GTK_PAGE_ORIENTATION_LANDSCAPE);
+  gtk_page_setup_set_paper_size (setup, paper_size);
+
+  gtk_page_setup_set_left_margin (setup, data->paper.lmargin * 10, GTK_UNIT_MM);
+  gtk_page_setup_set_top_margin (setup, data->paper.tmargin * 10, GTK_UNIT_MM);
+  gtk_page_setup_set_right_margin (setup, data->paper.rmargin * 10, GTK_UNIT_MM);
+  gtk_page_setup_set_bottom_margin (setup, data->paper.bmargin * 10, GTK_UNIT_MM);
+  
+}
+
+static void
+begin_print (GtkPrintOperation *operation,
+             GtkPrintContext   *context,
+             PrintData         *print_data)
+{
+  DiaCairoRenderer *cairo_renderer;
+  g_return_if_fail (print_data->renderer != NULL);
+  cairo_renderer = DIA_CAIRO_RENDERER (print_data->renderer);
+  g_return_if_fail (cairo_renderer->cr == NULL);
+
+  /* the renderer wants it's own reference */
+#if 0 /* no alpha with printers */
+  cairo_renderer->with_alpha = TRUE;
+#endif
+  cairo_renderer->cr = cairo_reference (gtk_print_context_get_cairo_context (context));
+  cairo_renderer->dia = print_data->data;
+#if 0 /* needs some text size scaling ... */
+  cairo_renderer->layout = gtk_print_context_create_pango_layout (context);
+#endif
+
+  /* scaling - as usual I don't get it, or do I? */
+  cairo_renderer->scale = (
+      gtk_page_setup_get_paper_width (gtk_print_context_get_page_setup (context), GTK_UNIT_MM) 
+      - gtk_page_setup_get_left_margin( gtk_print_context_get_page_setup (context), GTK_UNIT_MM)
+      - gtk_page_setup_get_right_margin( gtk_print_context_get_page_setup (context), GTK_UNIT_MM)
+      ) / print_data->data->paper.width;
+  cairo_renderer->skip_show_page = TRUE;
+}
+
+static void
+draw_page (GtkPrintOperation *operation,
+          GtkPrintContext *context,
+          int page_nr,
+          PrintData *print_data)
+{
+  Rectangle bounds;
+  DiagramData *data = print_data->data;
+  int x, y;
+  /* the effective sizes - dia already applied is_portrait */
+  double dp_width = data->paper.width;
+  double dp_height = data->paper.height;
+
+  DiaCairoRenderer *cairo_renderer;
+  g_return_if_fail (print_data->renderer != NULL);
+  cairo_renderer = DIA_CAIRO_RENDERER (print_data->renderer);
+
+  if (data->paper.fitto) {
+    x = page_nr % data->paper.fitwidth;
+    y = page_nr / data->paper.fitwidth;
+    
+    bounds.left = dp_width * x + data->extents.left;
+    bounds.top = dp_height * y + data->extents.top;
+    bounds.right = bounds.left + dp_width;
+    bounds.bottom = bounds.top + dp_height;
+  } else {
+    double dx, dy;
+    int nx = ceil((data->extents.right - data->extents.left) / dp_width);
+    x = page_nr % nx;
+    y = page_nr / nx;
+
+    /* Respect the original pagination as shown by the page guides.
+     * Caclulate the offset between page origin 0,0 and data.extents.topleft. 
+     * For the usual first page this boils down to lefttop=(0,0) but beware
+     * the origin being negative.
+     */
+    dx = fmod(data->extents.left, dp_width);
+    if (dx < 0.0)
+      dx += dp_width;
+    dy = fmod(data->extents.top, dp_height);
+    if (dy < 0.0)
+      dy += dp_height;
+
+    bounds.left = dp_width * x + data->extents.left - dx;
+    bounds.top = dp_height * y + data->extents.top - dy;
+    bounds.right = bounds.left + dp_width;
+    bounds.bottom = bounds.top + dp_height;
+  }
+
+#if 0 /* calls begin/end of the given renderer */
+  /* count the number of objects in this region */
+  data_render(data, print_data->renderer, &bounds,
+              (ObjectRenderer) count_objs, &nobjs);
+  if (!nobjs)
+    return; /* not printing empty pages */
+#endif
+
+  /* setup a clipping rect */
+  {
+    GtkPageSetup *setup = gtk_print_context_get_page_setup (context);
+    double left = gtk_page_setup_get_left_margin (setup, GTK_UNIT_MM);
+    double top = gtk_page_setup_get_top_margin (setup, GTK_UNIT_MM);
+    double width = gtk_page_setup_get_paper_width (setup, GTK_UNIT_MM) 
+                  - left - gtk_page_setup_get_right_margin (setup, GTK_UNIT_MM);
+    double height = gtk_page_setup_get_paper_height (setup, GTK_UNIT_MM) 
+                   - top - gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_MM);
+    cairo_save (cairo_renderer->cr);
+    /* we are still in the gtk-print coordinate system */
+#if 1
+    /* ... but apparently already transalted to top,left */
+    top = left = 0;
+#endif
+    cairo_rectangle (cairo_renderer->cr, left, top, width, height);
+    
+    cairo_clip (cairo_renderer->cr);
+  }
+
+  {
+    Rectangle extents = data->extents;
+
+    data->extents = bounds;
+    /* render only the region, FIXME: better way than modifying DiagramData ?  */
+    data_render(data, print_data->renderer, &bounds, NULL, NULL);
+    data->extents = extents;
+  }
+  cairo_restore (cairo_renderer->cr);
+}
+
+static void
+end_print (GtkPrintOperation *operation,
+           GtkPrintContext   *context,
+           PrintData         *print_data)
+{
+  g_object_unref (print_data->data);
+  g_object_unref (print_data->renderer);
+
+  g_free (print_data);
+}
+
+GtkPrintOperation *
+create_print_operation (DiagramData *data, const char *name)
+{
+  PrintData *print_data;
+  GtkPrintOperation *operation;
+  GtkPageSetup * setup;
+  int num_pages;
+
+  /* gets deleted in end_print */
+  print_data = g_new0 (PrintData, 1);
+  print_data->data = g_object_ref (data);
+  print_data->renderer = g_object_new (DIA_TYPE_CAIRO_RENDERER, NULL);
+  
+  operation = gtk_print_operation_new ();
+  
+  gtk_print_operation_set_job_name (operation, name);
+
+  setup = gtk_print_operation_get_default_page_setup (operation);
+  if (!setup)
+    setup = gtk_page_setup_new ();
+  _dia_to_gtk_page_setup (print_data->data, setup);
+  gtk_print_operation_set_default_page_setup (operation, setup);
+  g_object_unref (setup);
+
+  /* similar logic draw_page() but we need to set the total pages in advance */
+  if (data->paper.fitto) {
+    num_pages = data->paper.fitwidth * data->paper.fitheight;
+  } else {
+    int nx = ceil((data->extents.right - data->extents.left) / data->paper.width);
+    int ny = ceil((data->extents.bottom - data->extents.top) / data->paper.height);
+    num_pages = nx * ny;
+  }
+  gtk_print_operation_set_n_pages (operation, num_pages);
+
+  gtk_print_operation_set_unit (operation, GTK_UNIT_MM);
+
+  g_signal_connect (operation, "draw_page", G_CALLBACK (draw_page), print_data);
+  g_signal_connect (operation, "begin_print", G_CALLBACK (begin_print), print_data);
+  g_signal_connect (operation, "end_print", G_CALLBACK (end_print), print_data);
+  
+  return operation;
+}
+
+ObjectChange *
+cairo_print_callback (DiagramData *data,
+                      const gchar *filename,
+                      guint flags, /* further additions */
+                      void *user_data)
+{
+  GtkPrintOperation *op = create_print_operation (data, filename ? filename : "diagram");
+  GtkPrintOperationResult res;
+  GError *error = NULL;
+  
+  res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, &error);
+  if (GTK_PRINT_OPERATION_RESULT_ERROR == res) {
+    message_error ("%s", error->message);
+    g_error_free (error);
+  }
+  return NULL;
+}
diff --git a/app/renderer/diacairo-print.h b/app/renderer/diacairo-print.h
new file mode 100644
index 00000000..9725e51d
--- /dev/null
+++ b/app/renderer/diacairo-print.h
@@ -0,0 +1,13 @@
+
+#include <gtk/gtk.h>
+#include "diagramdata.h"
+
+GtkPrintOperation *
+create_print_operation (DiagramData *data, 
+                       const char *name);
+
+ObjectChange *
+cairo_print_callback (DiagramData *dia,
+                      const gchar *filename,
+                      guint flags, /* further additions */
+                      void *user_data);
diff --git a/app/renderer/diacairo-renderer.c b/app/renderer/diacairo-renderer.c
new file mode 100644
index 00000000..33c77b6c
--- /dev/null
+++ b/app/renderer/diacairo-renderer.c
@@ -0,0 +1,1242 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diacairo.c -- Cairo based export plugin for dia
+ * Copyright (C) 2004, Hans Breuer, <Hans Breuer Org>
+ *   based on wpg.c 
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <errno.h>
+#define G_LOG_DOMAIN "DiaCairo"
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <pango/pangocairo.h>
+
+#include <cairo.h>
+/* some backend headers, win32 missing in official Cairo */
+#include <cairo-svg.h>
+#ifdef  CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+#ifdef  CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+
+#include "intl.h"
+#include "message.h"
+#include "geometry.h"
+#include "dia_image.h"
+#include "diarenderer.h"
+#include "filter.h"
+#include "plug-ins.h"
+#include "object.h" /* only for object->ops->draw */
+#include "pattern.h"
+
+#include "diacairo.h"
+
+static void ensure_minimum_one_device_unit(DiaCairoRenderer *renderer, real *value);
+
+/* 
+ * render functions 
+ */ 
+static void
+begin_render(DiaRenderer *self, const Rectangle *update)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  real onedu = 0.0;
+  real lmargin = 0.0, tmargin = 0.0;
+  gboolean paginated = renderer->surface && /* only with our own pagination, not GtkPrint */
+    cairo_surface_get_type (renderer->surface) == CAIRO_SURFACE_TYPE_PDF && !renderer->skip_show_page;
+  Color background = color_white;
+
+  if (renderer->surface && !renderer->cr)
+    renderer->cr = cairo_create (renderer->surface);
+  else
+    g_assert (renderer->cr);
+
+  /* remember current state, so we can start from new with every page */
+  cairo_save (renderer->cr);
+
+  if (paginated && renderer->dia) {
+    DiagramData *data = renderer->dia;
+    /* Dia's paper.width already contains the scale, cairo needs it without 
+     * Similar for margins, Dia's without, but cairo wants them?
+     */
+    real width = (data->paper.lmargin + data->paper.width * data->paper.scaling + data->paper.rmargin)
+          * (72.0 / 2.54) + 0.5;
+    real height = (data->paper.tmargin + data->paper.height * data->paper.scaling + data->paper.bmargin)
+           * (72.0 / 2.54) + 0.5;
+    /* "Changes the size of a PDF surface for the current (and
+     * subsequent) pages." Pagination setup? */
+    cairo_pdf_surface_set_size (renderer->surface, width, height);
+    lmargin = data->paper.lmargin / data->paper.scaling;
+    tmargin = data->paper.tmargin / data->paper.scaling;
+  }
+
+  cairo_scale (renderer->cr, renderer->scale, renderer->scale);
+  /* to ensure no clipping at top/left we need some extra gymnastics,
+   * otherwise a box with a line witdh one one pixel might loose the
+   * top/left border as in bug #147386 */
+  ensure_minimum_one_device_unit (renderer, &onedu);
+
+  if (update && paginated) {
+    cairo_rectangle (renderer->cr, lmargin, tmargin,
+                     update->right - update->left, update->bottom - update->top);
+    cairo_clip (renderer->cr);
+    cairo_translate (renderer->cr, -update->left + lmargin, -update->top + tmargin);
+  } else {
+    if (renderer->dia)
+      cairo_translate (renderer->cr, -renderer->dia->extents.left + onedu, -renderer->dia->extents.top + 
onedu);
+  }
+  /* no more blurred UML diagrams */
+  cairo_set_antialias (renderer->cr, CAIRO_ANTIALIAS_NONE);
+
+  /* clear background */
+  if (renderer->dia)
+    background = renderer->dia->bg_color;
+  if (renderer->with_alpha)
+    {
+      cairo_set_operator (renderer->cr, CAIRO_OPERATOR_SOURCE);
+      cairo_set_source_rgba (renderer->cr,
+                             background.red, 
+                             background.green, 
+                             background.blue,
+                             0.0);
+    }
+  else
+    {
+      cairo_set_source_rgba (renderer->cr,
+                             background.red, 
+                             background.green, 
+                             background.blue,
+                             1.0);
+    }
+  cairo_paint (renderer->cr);
+  if (renderer->with_alpha)
+    {
+      /* restore to default drawing */
+      cairo_set_operator (renderer->cr, CAIRO_OPERATOR_OVER);
+      cairo_set_source_rgba (renderer->cr,
+                             background.red, 
+                             background.green, 
+                             background.blue,
+                             1.0);
+    }
+  if (!renderer->layout)
+    renderer->layout = pango_cairo_create_layout (renderer->cr);
+
+  cairo_set_fill_rule (renderer->cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+#if 0 /* try to work around bug #341481 - no luck */
+  {
+    cairo_font_options_t *fo = cairo_font_options_create ();
+    cairo_get_font_options (renderer->cr, fo);
+    /* try to switch off kerning */
+    cairo_font_options_set_hint_style (fo, CAIRO_HINT_STYLE_NONE);
+    cairo_font_options_set_hint_metrics (fo, CAIRO_HINT_METRICS_OFF);
+
+    cairo_set_font_options (renderer->cr, fo);
+    cairo_font_options_destroy (fo);
+#ifdef HAVE_PANGOCAIRO_H
+    pango_cairo_update_context (renderer->cr, pango_layout_get_context (renderer->layout));
+    pango_layout_context_changed (renderer->layout);
+#endif
+  }
+#endif
+  
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+end_render(DiaRenderer *self)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  DIAG_NOTE(g_message( "end_render"));
+ 
+  if (!renderer->skip_show_page)
+    cairo_show_page (renderer->cr);
+  /* pop current state, so we can start from new with every page */
+  cairo_restore (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+/*!
+ * \brief Advertize renderers capabilities
+ *
+ * Especially with cairo this should be 'all' so this function
+ * is complaining if it will return FALSE
+ * \memberof _DiaCairoRenderer
+ */
+static gboolean 
+is_capable_to (DiaRenderer *renderer, RenderCapability cap)
+{
+  static RenderCapability warned = RENDER_HOLES;
+
+  if (RENDER_HOLES == cap)
+    return TRUE;
+  else if (RENDER_ALPHA == cap)
+    return TRUE;
+  else if (RENDER_AFFINE == cap)
+    return TRUE;
+  else if (RENDER_PATTERN == cap)
+    return TRUE;
+  if (cap != warned)
+    g_warning ("New capability not supported by cairo??");
+  warned = cap;
+  return FALSE;
+}
+
+/*!
+ * \brief Remember the given pattern to use for consecutive fill
+ * @param self explicit this pointer
+ * @param pattern linear or radial gradient 
+ */
+static void
+set_pattern (DiaRenderer *self, DiaPattern *pattern)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  DiaPattern *prev = renderer->pattern;
+  if (pattern)
+    renderer->pattern = g_object_ref (pattern);
+  else
+    renderer->pattern = pattern;
+  if (prev)
+    g_object_unref (prev);
+}
+
+static gboolean
+_add_color_stop (real ofs, const Color *col, gpointer user_data)
+{
+  cairo_pattern_t *pat = (cairo_pattern_t *)user_data;
+  
+  cairo_pattern_add_color_stop_rgba (pat, ofs,
+                                    col->red, col->green, col->blue, col->alpha);
+  return TRUE;
+}
+
+static cairo_pattern_t *
+_pattern_build_for_cairo (DiaPattern *pattern, const Rectangle *ext)
+{
+  cairo_pattern_t *pat;
+  DiaPatternType type;
+  guint flags;
+  Point p1, p2;
+  real r;
+
+  g_return_val_if_fail (pattern != NULL, NULL);
+
+  dia_pattern_get_settings (pattern, &type, &flags);
+  dia_pattern_get_points (pattern, &p1, &p2);
+  dia_pattern_get_radius (pattern, &r);
+
+  switch (type ) {
+  case DIA_LINEAR_GRADIENT :
+    pat = cairo_pattern_create_linear (p1.x, p1.y, p2.x, p2.y);
+    break;
+  case DIA_RADIAL_GRADIENT :
+    pat = cairo_pattern_create_radial (p2.x, p2.y, 0.0, p1.x, p1.y, r);
+    break;
+  default :
+    g_warning ("_pattern_build_for_cairo non such.");
+    return NULL;
+  }
+  /* this must only be optionally done */
+  if ((flags & DIA_PATTERN_USER_SPACE)==0) {
+    cairo_matrix_t matrix;
+    real w = ext->right - ext->left;
+    real h = ext->bottom - ext->top;
+    cairo_matrix_init (&matrix, w, 0.0, 0.0, h, ext->left, ext->top);
+    cairo_matrix_invert (&matrix);
+    cairo_pattern_set_matrix (pat, &matrix);
+  }
+  if (flags & DIA_PATTERN_EXTEND_PAD)
+    cairo_pattern_set_extend (pat, CAIRO_EXTEND_PAD);
+  else if (flags & DIA_PATTERN_EXTEND_REPEAT)
+    cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
+  else if (flags & DIA_PATTERN_EXTEND_REFLECT)
+    cairo_pattern_set_extend (pat, CAIRO_EXTEND_REFLECT);
+
+  dia_pattern_foreach (pattern, _add_color_stop, pat);
+
+  return pat;
+}
+
+/*!
+ * \brief Make use of the pattern if any
+ */
+static void
+_dia_cairo_fill (DiaCairoRenderer *renderer, gboolean preserve)
+{
+  if (!renderer->pattern) {
+    if (preserve)
+      cairo_fill_preserve (renderer->cr);
+    else
+      cairo_fill (renderer->cr);
+  } else {
+    /* maybe we should cache the cairo-pattern */
+    cairo_pattern_t *pat;
+    Rectangle fe;
+
+    /* Using the extents to scale the pattern is probably not correct */
+    cairo_fill_extents (renderer->cr, &fe.left, &fe.top, &fe.right, &fe.bottom);
+
+    pat = _pattern_build_for_cairo (renderer->pattern, &fe);
+    cairo_set_source (renderer->cr, pat);
+    if (preserve)
+      cairo_fill_preserve (renderer->cr);
+    else
+      cairo_fill (renderer->cr);
+    cairo_pattern_destroy (pat);
+  }
+}
+
+/*!
+ * \brief Render the given object optionally transformed by matrix
+ * @param self explicit this pointer
+ * @param object the _DiaObject to draw
+ * @param matrix the transformation matrix to use or NULL
+ */
+static void
+draw_object (DiaRenderer *self, DiaObject *object, DiaMatrix *matrix)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  cairo_matrix_t before;
+  
+  if (matrix) {
+    /* at least in SVG the intent of an invalid matrix is not rendering */
+    if (!dia_matrix_is_invertible(matrix))
+      return;
+    cairo_get_matrix (renderer->cr, &before);
+    g_assert (sizeof(cairo_matrix_t) == sizeof(DiaMatrix));
+    cairo_transform (renderer->cr, (cairo_matrix_t *)matrix);
+  }
+  object->ops->draw(object, DIA_RENDERER (renderer));
+  if (matrix)
+    cairo_set_matrix (renderer->cr, &before);
+}
+
+/*!
+ * \brief Ensure a minimum of one device unit
+ * Dia as well as many other drawing applications/libraries is using a
+ * line with 0f 0.0 tho mean hairline. Cairo doe not have this capability
+ * so this functions should be used to get the thinnest line possible.
+ * \protected \memberof _DiaCairoRenderer
+ */
+static void
+ensure_minimum_one_device_unit(DiaCairoRenderer *renderer, real *value)
+{
+  double ax = 1., ay = 1.;
+
+  cairo_device_to_user_distance (renderer->cr, &ax, &ay);
+
+  ax = MAX(ax, ay);
+  if (*value < ax)
+      *value = ax;
+}
+
+static void
+set_linewidth(DiaRenderer *self, real linewidth)
+{  
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  DIAG_NOTE(g_message("set_linewidth %f", linewidth));
+
+  /* make hairline? Everythnig below one device unit get the same width,
+   * otherwise 0.0 may end up thicker than 0.0+epsilon
+   */
+  ensure_minimum_one_device_unit(renderer, &linewidth);
+
+  cairo_set_line_width (renderer->cr, linewidth);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+set_linecaps(DiaRenderer *self, LineCaps mode)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  DIAG_NOTE(g_message("set_linecaps %d", mode));
+
+  switch(mode) {
+  case LINECAPS_DEFAULT:
+  case LINECAPS_BUTT:
+    cairo_set_line_cap (renderer->cr, CAIRO_LINE_CAP_BUTT);
+    break;
+  case LINECAPS_ROUND:
+    cairo_set_line_cap (renderer->cr, CAIRO_LINE_CAP_ROUND);
+    break;
+  case LINECAPS_PROJECTING:
+    cairo_set_line_cap (renderer->cr, CAIRO_LINE_CAP_SQUARE); /* ?? */
+    break;
+  default:
+    g_warning("DiaCairoRenderer : Unsupported caps mode specified!\n");
+  }
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+set_linejoin(DiaRenderer *self, LineJoin mode)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  DIAG_NOTE(g_message("set_join %d", mode));
+
+  switch(mode) {
+  case LINEJOIN_DEFAULT:
+  case LINEJOIN_MITER:
+    cairo_set_line_join (renderer->cr, CAIRO_LINE_JOIN_MITER);
+    break;
+  case LINEJOIN_ROUND:
+    cairo_set_line_join (renderer->cr, CAIRO_LINE_JOIN_ROUND);
+    break;
+  case LINEJOIN_BEVEL:
+    cairo_set_line_join (renderer->cr, CAIRO_LINE_JOIN_BEVEL);
+    break;
+  default:
+    g_warning("DiaCairoRenderer : Unsupported join mode specified!\n");
+  }
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+set_linestyle(DiaRenderer *self, LineStyle mode, real dash_length)
+{
+  /* dot = 10% of len */
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  double dash[6];
+
+  DIAG_NOTE(g_message("set_linestyle %d", mode));
+
+  ensure_minimum_one_device_unit(renderer, &dash_length);
+  /* line type */
+  switch (mode) {
+  case LINESTYLE_DEFAULT:
+  case LINESTYLE_SOLID:
+    cairo_set_dash (renderer->cr, NULL, 0, 0);
+    break;
+  case LINESTYLE_DASHED:
+    dash[0] = dash_length;
+    dash[1] = dash_length;
+    cairo_set_dash (renderer->cr, dash, 2, 0);
+    break;
+  case LINESTYLE_DASH_DOT:
+    dash[0] = dash_length;
+    dash[1] = dash_length * 0.45;
+    dash[2] = dash_length * 0.1;
+    dash[3] = dash_length * 0.45;
+    cairo_set_dash (renderer->cr, dash, 4, 0);
+    break;
+  case LINESTYLE_DASH_DOT_DOT:
+    dash[0] = dash_length;
+    dash[1] = dash_length * (0.8/3);
+    dash[2] = dash_length * 0.1;
+    dash[3] = dash_length * (0.8/3);
+    dash[4] = dash_length * 0.1;
+    dash[5] = dash_length * (0.8/3);
+    cairo_set_dash (renderer->cr, dash, 6, 0);
+    break;
+  case LINESTYLE_DOTTED:
+    dash[0] = dash_length * 0.1;
+    dash[1] = dash_length * 0.1;
+    cairo_set_dash (renderer->cr, dash, 2, 0);
+    break;
+  default:
+    g_warning("DiaCairoRenderer : Unsupported line style specified!\n");
+  }
+  DIAG_STATE(renderer->cr)
+}
+
+/*!
+ * \brief Set the fill style
+ * The fill style is one of the areas, where the cairo library offers a lot
+ * more the Dia currently uses. Cairo can render repeating patterns as well
+ * as linear and radial gradients. As of this writing Dia just uses solid
+ * color fill.
+ * \memberof _DiaCairoRenderer
+ */
+static void
+set_fillstyle(DiaRenderer *self, FillStyle mode)
+{
+  DIAG_NOTE(g_message("set_fillstyle %d", mode));
+
+  switch(mode) {
+  case FILLSTYLE_SOLID:
+    /* FIXME: how to set _no_ pattern ?
+      * cairo_set_pattern (renderer->cr, NULL);
+      */
+    break;
+  default:
+    g_warning("DiaCairoRenderer : Unsupported fill mode specified!\n");
+  }
+  DIAG_STATE(DIA_CAIRO_RENDERER (self)->cr)
+}
+
+/* There is a recurring bug with pangocairo related to kerning and font scaling.
+ * See: https://bugzilla.gnome.org/buglist.cgi?quicksearch=341481+573261+700592
+ * Rather than waiting for another fix let's try to implement the ultimate work
+ * around. With Pango-1.32 and HarfBuzz the kludge in Pango is gone and apparently
+ * substituted with a precision problem. If we now use huge fonts when talking
+ * to Pango and downscale these with cairo it should work with all Pango versions.
+ */
+#define FONT_SIZE_TWEAK (72.0)
+
+static void
+set_font(DiaRenderer *self, DiaFont *font, real height)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  /* pango/cairo wants the font size, not the (line-) height */
+  real size = dia_font_get_size (font) * (height / dia_font_get_height (font));
+
+  PangoFontDescription *pfd = pango_font_description_copy (dia_font_get_description (font));
+  DIAG_NOTE(g_message("set_font %f %s", height, dia_font_get_family(font)));
+
+  /* select font and size */
+  pango_font_description_set_absolute_size (pfd, (int)(size * FONT_SIZE_TWEAK * PANGO_SCALE));
+  pango_layout_set_font_description (renderer->layout, pfd);
+  pango_font_description_free (pfd);
+
+  /* for the interactive case we must maintain the font field in the base class */
+  if (self->is_interactive) {
+    dia_font_ref(font);
+    if (self->font)
+      dia_font_unref(self->font);
+    self->font = font;
+    self->font_height = height;
+  }
+}
+
+static void
+draw_line(DiaRenderer *self, 
+          Point *start, Point *end, 
+          Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  DIAG_NOTE(g_message("draw_line %f,%f -> %f, %f", 
+            start->x, start->y, end->x, end->y));
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+  if (!renderer->stroke_pending) /* use current point from previous drawing command */
+    cairo_move_to (renderer->cr, start->x, start->y);
+  cairo_line_to (renderer->cr, end->x, end->y);
+  if (!renderer->stroke_pending)
+    cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+draw_polyline(DiaRenderer *self, 
+              Point *points, int num_points, 
+              Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  int i;
+
+  DIAG_NOTE(g_message("draw_polyline n:%d %f,%f ...", 
+            num_points, points->x, points->y));
+
+  g_return_if_fail(1 < num_points);
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+
+  cairo_new_path (renderer->cr);
+  /* point data */
+  cairo_move_to (renderer->cr, points[0].x, points[0].y);
+  for (i = 1; i < num_points; i++)
+    {
+      cairo_line_to (renderer->cr, points[i].x, points[i].y);
+    }
+  cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+_polygon(DiaRenderer *self, 
+         Point *points, int num_points, 
+         Color *color,
+         gboolean fill)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  int i;
+
+  DIAG_NOTE(g_message("%s_polygon n:%d %f,%f ...",
+            fill ? "fill" : "draw",
+            num_points, points->x, points->y));
+
+  g_return_if_fail(1 < num_points);
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+
+  cairo_new_path (renderer->cr);
+  /* point data */
+  cairo_move_to (renderer->cr, points[0].x, points[0].y);
+  for (i = 1; i < num_points; i++)
+    {
+      cairo_line_to (renderer->cr, points[i].x, points[i].y);
+    }
+  cairo_line_to (renderer->cr, points[0].x, points[0].y);
+  cairo_close_path (renderer->cr);
+  if (fill)
+    _dia_cairo_fill (renderer, FALSE);
+  else
+    cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+draw_polygon(DiaRenderer *self, 
+             Point *points, int num_points, 
+             Color *fill, Color *stroke)
+{
+  if (fill)
+    _polygon (self, points, num_points, fill, TRUE);
+  if (stroke)
+    _polygon (self, points, num_points, stroke, FALSE);
+}
+
+static void
+_rect(DiaRenderer *self, 
+      Point *ul_corner, Point *lr_corner,
+      Color *color,
+      gboolean fill)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  DIAG_NOTE(g_message("%s_rect %f,%f -> %f,%f", 
+            fill ? "fill" : "draw",
+            ul_corner->x, ul_corner->y, lr_corner->x, lr_corner->y));
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+  
+  cairo_rectangle (renderer->cr, 
+                   ul_corner->x, ul_corner->y, 
+                   lr_corner->x - ul_corner->x, lr_corner->y - ul_corner->y);
+
+  if (fill)
+    _dia_cairo_fill (renderer, FALSE);
+  else
+    cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+draw_rect(DiaRenderer *self, 
+          Point *ul_corner, Point *lr_corner,
+          Color *fill, Color *stroke)
+{
+  if (fill)
+    _rect (self, ul_corner, lr_corner, fill, TRUE);
+  if (stroke)
+    _rect (self, ul_corner, lr_corner, stroke, FALSE);
+}
+
+static void
+draw_arc(DiaRenderer *self, 
+        Point *center,
+        real width, real height,
+        real angle1, real angle2,
+        Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  Point start;
+  double a1, a2;
+  real onedu = 0.0;
+
+  DIAG_NOTE(g_message("draw_arc %fx%f <%f,<%f", 
+            width, height, angle1, angle2));
+
+  g_return_if_fail (!isnan (angle1) && !isnan (angle2));
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+
+  if (!renderer->stroke_pending)
+    cairo_new_path (renderer->cr);
+  start.x = center->x + (width / 2.0)  * cos((M_PI / 180.0) * angle1);
+  start.y = center->y - (height / 2.0) * sin((M_PI / 180.0) * angle1);
+  if (!renderer->stroke_pending) /* when activated the first current point must be set */
+    cairo_move_to (renderer->cr, start.x, start.y);
+  a1 = - (angle1 / 180.0) * G_PI;
+  a2 = - (angle2 / 180.0) * G_PI;
+  /* FIXME: to handle width != height some cairo_scale/cairo_translate would be needed */
+  ensure_minimum_one_device_unit (renderer, &onedu);
+  /* FIXME2: with too small arcs cairo goes into an endless loop */
+  if (height/2.0 > onedu && width/2.0 > onedu) {
+    if (angle2 > angle1)
+      cairo_arc_negative (renderer->cr, center->x, center->y, 
+                         width > height ? height / 2.0 : width / 2.0, /* FIXME 2nd radius */
+                         a1, a2);
+    else
+      cairo_arc (renderer->cr, center->x, center->y, 
+                width > height ? height / 2.0 : width / 2.0, /* FIXME 2nd radius */
+                a1, a2);
+  }
+  if (!renderer->stroke_pending)
+    cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+fill_arc(DiaRenderer *self, 
+         Point *center,
+         real width, real height,
+         real angle1, real angle2,
+         Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  Point start;
+  double a1, a2;
+
+  DIAG_NOTE(g_message("draw_arc %fx%f <%f,<%f", 
+            width, height, angle1, angle2));
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+  
+  cairo_new_path (renderer->cr);
+  start.x = center->x + (width / 2.0)  * cos((M_PI / 180.0) * angle1);
+  start.y = center->y - (height / 2.0) * sin((M_PI / 180.0) * angle1);
+  cairo_move_to (renderer->cr, center->x, center->y);
+  cairo_line_to (renderer->cr, start.x, start.y);
+  a1 = - (angle1 / 180.0) * G_PI;
+  a2 = - (angle2 / 180.0) * G_PI;
+  /* FIXME: to handle width != height some cairo_scale/cairo_translate would be needed */
+  if (angle2 > angle1)
+    cairo_arc_negative (renderer->cr, center->x, center->y, 
+                       width > height ? height / 2.0 : width / 2.0, /* XXX 2nd radius */
+                       a1, a2);
+  else
+    cairo_arc (renderer->cr, center->x, center->y, 
+              width > height ? height / 2.0 : width / 2.0, /* XXX 2nd radius */
+              a1, a2);
+  cairo_line_to (renderer->cr, center->x, center->y);
+  cairo_close_path (renderer->cr);
+  _dia_cairo_fill (renderer, FALSE);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+_ellipse(DiaRenderer *self, 
+         Point *center,
+         real width, real height,
+         Color *color,
+         gboolean fill)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  DIAG_NOTE(g_message("%s_ellipse %fx%f center @ %f,%f", 
+            fill ? "fill" : "draw", width, height, center->x, center->y));
+
+  /* avoid screwing cairo context - I'd say restore should fix it again, but it doesn't
+   * (dia.exe:3152): DiaCairo-WARNING **: diacairo-renderer.c:254, invalid matrix (not invertible)
+   */
+  if (!(width > 0. && height > 0.))
+    return;
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+
+  cairo_save (renderer->cr);
+  /* don't create a line from the current point to the beginning 
+   * of the ellipse */
+  cairo_new_sub_path (renderer->cr);
+  /* copied straight from cairo's documentation, and fixed the bug there */
+  cairo_translate (renderer->cr, center->x, center->y);
+  cairo_scale (renderer->cr, width / 2., height / 2.);
+  cairo_arc (renderer->cr, 0., 0., 1., 0., 2 * G_PI);
+  cairo_restore (renderer->cr);
+
+  if (fill)
+    _dia_cairo_fill (renderer, FALSE);
+  else
+    cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+draw_ellipse(DiaRenderer *self, 
+             Point *center,
+             real width, real height,
+             Color *fill, Color *stroke)
+{
+  if (fill)
+    _ellipse (self, center, width, height, fill, TRUE);
+  if (stroke)
+    _ellipse (self, center, width, height, stroke, FALSE);
+}
+
+static void
+_bezier(DiaRenderer *self, 
+        BezPoint *points,
+        int numpoints,
+        Color *color,
+        gboolean fill,
+        gboolean closed)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  int i;
+
+  DIAG_NOTE(g_message("%s_bezier n:%d %fx%f ...", 
+            fill ? "fill" : "draw", numpoints, points->p1.x, points->p1.y));
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+
+  cairo_new_path (renderer->cr);
+  for (i = 0; i < numpoints; i++)
+  {
+    switch (points[i].type)
+    {
+    case BEZ_MOVE_TO:
+      cairo_move_to (renderer->cr, points[i].p1.x, points[i].p1.y);
+      break;
+    case BEZ_LINE_TO:
+      cairo_line_to (renderer->cr, points[i].p1.x, points[i].p1.y);
+      break;
+    case BEZ_CURVE_TO:
+      cairo_curve_to (renderer->cr, 
+                      points[i].p1.x, points[i].p1.y,
+                      points[i].p2.x, points[i].p2.y,
+                      points[i].p3.x, points[i].p3.y);
+      break;
+    default :
+      g_assert_not_reached ();
+    }
+  }
+
+  if (closed)
+    cairo_close_path(renderer->cr);
+  if (fill)
+    _dia_cairo_fill (renderer, FALSE);
+  else
+    cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+static void
+draw_bezier(DiaRenderer *self, 
+            BezPoint *points,
+            int numpoints,
+            Color *color)
+{
+  _bezier (self, points, numpoints, color, FALSE, FALSE);
+}
+
+static void
+draw_beziergon (DiaRenderer *self, 
+               BezPoint *points,
+               int numpoints,
+               Color *fill,
+               Color *stroke)
+{
+  if (fill)
+    _bezier (self, points, numpoints, fill, TRUE, TRUE);
+  /* XXX: optimize if line_width is zero and fill==stroke */
+  if (stroke)
+    _bezier (self, points, numpoints, stroke, FALSE, TRUE);
+}
+
+static void
+draw_string(DiaRenderer *self,
+            const char *text,
+            Point *pos, Alignment alignment,
+            Color *color)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  int len = strlen(text);
+
+  DIAG_NOTE(g_message("draw_string(%d) %f,%f %s", 
+            len, pos->x, pos->y, text));
+
+  if (len < 1) return; /* shouldn't this be handled by Dia's core ? */
+
+  cairo_set_source_rgba (renderer->cr, color->red, color->green, color->blue, color->alpha);
+  cairo_save (renderer->cr);
+  /* alignment calculation done by pangocairo? */
+  pango_layout_set_alignment (renderer->layout, alignment == ALIGN_CENTER ? PANGO_ALIGN_CENTER :
+                                                alignment == ALIGN_RIGHT ? PANGO_ALIGN_RIGHT : 
PANGO_ALIGN_LEFT);
+  pango_layout_set_text (renderer->layout, text, len);
+  {
+    PangoLayoutIter *iter = pango_layout_get_iter(renderer->layout);
+    int bline = pango_layout_iter_get_baseline(iter);
+    /* although we give the alignment above we need to adjust the start point */
+    PangoRectangle extents;
+    int shift;
+    pango_layout_iter_get_line_extents (iter, NULL, &extents);
+    shift = alignment == ALIGN_CENTER ? PANGO_RBEARING(extents)/2 :
+            alignment == ALIGN_RIGHT ? PANGO_RBEARING(extents) : 0;
+    shift /= FONT_SIZE_TWEAK;
+    bline /= FONT_SIZE_TWEAK;
+    cairo_move_to (renderer->cr, pos->x - (double)shift / PANGO_SCALE, pos->y - (double)bline / PANGO_SCALE);
+    pango_layout_iter_free (iter);
+  }
+  /* does this hide bug #341481? */
+  cairo_scale (renderer->cr, 1.0/FONT_SIZE_TWEAK, 1.0/FONT_SIZE_TWEAK);
+  pango_cairo_update_layout (renderer->cr, renderer->layout);
+
+  pango_cairo_show_layout (renderer->cr, renderer->layout);
+  /* restoring the previous scale */
+  cairo_restore (renderer->cr);
+
+  DIAG_STATE(renderer->cr)
+}
+
+static cairo_surface_t *
+_image_to_mime_surface (DiaCairoRenderer *renderer,
+                       DiaImage         *image)
+{
+  cairo_surface_type_t st;
+  const char *fname = dia_image_filename (image);
+  int w = dia_image_width(image);
+  int h = dia_image_height(image);
+  const char *mime_type = NULL;
+
+  if (!renderer->surface)
+    return NULL;
+
+  /* We only use the "mime" surface if:
+   *  - the target supports it
+   *  - we have a file name with a supported format
+   *  - the cairo version is new enough including
+   *    http://cgit.freedesktop.org/cairo/commit/?id=35e0a2685134
+   */
+  if (g_str_has_suffix (fname, ".jpg") || g_str_has_suffix (fname, ".jpeg"))
+    mime_type = CAIRO_MIME_TYPE_JPEG;
+  else if (g_str_has_suffix (fname, ".png"))
+    mime_type = CAIRO_MIME_TYPE_PNG;
+  st = cairo_surface_get_type (renderer->surface);
+  if (   mime_type
+      && cairo_version() >= CAIRO_VERSION_ENCODE(1, 12, 18)
+      && (CAIRO_SURFACE_TYPE_PDF == st || CAIRO_SURFACE_TYPE_SVG == st))
+    {
+      cairo_surface_t *surface;
+      gchar *data = NULL;
+      gsize  length = 0;
+
+      /* we still ned to create the image surface, but dont need to fill it */
+      surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
+      cairo_surface_mark_dirty (surface); /* no effect */
+      if (   g_file_get_contents (fname, &data, &length, NULL)
+          && cairo_surface_set_mime_data (surface, mime_type,
+                                         (unsigned char *)data,
+                                         length, g_free, data) == CAIRO_STATUS_SUCCESS)
+        return surface;
+      cairo_surface_destroy (surface);
+      g_free (data);
+    }
+  return NULL;
+}
+
+static void
+draw_rotated_image (DiaRenderer *self,
+                   Point *point,
+                   real width, real height,
+                   real angle,
+                   DiaImage *image)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  cairo_surface_t *surface;
+  guint8 *data;
+  int w = dia_image_width(image);
+  int h = dia_image_height(image);
+  int rs = dia_image_rowstride(image);
+
+  DIAG_NOTE(g_message("draw_image %fx%f [%d(%d),%d] @%f,%f", 
+            width, height, w, rs, h, point->x, point->y));
+
+  if ((surface = _image_to_mime_surface (renderer, image)) != NULL)
+    {
+      data = NULL;
+    }
+  else if (dia_image_rgba_data (image))
+    {
+      const guint8 *p1 = dia_image_rgba_data (image);
+      /* we need to make a copy to rearrange channels ... */
+      guint8 *p2 = data = g_try_malloc (h * rs);
+      int i;
+
+      if (!data) 
+        {
+          message_warning (_("Not enough memory for image drawing."));
+         return;
+        }
+
+      for (i = 0; i < (h * rs) / 4; i++)
+        {
+#  if G_BYTE_ORDER == G_LITTLE_ENDIAN
+          p2[0] = p1[2]; /* b */
+          p2[1] = p1[1]; /* g */
+          p2[2] = p1[0]; /* r */
+          p2[3] = p1[3]; /* a */
+#  else
+          p2[3] = p1[2]; /* b */
+          p2[2] = p1[1]; /* g */
+          p2[1] = p1[0]; /* r */
+          p2[0] = p1[3]; /* a */
+#  endif
+          p1+=4;
+          p2+=4;
+        }
+
+      surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, w, h, rs);
+    }
+  else
+    {
+      guint8 *p = NULL, *p2;
+      guint8 *p1 = data = dia_image_rgb_data (image);
+      /* cairo wants RGB24 32 bit aligned, so copy ... */
+      int x, y;
+
+      if (data)
+       p = p2 = g_try_malloc(h*w*4);
+       
+      if (!p)
+        {
+          message_warning (_("Not enough memory for image drawing."));
+         return;
+        }
+
+      for (y = 0; y < h; y++)
+        {
+          for (x = 0; x < w; x++)
+            {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+              /* apparently BGR is required */
+              p2[x*4  ] = p1[x*3+2];
+              p2[x*4+1] = p1[x*3+1];
+              p2[x*4+2] = p1[x*3  ];
+              p2[x*4+3] = 0x80; /* should not matter */
+#else
+              p2[x*4+3] = p1[x*3+2];
+              p2[x*4+2] = p1[x*3+1];
+              p2[x*4+1] = p1[x*3  ];
+              p2[x*4+0] = 0x80; /* should not matter */
+#endif
+            }
+          p2 += (w*4);
+          p1 += rs;
+        }
+      surface = cairo_image_surface_create_for_data (p, CAIRO_FORMAT_RGB24, w, h, w*4);
+      g_free (data);
+      data = p;
+    }
+  cairo_save (renderer->cr);
+  cairo_translate (renderer->cr, point->x, point->y);
+  cairo_scale (renderer->cr, width/w, height/h);
+  cairo_move_to (renderer->cr, 0.0, 0.0);
+  cairo_set_source_surface (renderer->cr, surface, 0.0, 0.0);
+  if (angle != 0.0)
+    {
+      DiaMatrix rotate;
+      Point center = { w/2, h/2  };
+
+      dia_matrix_set_rotate_around (&rotate, -G_PI * angle / 180.0, &center);
+      cairo_pattern_set_matrix (cairo_get_source (renderer->cr), (cairo_matrix_t *)&rotate);
+    }
+#if 0
+  /*
+   * CAIRO_FILTER_FAST: aka. CAIRO_FILTER_NEAREST
+   * CAIRO_FILTER_GOOD: maybe bilinear, "reasonable-performance filter" (default?)
+   * CAIRO_FILTER_BEST: "may not be suitable for interactive use"
+   */
+  cairo_pattern_set_filter (cairo_get_source (renderer->cr), CAIRO_FILTER_BILINEAR);
+#endif
+  cairo_paint (renderer->cr);
+  cairo_restore (renderer->cr);
+  cairo_surface_destroy (surface);
+
+  g_free (data);
+
+  DIAG_STATE(renderer->cr);
+}
+
+static void
+draw_image (DiaRenderer *self,
+           Point *point,
+           real width, real height,
+           DiaImage *image)
+{
+  draw_rotated_image (self, point, width, height, 0.0, image);
+}
+static gpointer parent_class = NULL;
+
+/*!
+ * \brief Fill and/or stroke a rectangle with rounded corner
+ * Implemented to avoid seams between arcs and lines caused by the base class
+ * working in real which than gets rounded independently to int here
+ * \memberof _DiaCairoRenderer
+ */
+static void
+draw_rounded_rect (DiaRenderer *self, 
+                  Point *ul_corner, Point *lr_corner,
+                  Color *fill, Color *stroke,
+                  real radius)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+  real r2 = (lr_corner->x - ul_corner->x) / 2.0;
+  radius = MIN(r2, radius);
+  r2 = (lr_corner->y - ul_corner->y) / 2.0;
+  radius = MIN(r2, radius);
+  if (radius < 0.0001) {
+    draw_rect (self, ul_corner, lr_corner, fill, stroke);
+    return;
+  }
+  g_return_if_fail (stroke != NULL || fill != NULL);
+  /* use base class implementation to create a path */
+  cairo_new_path (renderer->cr);
+  cairo_move_to (renderer->cr, ul_corner->x + radius, ul_corner->y);
+  renderer->stroke_pending = TRUE;
+  /* only stroke, no fill gives us the contour */
+  DIA_RENDERER_CLASS(parent_class)->draw_rounded_rect(self, 
+                                                     ul_corner, lr_corner,
+                                                     NULL, stroke ? stroke : fill, radius);
+  renderer->stroke_pending = FALSE;
+  cairo_close_path (renderer->cr);
+  if (fill) { /* if a stroke follows preserve the path */
+    cairo_set_source_rgba (renderer->cr, fill->red, fill->green, fill->blue, fill->alpha);
+    _dia_cairo_fill (renderer, stroke ? TRUE : FALSE);
+  }
+  if (stroke) {
+    cairo_set_source_rgba (renderer->cr, stroke->red, stroke->green, stroke->blue, stroke->alpha);
+    cairo_stroke (renderer->cr);
+  }
+}
+
+static void
+draw_rounded_polyline (DiaRenderer *self,
+                       Point *points, int num_points,
+                       Color *color, real radius)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (self);
+
+  cairo_new_path (renderer->cr);
+  cairo_move_to (renderer->cr, points[0].x, points[0].y);
+  /* use base class implementation */
+  renderer->stroke_pending = TRUE;
+  /* set the starting point to avoid move-to in between */
+  cairo_move_to (renderer->cr, points[0].x, points[0].y);
+  DIA_RENDERER_CLASS(parent_class)->draw_rounded_polyline (self, 
+                                                           points, num_points,
+                                                          color, radius);
+  renderer->stroke_pending = FALSE;
+  cairo_stroke (renderer->cr);
+  DIAG_STATE(renderer->cr)
+}
+
+/* gobject boiler plate */
+static void cairo_renderer_init (DiaCairoRenderer *r, void *p);
+static void cairo_renderer_class_init (DiaCairoRendererClass *klass);
+
+GType
+dia_cairo_renderer_get_type (void)
+{
+  static GType object_type = 0;
+
+  if (!object_type)
+    {
+      static const GTypeInfo object_info =
+      {
+        sizeof (DiaCairoRendererClass),
+        (GBaseInitFunc) NULL,
+        (GBaseFinalizeFunc) NULL,
+        (GClassInitFunc) cairo_renderer_class_init,
+        NULL,           /* class_finalize */
+        NULL,           /* class_data */
+        sizeof (DiaCairoRenderer),
+        0,              /* n_preallocs */
+       (GInstanceInitFunc)cairo_renderer_init /* init */
+      };
+
+      object_type = g_type_register_static (DIA_TYPE_RENDERER,
+                                            "DiaCairoRenderer",
+                                            &object_info, 0);
+    }
+  
+  return object_type;
+}
+
+static void
+cairo_renderer_init (DiaCairoRenderer *renderer, void *p)
+{
+  renderer->scale = 1.0;
+}
+
+static void
+cairo_renderer_finalize (GObject *object)
+{
+  DiaCairoRenderer *renderer = DIA_CAIRO_RENDERER (object);
+
+  cairo_destroy (renderer->cr);
+  if (renderer->surface)
+    cairo_surface_destroy (renderer->surface);
+  if (renderer->layout)
+    g_object_unref (renderer->layout);  
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+cairo_renderer_class_init (DiaCairoRendererClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  object_class->finalize = cairo_renderer_finalize;
+
+  /* renderer members */
+  renderer_class->begin_render = begin_render;
+  renderer_class->end_render   = end_render;
+  renderer_class->draw_object = draw_object;
+
+  renderer_class->set_linewidth  = set_linewidth;
+  renderer_class->set_linecaps   = set_linecaps;
+  renderer_class->set_linejoin   = set_linejoin;
+  renderer_class->set_linestyle  = set_linestyle;
+  renderer_class->set_fillstyle  = set_fillstyle;
+
+  renderer_class->set_font  = set_font;
+
+  renderer_class->draw_line    = draw_line;
+  renderer_class->draw_polygon = draw_polygon;
+  renderer_class->draw_arc     = draw_arc;
+  renderer_class->fill_arc     = fill_arc;
+  renderer_class->draw_ellipse = draw_ellipse;
+
+  renderer_class->draw_string  = draw_string;
+  renderer_class->draw_image   = draw_image;
+
+  /* medium level functions */
+  renderer_class->draw_rect = draw_rect;
+  renderer_class->draw_polyline  = draw_polyline;
+
+  renderer_class->draw_bezier   = draw_bezier;
+  renderer_class->draw_beziergon = draw_beziergon;
+
+  /* highest level functions */
+  renderer_class->draw_rounded_rect = draw_rounded_rect;
+  renderer_class->draw_rounded_polyline = draw_rounded_polyline;
+  renderer_class->draw_rotated_image = draw_rotated_image;
+
+  /* other */
+  renderer_class->is_capable_to = is_capable_to;
+  renderer_class->set_pattern = set_pattern;
+}
diff --git a/app/renderer/diacairo.c b/app/renderer/diacairo.c
new file mode 100644
index 00000000..4371b4ab
--- /dev/null
+++ b/app/renderer/diacairo.c
@@ -0,0 +1,539 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diacairo.c -- Cairo based export plugin for dia
+ * Copyright (C) 2004, Hans Breuer, <Hans Breuer Org>
+ *   based on wpg.c 
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <errno.h>
+#define G_LOG_DOMAIN "DiaCairo"
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include <cairo.h>
+/* some backend headers, win32 missing in official Cairo */
+#include <cairo-svg.h>
+#ifdef  CAIRO_HAS_PS_SURFACE
+#include <cairo-ps.h>
+#endif
+#ifdef  CAIRO_HAS_PDF_SURFACE
+#include <cairo-pdf.h>
+#endif
+#ifdef CAIRO_HAS_WIN32_SURFACE
+#include <cairo-win32.h>
+/* avoid namespace collisions */
+#define Rectangle RectangleWin32
+#endif
+#ifdef CAIRO_HAS_SCRIPT_SURFACE
+#include <cairo-script.h>
+#endif
+
+#include <pango/pangocairo.h>
+
+#include "intl.h"
+#include "geometry.h"
+#include "dia_image.h"
+#include "diarenderer.h"
+#include "filter.h"
+#include "plug-ins.h"
+
+#include "diacairo.h"
+#include "diacairo-print.h"
+
+typedef enum OutputKind
+{
+  OUTPUT_PS = 1,
+  OUTPUT_PNG,
+  OUTPUT_PNGA,
+  OUTPUT_PDF,
+  OUTPUT_WMF,
+  OUTPUT_EMF,
+  OUTPUT_CLIPBOARD,
+  OUTPUT_SVG,
+  OUTPUT_CAIRO_SCRIPT
+} OutputKind;
+
+#if defined CAIRO_HAS_WIN32_SURFACE && CAIRO_VERSION > 10510
+#define DIA_CAIRO_CAN_EMF 1
+#pragma message ("DiaCairo can EMF;)")
+#endif
+
+/* dia export funtion */
+static gboolean
+export_data(DiagramData *data, DiaContext *ctx,
+           const gchar *filename, const gchar *diafilename,
+            void* user_data)
+{
+  DiaCairoRenderer *renderer;
+  FILE *file;
+  real width, height;
+  OutputKind kind = (OutputKind)user_data;
+  /* the passed in filename is in GLib's filename encoding. On Linux everything 
+   * should be fine in passing it to the C-runtime (or cairo). On win32 GLib's
+   * filename encdong is always utf-8, so another conversion is needed.
+   */
+  gchar *filename_crt = (gchar *)filename;
+#if DIA_CAIRO_CAN_EMF
+  HDC hFileDC = NULL;
+#endif
+
+  if (kind != OUTPUT_CLIPBOARD) {
+    file = g_fopen(filename, "wb"); /* "wb" for binary! */
+
+    if (file == NULL) {
+      dia_context_add_message_with_errno(ctx, errno, _("Can't open output file %s."), 
+                                        dia_context_get_filename(ctx));
+      return FALSE;
+    }
+    fclose (file);
+#ifdef G_OS_WIN32
+    filename_crt =  g_locale_from_utf8 (filename, -1, NULL, NULL, NULL);
+    if (!filename_crt) {
+      dia_context_add_message(ctx, _("Can't convert output filename '%s' to locale encoding.\n"
+                                    "Please choose a different name to save with Cairo.\n"),
+                             dia_context_get_filename(ctx));
+      return FALSE;
+    }
+#endif
+  } /* != CLIPBOARD */
+  renderer = g_object_new (DIA_TYPE_CAIRO_RENDERER, NULL);
+  renderer->dia = data; /* FIXME: not sure if this a good idea */
+  renderer->scale = 1.0;
+
+  switch (kind) {
+#ifdef CAIRO_HAS_PS_SURFACE
+  case OUTPUT_PS :
+    width  = data->paper.width * (72.0 / 2.54) + 0.5;
+    height = data->paper.height * (72.0 / 2.54) + 0.5;
+    renderer->scale = data->paper.scaling * (72.0 / 2.54);
+    DIAG_NOTE(g_message ("PS Surface %dx%d\n", (int)width, (int)height)); 
+    renderer->surface = cairo_ps_surface_create (filename_crt,
+                                                 width, height); /*  in points? */
+    /* maybe we should increase the resolution here as well */
+    break;
+#endif  
+#if defined CAIRO_HAS_PNG_SURFACE || defined CAIRO_HAS_PNG_FUNCTIONS
+  case OUTPUT_PNGA :
+    renderer->with_alpha = TRUE;
+    /* fall through */
+  case OUTPUT_PNG :
+    /* quite arbitrary, but consistent with ../pixbuf ;-) */
+    renderer->scale = 20.0 * data->paper.scaling; 
+    width  = ceil((data->extents.right - data->extents.left) * renderer->scale) + 1;
+    height = ceil((data->extents.bottom - data->extents.top) * renderer->scale) + 1;
+    DIAG_NOTE(g_message ("PNG Surface %dx%d\n", (int)width, (int)height));
+    /* use case screwed by API shakeup. We need to special case */
+    renderer->surface = cairo_image_surface_create(
+                                               CAIRO_FORMAT_ARGB32,
+                                               (int)width, (int)height);
+    /* an extra refernce to make it survive end_render():cairo_surface_destroy() */
+    cairo_surface_reference(renderer->surface);
+    break;
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+  case OUTPUT_PDF :
+#define DPI 300.0 /* 600.0? */
+    /* I just don't get how the scaling is supposed to work, dpi versus page size ? */
+    renderer->scale = data->paper.scaling * (72.0 / 2.54);
+    /* Dia's paper.width already contains the scale, cairo needs it without 
+     * Similar for margins, Dia's without, but cairo wants them. The full
+     * extents don't matter here, because we do cairo_pdf_set_size() for every page.
+     */
+    width = (data->paper.lmargin + data->paper.width * data->paper.scaling + data->paper.rmargin)
+          * (72.0 / 2.54) + 0.5;
+    height = (data->paper.tmargin + data->paper.height * data->paper.scaling + data->paper.bmargin)
+           * (72.0 / 2.54) + 0.5;
+    DIAG_NOTE(g_message ("PDF Surface %dx%d\n", (int)width, (int)height));
+    renderer->surface = cairo_pdf_surface_create (filename_crt,
+                                                  width, height);
+    cairo_surface_set_fallback_resolution (renderer->surface, DPI, DPI);
+#undef DPI
+    break;
+#endif
+  case OUTPUT_SVG :
+    /* quite arbitrary, but consistent with ../pixbuf ;-) */
+    renderer->scale = 20.0 * data->paper.scaling; 
+    width  = ceil((data->extents.right - data->extents.left) * renderer->scale) + 1;
+    height = ceil((data->extents.bottom - data->extents.top) * renderer->scale) + 1;
+    DIAG_NOTE(g_message ("SVG Surface %dx%d\n", (int)width, (int)height));
+    /* use case screwed by API shakeup. We need to special case */
+    renderer->surface = cairo_svg_surface_create(
+                                               filename_crt,
+                                               (int)width, (int)height);
+    break;
+#ifdef CAIRO_HAS_SCRIPT_SURFACE
+  case OUTPUT_CAIRO_SCRIPT :
+    /* quite arbitrary, but consistent with ../pixbuf ;-) */
+    renderer->scale = 20.0 * data->paper.scaling; 
+    width  = (data->extents.right - data->extents.left) * renderer->scale + 0.5;
+    height = (data->extents.bottom - data->extents.top) * renderer->scale + 0.5;
+    DIAG_NOTE(g_message ("CairoScript Surface %dx%d\n", (int)width, (int)height));
+    {
+      cairo_device_t *csdev = cairo_script_create (filename_crt);
+      cairo_script_set_mode (csdev, CAIRO_SCRIPT_MODE_ASCII);
+      renderer->surface = cairo_script_surface_create(csdev, CAIRO_CONTENT_COLOR_ALPHA,
+                                                     width, height);
+      cairo_device_destroy (csdev);
+    }
+    break;
+#endif
+  /* finally cairo can render to MetaFiles */
+#if DIA_CAIRO_CAN_EMF
+  case OUTPUT_EMF :
+  case OUTPUT_WMF : /* different only on close/'play' */
+  case OUTPUT_CLIPBOARD :
+    /* NOT: renderer->with_alpha = TRUE; */
+    {
+      /* see wmf/wmf.cpp */
+      /* CreateEnhMetaFile() takes 0.01 mm, but the resulting clipboard 
+       * image is much too big, e.g. when pasting to PowerPoint. So instead
+       * of 1000 use sth smaller to scale? But that would need new scaling 
+       * for line thickness as well ... 
+       * Also there is something wrong with clipping if running on a dual screen
+       * sometimes parts of the diagram are clipped away. Not sure if this is
+       * hitting some internal width limits, maintianing the viewport ratio,
+       * but not the diagram boundaries.
+       */
+      RECT bbox = { 0, 0, 
+                   (int)((data->extents.right - data->extents.left) * data->paper.scaling * 1000.0),
+                  (int)((data->extents.bottom - data->extents.top) * data->paper.scaling * 1000.0) };
+      RECT clip;
+      /* CreateEnhMetaFile() takes resolution 0.01 mm,  */
+      hFileDC = CreateEnhMetaFile (NULL, NULL, &bbox, "DiaCairo\0Diagram\0");
+
+#if 0
+      /* On Windows 7/64 with two wide screen monitors, the clipping of the resulting 
+       * metafile is too small. Scaling the bbox or via SetWorldTransform() does not help. 
+       * Maybe we need to explitily set the clipping for cairo?
+       */
+      GetClipBox (hFileDC, &clip); /* this is the display resolution */
+      if (clip.right / (real)bbox.right > clip.bottom / (real)bbox.bottom)
+       clip.right = clip.bottom * bbox.right / bbox.bottom;
+      else
+       clip.bottom = clip.bottom * bbox.right / bbox.bottom;
+
+      IntersectClipRect(hFileDC, clip.left, clip.top, clip.right, clip.bottom);
+#endif
+      renderer->surface = cairo_win32_printing_surface_create (hFileDC);
+
+      renderer->scale = 1000.0/25.4 * data->paper.scaling;
+      if (LOBYTE (g_win32_get_windows_version()) > 0x05 ||
+         LOWORD (g_win32_get_windows_version()) > 0x0105)
+       renderer->scale *= 0.72; /* Works w/o for XP, but not on Vista/Win7 */
+    }
+    break;
+#endif
+  default :
+    /* quite arbitrary, but consistent with ../pixbuf ;-) */
+    renderer->scale = 20.0 * data->paper.scaling; 
+    width  = ceil((data->extents.right - data->extents.left) * renderer->scale) + 1;
+    height = ceil((data->extents.bottom - data->extents.top) * renderer->scale) + 1;
+    DIAG_NOTE(g_message ("Image Surface %dx%d\n", (int)width, (int)height)); 
+    renderer->surface = cairo_image_surface_create (CAIRO_FORMAT_A8, (int)width, (int)height);
+  }
+
+  /* use extents */
+  DIAG_NOTE(g_message("export_data extents %f,%f -> %f,%f", 
+            data->extents.left, data->extents.top, data->extents.right, data->extents.bottom));
+
+  if (OUTPUT_PDF == kind)
+    data_render_paginated(data, DIA_RENDERER(renderer), NULL);
+  else
+    data_render(data, DIA_RENDERER(renderer), NULL, NULL, NULL);
+
+#if defined CAIRO_HAS_PNG_FUNCTIONS
+  if (OUTPUT_PNGA == kind || OUTPUT_PNG == kind)
+    {
+      cairo_surface_write_to_png(renderer->surface, filename_crt);
+      cairo_surface_destroy(renderer->surface);
+    }
+#endif
+#if DIA_CAIRO_CAN_EMF
+  if (OUTPUT_EMF == kind) {
+    FILE* f = g_fopen(filename, "wb");
+    HENHMETAFILE hEmf = CloseEnhMetaFile(hFileDC);
+    UINT nSize = GetEnhMetaFileBits (hEmf, 0, NULL);
+    BYTE* pData = g_new(BYTE, nSize);
+    nSize = GetEnhMetaFileBits (hEmf, nSize, pData);
+    if (f) {
+      fwrite(pData,1,nSize,f);
+      fclose(f);
+    } else {
+      dia_context_add_message(ctx, _("Can't write %d bytes to %s"), nSize, filename);
+    }
+    DeleteEnhMetaFile (hEmf);
+    g_free (pData);
+  } else if (OUTPUT_WMF == kind) {
+    FILE* f = g_fopen(filename, "wb");
+    HENHMETAFILE hEmf = CloseEnhMetaFile(hFileDC);
+    HDC hdc = GetDC(NULL);
+    UINT nSize = GetWinMetaFileBits (hEmf, 0, NULL, MM_ANISOTROPIC, hdc);
+    BYTE* pData = g_new(BYTE, nSize);
+    nSize = GetWinMetaFileBits (hEmf, nSize, pData, MM_ANISOTROPIC, hdc);
+    if (f) {
+      /* FIXME: write the placeable header */
+      fwrite(pData,1,nSize,f);
+      fclose(f);
+    } else {
+      dia_context_add_message(ctx, _("Can't write %d bytes to %s"), nSize, filename);
+    }
+    ReleaseDC(NULL, hdc);
+    DeleteEnhMetaFile (hEmf);
+    g_free (pData);
+  } else if (OUTPUT_CLIPBOARD == kind) {
+    HENHMETAFILE hEmf = CloseEnhMetaFile(hFileDC);
+    if (   OpenClipboard(NULL) 
+        && EmptyClipboard() 
+        && SetClipboardData (CF_ENHMETAFILE, hEmf)
+        && CloseClipboard ()) {
+      hEmf = NULL; /* data now owned by clipboard */
+    } else {
+      dia_context_add_message(ctx, _("Clipboard copy failed"));
+      DeleteEnhMetaFile (hEmf);
+    }
+  }
+#endif
+  g_object_unref(renderer);
+  if (filename != filename_crt)
+    g_free (filename_crt);
+  return TRUE;
+}
+
+G_GNUC_UNUSED /* keep implmentation for reference, see bug 599401 */
+static gboolean
+export_print_data (DiagramData *data, DiaContext *ctx,
+                  const gchar *filename_utf8, const gchar *diafilename,
+                  void* user_data)
+{
+  OutputKind kind = (OutputKind)user_data;
+  GtkPrintOperation *op = create_print_operation (data, filename_utf8);
+  GtkPrintOperationResult res;
+  GError *error = NULL;
+
+# ifdef CAIRO_HAS_PDF_SURFACE
+  /* as of this writing the only format Gtk+ supports here is PDF */
+  g_assert (OUTPUT_PDF == kind);
+# endif
+
+  if (!data) {
+    dia_context_add_message(ctx, _("Nothing to print"));
+    return FALSE;
+  }
+
+  gtk_print_operation_set_export_filename (op, filename_utf8 ? filename_utf8 : "output.pdf");
+  res = gtk_print_operation_run (op, GTK_PRINT_OPERATION_ACTION_EXPORT, NULL, &error);
+  if (GTK_PRINT_OPERATION_RESULT_ERROR == res) {
+    dia_context_add_message(ctx, "%s", error->message);
+    g_error_free (error);
+    return FALSE;
+  }
+  return TRUE;
+}
+
+#ifdef CAIRO_HAS_PS_SURFACE
+static const gchar *ps_extensions[] = { "ps", NULL };
+static DiaExportFilter ps_export_filter = {
+    N_("Cairo PostScript"),
+    ps_extensions,
+    export_data,
+    (void*)OUTPUT_PS,
+    "cairo-ps" /* unique name */
+};
+#endif
+
+#ifdef CAIRO_HAS_PDF_SURFACE
+static const gchar *pdf_extensions[] = { "pdf", NULL };
+static DiaExportFilter pdf_export_filter = {
+    N_("Cairo Portable Document Format"),
+    pdf_extensions,
+    /* not using export_print_data() due to bug 599401 */
+    export_data,
+    (void*)OUTPUT_PDF,
+    "cairo-pdf"
+};
+#endif
+
+static const gchar *svg_extensions[] = { "svg", NULL };
+static DiaExportFilter svg_export_filter = {
+    N_("Cairo Scalable Vector Graphics"),
+    svg_extensions,
+    export_data,
+    (void*)OUTPUT_SVG,
+    "cairo-svg",
+    FILTER_DONT_GUESS /* don't use this if not asked explicit */
+};
+
+#ifdef CAIRO_HAS_SCRIPT_SURFACE
+static const gchar *cs_extensions[] = { "cs", NULL };
+static DiaExportFilter cs_export_filter = {
+    N_("CairoScript"),
+    cs_extensions,
+    export_data,
+    (void*)OUTPUT_CAIRO_SCRIPT,
+    "cairo-script",
+    FILTER_DONT_GUESS /* don't use this if not asked explicit */
+};
+#endif
+
+static const gchar *png_extensions[] = { "png", NULL };
+static DiaExportFilter png_export_filter = {
+    N_("Cairo PNG"),
+    png_extensions,
+    export_data,
+    (void*)OUTPUT_PNG,
+    "cairo-png"
+};
+
+static DiaExportFilter pnga_export_filter = {
+    N_("Cairo PNG (with alpha)"),
+    png_extensions,
+    export_data,
+    (void*)OUTPUT_PNGA,
+    "cairo-alpha-png"
+};
+
+#if DIA_CAIRO_CAN_EMF
+static const gchar *emf_extensions[] = { "emf", NULL };
+static DiaExportFilter emf_export_filter = {
+    N_("Cairo EMF"),
+    emf_extensions,
+    export_data,
+    (void*)OUTPUT_EMF,
+    "cairo-emf",
+    FILTER_DONT_GUESS /* don't use this if not asked explicit */
+};
+
+static const gchar *wmf_extensions[] = { "wmf", NULL };
+static DiaExportFilter wmf_export_filter = {
+    N_("Cairo WMF"),
+    wmf_extensions,
+    export_data,
+    (void*)OUTPUT_WMF,
+    "cairo-wmf",
+    FILTER_DONT_GUESS /* don't use this if not asked explicit */
+};
+
+static ObjectChange *
+cairo_clipboard_callback (DiagramData *data,
+                          const gchar *filename,
+                          guint flags, /* further additions */
+                          void *user_data)
+{
+  DiaContext *ctx = dia_context_new(_("Cairo Clipboard Copy"));
+
+  g_return_val_if_fail ((OutputKind)user_data == OUTPUT_CLIPBOARD, NULL);
+  g_return_val_if_fail (data != NULL, NULL);
+
+  /* filename is not necessary */
+  export_data (data, ctx, filename, filename, user_data);
+  dia_context_release (ctx);
+
+  return NULL;
+}
+
+static DiaCallbackFilter cb_clipboard = {
+   "EditCopyDiagram",
+    N_("Copy _Diagram"),
+    "/DisplayMenu/Edit/CopyDiagram",
+    cairo_clipboard_callback,
+    (void*)OUTPUT_CLIPBOARD
+};
+#endif
+
+static DiaCallbackFilter cb_gtk_print = {
+    "FilePrintGTK",
+    N_("Print (GTK) \342\200\246"),
+    "/InvisibleMenu/File/FilePrint",
+    cairo_print_callback,
+    (void*)OUTPUT_PDF
+};
+
+static gboolean
+_plugin_can_unload (PluginInfo *info)
+{
+  /* Can't unload as long as we are giving away our types,
+   * e.g. dia_cairo_interactive_renderer_get_type () */
+  return FALSE;
+}
+
+static void
+_plugin_unload (PluginInfo *info)
+{
+#ifdef CAIRO_HAS_PS_SURFACE
+  filter_unregister_export(&ps_export_filter);
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+  filter_unregister_export(&pdf_export_filter);
+#endif
+  filter_unregister_export(&svg_export_filter);
+#ifdef CAIRO_HAS_SCRIPT_SURFACE
+  filter_unregister_export(&cs_export_filter);
+#endif
+  filter_unregister_export(&png_export_filter);
+  filter_unregister_export(&pnga_export_filter);
+#if DIA_CAIRO_CAN_EMF
+  filter_unregister_export(&emf_export_filter);
+  filter_unregister_export(&wmf_export_filter);
+  filter_unregister_callback (&cb_clipboard);
+#endif
+  filter_unregister_callback (&cb_gtk_print);
+}
+
+/* --- dia plug-in interface --- */
+
+DIA_PLUGIN_CHECK_INIT
+
+PluginInitResult
+dia_plugin_init(PluginInfo *info)
+{
+  if (!dia_plugin_info_init(info, "Cairo",
+                            _("Cairo-based Rendering"),
+                            _plugin_can_unload,
+                            _plugin_unload))
+    return DIA_PLUGIN_INIT_ERROR;
+
+  /* FIXME: need to think about of proper way of registration, see also app/display.c */
+  png_export_filter.renderer_type = dia_cairo_interactive_renderer_get_type ();
+
+#ifdef CAIRO_HAS_PS_SURFACE
+  filter_register_export(&ps_export_filter);
+#endif
+#ifdef CAIRO_HAS_PDF_SURFACE
+  filter_register_export(&pdf_export_filter);
+#endif
+  filter_register_export(&svg_export_filter);
+#ifdef CAIRO_HAS_SCRIPT_SURFACE
+  filter_register_export(&cs_export_filter);
+#endif
+  filter_register_export(&png_export_filter);
+  filter_register_export(&pnga_export_filter);
+#if DIA_CAIRO_CAN_EMF
+  filter_register_export(&emf_export_filter);
+  filter_register_export(&wmf_export_filter);
+  filter_register_callback (&cb_clipboard);
+#endif
+
+  filter_register_callback (&cb_gtk_print);
+  
+  return DIA_PLUGIN_INIT_OK;
+}
diff --git a/app/renderer/diacairo.h b/app/renderer/diacairo.h
new file mode 100644
index 00000000..32d4f464
--- /dev/null
+++ b/app/renderer/diacairo.h
@@ -0,0 +1,99 @@
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diacairo.c -- Cairo based export plugin for dia
+ * Copyright (C) 2004, 2007 Hans Breuer, <Hans Breuer 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <cairo.h>
+#include "diarenderer.h"
+#include "display.h"
+
+/*
+#define DEBUG_CAIRO
+ */
+#ifdef DEBUG_CAIRO
+#  define DIAG_NOTE(action) action
+#else
+#  define DIAG_NOTE(action)
+#endif
+/* Unconditional complain about cairo being in wrong state,
+ * it usually shows some wrong assumptions in Dia's code.
+ */
+#define DIAG_STATE(cr) { \
+  if (cairo_status (cr) != CAIRO_STATUS_SUCCESS) \
+    g_warning ("%s:%d, %s\n", __FILE__, __LINE__, cairo_status_to_string (cairo_status(cr))); \
+}
+
+/* --- the renderer base class --- */
+G_BEGIN_DECLS
+
+#define DIA_TYPE_CAIRO_RENDERER           (dia_cairo_renderer_get_type ())
+#define DIA_CAIRO_RENDERER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), DIA_TYPE_CAIRO_RENDERER, 
DiaCairoRenderer))
+#define DIA_CAIRO_RENDERER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST ((klass), DIA_TYPE_CAIRO_RENDERER, 
DiaCairoRendererClass))
+#define DIA_IS_CAIRO_RENDERER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DIA_TYPE_CAIRO_RENDERER))
+#define DIA_CAIRO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DIA_CAIRO_TYPE_RENDERER, 
DiaCairoRendererClass))
+
+GType dia_cairo_renderer_get_type (void) G_GNUC_CONST;
+
+typedef struct _DiaCairoRenderer DiaCairoRenderer;
+typedef struct _DiaCairoRendererClass DiaCairoRendererClass;
+
+/*!
+ * \brief Multi format renderer based on cairo API (http://cairographics.org)
+ *
+ * The DiaCairoRenderer supports various output formats depending on the build
+ * configuration of libcairo. Typically these include SVG, PNG, PDF, PostScript
+ * and the display of the windowing system in use.
+ * Also - with a recent enough GTK+ version the cairo renderer is interfacing 
+ * the native printing subsystem.
+ * Finally - only on Windows - there is usually support for Windows Metafiles
+* (WMF and EMF), the latter used for Clipboard transport of the whole diagram.
+ * \extends _DiaRenderer
+ */
+struct _DiaCairoRenderer
+{
+  DiaRenderer parent_instance; /*!< GObject inheritance */
+
+  cairo_t *cr; /**< if NULL it gets created from the surface */
+  cairo_surface_t *surface; /**< can be NULL to use the provived cr */
+
+  DiagramData *dia; /*!< pointer to the diagram to render, might be NULL for the display case */
+
+  real scale;
+  gboolean with_alpha; /*!< define to TRUE for transparent background */
+  gboolean skip_show_page; /*!< when using for print avoid the internal show_page */
+  gboolean stroke_pending; /*!< to delay call to cairo_stroke */
+
+  /** caching the font description from set_font */
+  PangoLayout *layout;
+
+  /*! If set use for fill */
+  DiaPattern *pattern;
+};
+
+struct _DiaCairoRendererClass
+{
+  DiaRendererClass parent_class;
+};
+
+/* FIXME: need to think about proper registration */
+GType dia_cairo_interactive_renderer_get_type (void) G_GNUC_CONST;
+
+DiaRenderer *dia_cairo_interactive_renderer_new (DDisplay *ddisp);
+
+G_END_DECLS
diff --git a/plug-ins/cairo/diacairo-renderer.c b/plug-ins/cairo/diacairo-renderer.c
index 1c5c0144..33c77b6c 100644
--- a/plug-ins/cairo/diacairo-renderer.c
+++ b/plug-ins/cairo/diacairo-renderer.c
@@ -34,7 +34,6 @@
 
 #include <cairo.h>
 /* some backend headers, win32 missing in official Cairo */
-#include <cairo-png.h>
 #include <cairo-svg.h>
 #ifdef  CAIRO_HAS_PS_SURFACE
 #include <cairo-ps.h>
diff --git a/plug-ins/cairo/diacairo.c b/plug-ins/cairo/diacairo.c
index e0842d02..4371b4ab 100644
--- a/plug-ins/cairo/diacairo.c
+++ b/plug-ins/cairo/diacairo.c
@@ -32,7 +32,6 @@
 
 #include <cairo.h>
 /* some backend headers, win32 missing in official Cairo */
-#include <cairo-png.h>
 #include <cairo-svg.h>
 #ifdef  CAIRO_HAS_PS_SURFACE
 #include <cairo-ps.h>


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