[dia/cairo-port] Copy the cairo plugin into Dia core
- From: Zander <zbrown src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dia/cairo-port] Copy the cairo plugin into Dia core
- Date: Thu, 6 Dec 2018 22:06:09 +0000 (UTC)
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, ¢er);
+ 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]