summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2015-02-08 13:38:35 +0000
committerDavid Robillard <d@drobilla.net>2015-02-08 13:38:35 +0000
commit182ed04eaef3db9d0e07d466b19307e6c1b4aeb9 (patch)
treea1a4a1da31357aec0a308c7ea41b4e3eb79643ae
parent3d7cca74006eee42c9f8b6dfd23cb61d74a91945 (diff)
downloadganv-182ed04eaef3db9d0e07d466b19307e6c1b4aeb9.tar.gz
ganv-182ed04eaef3db9d0e07d466b19307e6c1b4aeb9.tar.bz2
ganv-182ed04eaef3db9d0e07d466b19307e6c1b4aeb9.zip
Add support for exporting canvas as PDF or PS.
git-svn-id: http://svn.drobilla.net/lad/trunk/ganv@5543 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--NEWS6
-rw-r--r--ganv/Canvas.hpp1
-rw-r--r--ganv/canvas.h13
-rw-r--r--ganv/wrap.hpp5
-rw-r--r--src/Canvas.cpp65
-rw-r--r--src/ganv-private.h2
-rw-r--r--src/group.c5
-rw-r--r--src/text.c87
-rw-r--r--wscript2
9 files changed, 124 insertions, 62 deletions
diff --git a/NEWS b/NEWS
index e6ea402..60df0eb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,11 +1,13 @@
-ganv (1.4.3) unstable;
+ganv (1.5.0) unstable;
* Fix positioning of embedded widgets when changing layout.
* Fix unexpected node jumping when dragging new connections.
+ * Add support for PDF and PS export.
+ * Improve text rendering at high zoom.
* Fix compilation with --no-fdgl (patch from Vlad Glagolev).
* Fix crash when destroying canvas.
- -- David Robillard <d@drobilla.net> Thu, 05 Feb 2015 18:32:13 -0500
+ -- David Robillard <d@drobilla.net> Sun, 08 Feb 2015 08:32:08 -0500
ganv (1.4.2) stable;
diff --git a/ganv/Canvas.hpp b/ganv/Canvas.hpp
index 88211d4..4a83de2 100644
--- a/ganv/Canvas.hpp
+++ b/ganv/Canvas.hpp
@@ -84,6 +84,7 @@ public:
void remove_edge(Edge* edge);
METHOD0(ganv_canvas, arrange);
+ METHODRET2(ganv_canvas, int, export_image, const char*, filename, bool, draw_background);
METHOD1(ganv_canvas, export_dot, const char*, filename);
METHODRET0(ganv_canvas, gboolean, supports_sprung_layout);
METHODRET1(ganv_canvas, gboolean, set_sprung_layout, gboolean, sprung_layout);
diff --git a/ganv/canvas.h b/ganv/canvas.h
index 83e3f8a..e923f65 100644
--- a/ganv/canvas.h
+++ b/ganv/canvas.h
@@ -378,6 +378,19 @@ void
ganv_canvas_arrange(GanvCanvas* canvas);
/**
+ * ganv_canvas_export_image:
+ *
+ * Draw the canvas to an image file. The file type is determined by extension,
+ * currently supported: pdf, ps, svg, dot.
+ *
+ * Returns: 0 on success.
+ */
+int
+ganv_canvas_export_image(GanvCanvas* canvas,
+ const char* filename,
+ gboolean draw_background);
+
+/**
* ganv_canvas_export_dot:
*
* Write a Graphviz DOT description of the canvas to a file.
diff --git a/ganv/wrap.hpp b/ganv/wrap.hpp
index f4f2e9c..f907dea 100644
--- a/ganv/wrap.hpp
+++ b/ganv/wrap.hpp
@@ -76,6 +76,11 @@ private: \
return prefix##_##name(gobj(), a1); \
}
+#define METHODRET2(prefix, ret, name, t1, a1, t2, a2) \
+ virtual ret name(t1 a1, t2 a2) { \
+ return prefix##_##name(gobj(), a1, a2); \
+ }
+
#define METHODRETWRAP0(prefix, ret, name) \
virtual ret name() const { \
if (gobj()) { \
diff --git a/src/Canvas.cpp b/src/Canvas.cpp
index 02bf4f6..c3ed2f6 100644
--- a/src/Canvas.cpp
+++ b/src/Canvas.cpp
@@ -2586,6 +2586,64 @@ ganv_canvas_arrange(GanvCanvas* canvas)
#endif
}
+int
+ganv_canvas_export_image(GanvCanvas* canvas,
+ const char* filename,
+ gboolean draw_background)
+{
+ const char* ext = strrchr(filename, '.');
+ if (!ext) {
+ return 1;
+ } else if (!strcmp(ext, ".dot")) {
+ ganv_canvas_export_dot(canvas, filename);
+ return 0;
+ }
+
+ cairo_surface_t* rec_surface = cairo_recording_surface_create(
+ CAIRO_CONTENT_COLOR_ALPHA, NULL);
+
+ // Draw to recording surface
+ cairo_t* cr = cairo_create(rec_surface);
+ (*GANV_ITEM_GET_CLASS(canvas->impl->root)->draw)(
+ canvas->impl->root, cr,
+ 0, 0, canvas->impl->width, canvas->impl->height);
+ cairo_destroy(cr);
+
+ // Get draw extent
+ double x, y, w, h;
+ cairo_recording_surface_ink_extents(rec_surface, &x, &y, &w, &h);
+
+ // Create image surface with the appropriate size
+ const double pad = GANV_CANVAS_PAD;
+ const double img_w = w + pad * 2;
+ const double img_h = h + pad * 2;
+ cairo_surface_t* img = NULL;
+ if (!strcmp(ext, ".svg")) {
+ img = cairo_svg_surface_create(filename, img_w, img_h);
+ } else if (!strcmp(ext, ".pdf")) {
+ img = cairo_pdf_surface_create(filename, img_w, img_h);
+ } else if (!strcmp(ext, ".ps")) {
+ img = cairo_ps_surface_create(filename, img_w, img_h);
+ } else {
+ cairo_surface_destroy(rec_surface);
+ return 1;
+ }
+
+ // Draw recording to image surface
+ cr = cairo_create(img);
+ if (draw_background) {
+ cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
+ cairo_rectangle(cr, 0, 0, w + 2 * pad, h + 2 * pad);
+ cairo_fill(cr);
+ }
+ cairo_set_source_surface(cr, rec_surface, -x + pad, -y + pad);
+ cairo_paint(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(rec_surface);
+ cairo_surface_destroy(img);
+ return 0;
+}
+
void
ganv_canvas_export_dot(GanvCanvas* canvas, const char* filename)
{
@@ -3590,6 +3648,13 @@ ganv_canvas_paint_rect(GanvCanvas* canvas, gint x0, gint y0, gint x1, gint y1)
double wx1, wy1, ww, wh;
ganv_canvas_c2w(canvas, draw_x1, draw_y1, &wx1, &wy1);
ganv_canvas_c2w(canvas, draw_width, draw_height, &ww, &wh);
+
+ // Draw background
+ cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
+ cairo_rectangle(cr, wx1, wy1, ww, wh);
+ cairo_fill(cr);
+
+ // Draw root group
(*GANV_ITEM_GET_CLASS(canvas->impl->root)->draw)(
canvas->impl->root, cr,
wx1, wy1, ww, wh);
diff --git a/src/ganv-private.h b/src/ganv-private.h
index 8f32480..2fc6c00 100644
--- a/src/ganv-private.h
+++ b/src/ganv-private.h
@@ -237,7 +237,7 @@ typedef struct
struct _GanvTextImpl
{
- cairo_surface_t* surface;
+ PangoLayout* layout;
char* text;
GanvTextCoords coords;
GanvTextCoords old_coords;
diff --git a/src/group.c b/src/group.c
index 20ef354..e867cef 100644
--- a/src/group.c
+++ b/src/group.c
@@ -203,11 +203,6 @@ ganv_group_draw(GanvItem* item,
{
GanvGroup* group = GANV_GROUP(item);
- // Draw background
- cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
- cairo_rectangle(cr, cx, cy, cw, ch);
- cairo_fill(cr);
-
// TODO: Layered drawing
for (GList* list = group->impl->item_list; list; list = list->next) {
diff --git a/src/text.c b/src/text.c
index 39df696..6dda2e1 100644
--- a/src/text.c
+++ b/src/text.c
@@ -55,7 +55,7 @@ ganv_text_init(GanvText* text)
impl->coords.height = 1.0;
impl->old_coords = impl->coords;
- impl->surface = NULL;
+ impl->layout = NULL;
impl->text = NULL;
impl->color = 0xFFFFFFFF;
impl->needs_layout = FALSE;
@@ -75,9 +75,9 @@ ganv_text_destroy(GtkObject* object)
impl->text = NULL;
}
- if (impl->surface) {
- cairo_surface_destroy(impl->surface);
- impl->surface = NULL;
+ if (impl->layout) {
+ g_object_unref(impl->layout);
+ impl->layout = NULL;
}
if (GTK_OBJECT_CLASS(parent_class)->destroy) {
@@ -88,57 +88,36 @@ ganv_text_destroy(GtkObject* object)
void
ganv_text_layout(GanvText* text)
{
- GanvTextImpl* impl = text->impl;
- GanvItem* item = GANV_ITEM(text);
- GanvCanvas* canvas = ganv_item_get_canvas(item);
- GtkWidget* widget = GTK_WIDGET(canvas);
- double font_size = ganv_canvas_get_font_size(canvas);
- guint color = 0xFFFFFFFF;
-
- GtkStyle* style = gtk_rc_get_style(widget);
- PangoFontDescription* font = pango_font_description_copy(style->font_desc);
- PangoLayout* layout = gtk_widget_create_pango_layout(widget, impl->text);
- PangoContext* context = pango_layout_get_context(layout);
- cairo_font_options_t* options = cairo_font_options_copy(
- pango_cairo_context_get_font_options(context));
-
- pango_font_description_set_size(font, font_size * (double)PANGO_SCALE);
- pango_layout_set_font_description(layout, font);
-
- if (cairo_font_options_get_antialias(options) == CAIRO_ANTIALIAS_SUBPIXEL) {
- cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
+ GanvTextImpl* impl = text->impl;
+ GanvItem* item = GANV_ITEM(text);
+ GanvCanvas* canvas = ganv_item_get_canvas(item);
+ GtkWidget* widget = GTK_WIDGET(canvas);
+ double points = ganv_canvas_get_font_size(canvas);
+ GtkStyle* style = gtk_rc_get_style(widget);
+
+ if (impl->layout) {
+ g_object_unref(impl->layout);
}
+ impl->layout = gtk_widget_create_pango_layout(widget, impl->text);
- pango_cairo_context_set_font_options(context, options);
- cairo_font_options_destroy(options);
+ PangoFontDescription* font = pango_font_description_copy(style->font_desc);
+ PangoContext* ctx = pango_layout_get_context(impl->layout);
+ cairo_font_options_t* opt = cairo_font_options_copy(
+ pango_cairo_context_get_font_options(ctx));
+
+ pango_font_description_set_size(font, points * (double)PANGO_SCALE);
+ pango_layout_set_font_description(impl->layout, font);
+ pango_cairo_context_set_font_options(ctx, opt);
+ cairo_font_options_destroy(opt);
+ pango_font_description_free(font);
int width, height;
- pango_layout_get_pixel_size(layout, &width, &height);
+ pango_layout_get_pixel_size(impl->layout, &width, &height);
- impl->coords.width = width;
+ impl->coords.width = width;
impl->coords.height = height;
+ impl->needs_layout = FALSE;
- if (impl->surface) {
- cairo_surface_destroy(impl->surface);
- }
-
- impl->surface = cairo_image_surface_create(
- CAIRO_FORMAT_ARGB32, width, height);
-
- cairo_t* cr = cairo_create(impl->surface);
-
- double r, g, b, a;
- color_to_rgba(color, &r, &g, &b, &a);
-
- cairo_set_source_rgba(cr, r, g, b, a);
- cairo_move_to(cr, 0, 0);
- pango_cairo_show_layout(cr, layout);
-
- cairo_destroy(cr);
- g_object_unref(layout);
- pango_font_description_free(font);
-
- impl->needs_layout = FALSE;
ganv_item_request_update(GANV_ITEM(text));
}
@@ -293,12 +272,14 @@ ganv_text_draw(GanvItem* item,
ganv_text_layout(text);
}
- // Round to the nearest pixel so text isn't blurry
- wx = lrint(wx);
- wy = lrint(wy);
+ guint color = 0xFFFFFFFF;
- cairo_set_source_surface(cr, impl->surface, wx, wy);
- cairo_paint(cr);
+ double r, g, b, a;
+ color_to_rgba(color, &r, &g, &b, &a);
+
+ cairo_set_source_rgba(cr, r, g, b, a);
+ cairo_move_to(cr, wx, wy);
+ pango_cairo_show_layout(cr, impl->layout);
}
static void
diff --git a/wscript b/wscript
index 894bbee..d1c0686 100644
--- a/wscript
+++ b/wscript
@@ -8,7 +8,7 @@ import waflib.extras.autowaf as autowaf
# major increment <=> incompatible changes
# minor increment <=> compatible changes (additions)
# micro increment <=> no interface changes
-GANV_VERSION = '1.4.3'
+GANV_VERSION = '1.5.0'
GANV_MAJOR_VERSION = '1'
# Mandatory waf variables