[dia] [gradient] svg round-trip (i.e. save what it loads)
- From: Hans Breuer <hans src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dia] [gradient] svg round-trip (i.e. save what it loads)
- Date: Wed, 1 Jan 2014 11:51:02 +0000 (UTC)
commit 722d6c2c75b235061a12392988e2440fbeb6239c
Author: Hans Breuer <hans breuer org>
Date: Thu Dec 26 14:53:34 2013 +0100
[gradient] svg round-trip (i.e. save what it loads)
export is implemented in the base class, but currently only activated
for the SVG export, not for shape export
gradient can also be referenced by style="fill:url(#x)" now
lib/diasvgrenderer.c | 152 +++++++++++++++++++++++++++++++++++++++++++--
lib/diasvgrenderer.h | 5 ++
plug-ins/svg/render_svg.c | 2 +
plug-ins/svg/svg-import.c | 33 +++++++---
4 files changed, 178 insertions(+), 14 deletions(-)
---
diff --git a/lib/diasvgrenderer.c b/lib/diasvgrenderer.c
index d637942..5236f14 100644
--- a/lib/diasvgrenderer.c
+++ b/lib/diasvgrenderer.c
@@ -31,6 +31,7 @@
#include <time.h>
#include <math.h>
#include <glib.h>
+#include <glib/gstdio.h> /* g_sprintf */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -47,6 +48,7 @@
#include "diasvgrenderer.h"
#include "textline.h"
#include "prop_pixbuf.h" /* pixbuf_encode_base64() */
+#include "pattern.h"
#define DTOSTR_BUF_SIZE G_ASCII_DTOSTR_BUF_SIZE
#define dia_svg_dtostr(buf,d) \
@@ -67,12 +69,120 @@ begin_render(DiaRenderer *self, const Rectangle *update)
renderer->linestyle = NULL;
}
+/*!
+ * \brief Create a unique key to identify the pattern
+ */
+static gchar *
+_make_pattern_key (const DiaPattern *pattern)
+{
+ gchar *key = g_strdup_printf ("%p", pattern);
+
+ return key;
+}
+
+static gboolean
+_color_stop_do (real ofs,
+ const Color *col,
+ gpointer user_data)
+{
+ xmlNodePtr parent = (xmlNodePtr)user_data;
+ xmlNodePtr stop = xmlNewChild (parent, parent->ns, (const xmlChar *)"stop", NULL);
+ gchar vbuf[DTOSTR_BUF_SIZE];
+
+ g_ascii_formatd(vbuf,sizeof(vbuf),"%g", ofs);
+ xmlSetProp (stop, (const xmlChar *)"offset", vbuf);
+
+ g_sprintf (vbuf, "#%02x%02x%02x", (int)(255*col->red), (int)(255*col->green), (int)(255*col->blue));
+ xmlSetProp (stop, (const xmlChar *)"stop-color", vbuf);
+
+ g_ascii_formatd(vbuf,sizeof(vbuf),"%g", col->alpha);
+ xmlSetProp (stop, (const xmlChar *)"stop-opacity", vbuf);
+
+ return TRUE;
+}
+
+typedef struct {
+ DiaSvgRenderer *renderer;
+ xmlNodePtr node;
+} GradientData;
+
+static void
+_gradient_do (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ gchar *name = (gchar *)key;
+ DiaPattern *pattern = (DiaPattern *)value;
+ GradientData *gd = (GradientData *)user_data;
+ DiaSvgRenderer *renderer = gd->renderer;
+ xmlNodePtr parent = gd->node;
+ xmlNodePtr gradient;
+ DiaPatternType pt;
+ guint flags;
+ Point p1, p2;
+ gchar vbuf[DTOSTR_BUF_SIZE];
+ real scale = renderer->scale;
+
+ dia_pattern_get_settings (pattern, &pt, &flags);
+ if ((flags & DIA_PATTERN_USER_SPACE)==0)
+ scale = 1.0;
+ dia_pattern_get_points (pattern, &p1, &p2);
+ if (pt == DIA_LINEAR_GRADIENT) {
+ gradient = xmlNewChild (parent, parent->ns, (const xmlChar *)"linearGradient", NULL);
+ xmlSetProp (gradient, (const xmlChar *)"x1", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p1.x * scale));
+ xmlSetProp (gradient, (const xmlChar *)"y1", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p1.y * scale));
+ xmlSetProp (gradient, (const xmlChar *)"x2", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p2.x * scale));
+ xmlSetProp (gradient, (const xmlChar *)"y2", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p2.y * scale));
+ } else if (pt == DIA_RADIAL_GRADIENT) {
+ real r;
+ dia_pattern_get_radius (pattern, &r);
+ gradient = xmlNewChild (parent, parent->ns, (const xmlChar *)"radialGradient", NULL);
+ xmlSetProp (gradient, (const xmlChar *)"cx", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p1.x * scale));
+ xmlSetProp (gradient, (const xmlChar *)"cy", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p1.y * scale));
+ xmlSetProp (gradient, (const xmlChar *)"fx", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p2.x * scale));
+ xmlSetProp (gradient, (const xmlChar *)"fy", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", p2.y * scale));
+ xmlSetProp (gradient, (const xmlChar *)"r", g_ascii_formatd(vbuf,sizeof(vbuf),"%g", r * scale));
+ } else {
+ gradient = xmlNewChild (parent, parent->ns, (const xmlChar *)"pattern", NULL);
+ }
+ /* don't miss to set the id */
+ {
+ gchar *id = _make_pattern_key (pattern);
+ xmlSetProp (gradient, (const xmlChar *)"id", (const xmlChar *)id);
+ g_free (id);
+ }
+ if (flags & DIA_PATTERN_USER_SPACE)
+ xmlSetProp (gradient, (const xmlChar *)"gradientUnits", (const xmlChar *)"userSpaceOnUse");
+ if (flags & DIA_PATTERN_EXTEND_REPEAT)
+ xmlSetProp (gradient, (const xmlChar *)"spreadMethod", (const xmlChar *)"repeat");
+ else if (flags & DIA_PATTERN_EXTEND_REFLECT)
+ xmlSetProp (gradient, (const xmlChar *)"spreadMethod", (const xmlChar *)"reflect");
+ else if (flags & DIA_PATTERN_EXTEND_PAD)
+ xmlSetProp (gradient, (const xmlChar *)"spreadMethod", (const xmlChar *)"pad");
+
+ if (pt == DIA_LINEAR_GRADIENT || pt == DIA_RADIAL_GRADIENT) {
+ dia_pattern_foreach (pattern, _color_stop_do, gradient);
+ } else {
+ g_warning ("SVG pattern data not implemented");
+ }
+}
+
static void
end_render(DiaRenderer *self)
{
DiaSvgRenderer *renderer = DIA_SVG_RENDERER (self);
g_free(renderer->linestyle);
+ /* handle potential patterns */
+ if (renderer->patterns) {
+ xmlNodePtr root = xmlDocGetRootElement (renderer->doc);
+ xmlNodePtr defs = xmlNewNode (renderer->svg_name_space, (const xmlChar *)"defs");
+ GradientData gd = { renderer, defs };
+ g_hash_table_foreach (renderer->patterns, _gradient_do, &gd);
+ xmlAddPrevSibling (root->children, defs);
+ g_hash_table_destroy (renderer->patterns);
+ renderer->patterns = NULL;
+ }
xmlSetDocCompressMode(renderer->doc, 0);
xmlDiaSaveFile(renderer->filename, renderer->doc);
g_free(renderer->filename);
@@ -214,6 +324,30 @@ set_fillstyle(DiaRenderer *self, FillStyle mode)
}
}
+/*!
+ * \brief Remember the pattern for later use
+ */
+static void
+set_pattern(DiaRenderer *self, DiaPattern *pattern)
+{
+ DiaSvgRenderer *renderer = DIA_SVG_RENDERER (self);
+ DiaPattern *prev = renderer->active_pattern;
+
+ if (!renderer->patterns)
+ renderer->patterns = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ /* remember */
+ if (pattern) {
+ renderer->active_pattern = g_object_ref (pattern);
+ if (!g_hash_table_lookup (renderer->patterns, pattern))
+ g_hash_table_insert (renderer->patterns, _make_pattern_key (pattern), g_object_ref (pattern));
+ } else {
+ renderer->active_pattern = NULL;
+ }
+
+ if (prev)
+ g_object_unref (prev);
+}
+
/* the return value of this function should not be saved anywhere */
static const gchar *
get_draw_style(DiaSvgRenderer *renderer,
@@ -240,7 +374,7 @@ get_draw_style(DiaSvgRenderer *renderer,
if (colour)
g_string_append_printf(str, "; stroke: #%02x%02x%02x",
(int)(255*colour->red),
- (int)(255*colour->green),
+ (int)(255*colour->green),
(int)(255*colour->blue));
return str->str;
@@ -256,11 +390,16 @@ get_fill_style(DiaSvgRenderer *renderer,
if (!str) str = g_string_new(NULL);
- g_string_printf(str, "fill: #%02x%02x%02x; fill-opacity: %s",
- (int)(255*colour->red), (int)(255*colour->green),
- (int)(255*colour->blue),
- g_ascii_formatd(alpha_buf, sizeof(alpha_buf), "%g", colour->alpha));
-
+ if (renderer->active_pattern) {
+ gchar *key = _make_pattern_key (renderer->active_pattern);
+ g_string_printf(str, "fill:url(#%s)", key);
+ g_free (key);
+ } else {
+ g_string_printf(str, "fill: #%02x%02x%02x; fill-opacity: %s",
+ (int)(255*colour->red), (int)(255*colour->green),
+ (int)(255*colour->blue),
+ g_ascii_formatd(alpha_buf, sizeof(alpha_buf), "%g", colour->alpha));
+ }
return str->str;
}
@@ -826,6 +965,7 @@ dia_svg_renderer_class_init (DiaSvgRendererClass *klass)
renderer_class->set_linestyle = set_linestyle;
renderer_class->set_dashlength = set_dashlength;
renderer_class->set_fillstyle = set_fillstyle;
+ renderer_class->set_pattern = set_pattern;
renderer_class->draw_line = draw_line;
renderer_class->fill_polygon = fill_polygon;
diff --git a/lib/diasvgrenderer.h b/lib/diasvgrenderer.h
index cc85958..12ba7cd 100644
--- a/lib/diasvgrenderer.h
+++ b/lib/diasvgrenderer.h
@@ -41,6 +41,11 @@ struct _DiaSvgRenderer
const char *linejoin;
char *linestyle; /* not const -- must free */
real scale; /*!< scale=1.0 for shape output, more for svg output, */
+
+ /*! \private pattern set by set_pattern */
+ DiaPattern *active_pattern;
+ /*! \private all patterns seen between begin_render and end_render */
+ GHashTable *patterns;
};
struct _DiaSvgRendererClass
diff --git a/plug-ins/svg/render_svg.c b/plug-ins/svg/render_svg.c
index 4dd9e19..d0685ab 100644
--- a/plug-ins/svg/render_svg.c
+++ b/plug-ins/svg/render_svg.c
@@ -179,6 +179,8 @@ is_capable_to (DiaRenderer *renderer, RenderCapability cap)
return TRUE;
else if (RENDER_AFFINE == cap)
return TRUE;
+ else if (RENDER_PATTERN == cap)
+ return TRUE;
return FALSE;
}
diff --git a/plug-ins/svg/svg-import.c b/plug-ins/svg/svg-import.c
index c839027..4972008 100644
--- a/plug-ins/svg/svg-import.c
+++ b/plug-ins/svg/svg-import.c
@@ -492,6 +492,24 @@ apply_style(DiaObject *obj, xmlNodePtr node, DiaSvgStyle *parent_style,
g_free (key);
}
xmlFree(str);
+ } else if (gs->fill == DIA_SVG_COLOUR_NONE) {
+ /* check the style again, it might contain a pattern */
+ str = xmlGetProp(node, (const xmlChar*)"style");
+ if (str) {
+ const char *left = strstr ((const char*)str, "fill:url(#");
+ const char *right = left ? strrchr (left, ')') : NULL;
+ if (left && right) {
+ gchar *key = g_strndup (left + 10, right - left - 10);
+ DiaPattern *pattern = g_hash_table_lookup (pattern_ht, key);
+ if (pattern) {
+ dia_object_set_pattern (obj, pattern);
+ /* activate "show_background" */
+ bprop->bool_data = TRUE;
+ }
+ g_free (key);
+ }
+ xmlFree (str);
+ }
}
eprop = g_ptr_array_index(props,5);
@@ -1706,12 +1724,6 @@ read_defs (xmlNodePtr startnode,
defs_ht, style_ht, pattern_ht,
filename_svg, ctx);
- if (!defs) {
- read_defs (node->xmlChildrenNode, parent_gs,
- defs_ht, style_ht, pattern_ht,
- filename_svg, ctx);
- continue;
- }
/* Commonly seen in <defs/> are
* clipPath, font, filter, linearGradient, mask, marker, pattern, radialGradient, style
* all not supported as of this writing.
@@ -1740,6 +1752,10 @@ read_defs (xmlNodePtr startnode,
list->data = NULL;
}
}
+ /* kind of greedy */
+ read_defs (node->xmlChildrenNode, parent_gs,
+ defs_ht, style_ht, pattern_ht,
+ filename_svg, ctx);
} else if(!xmlStrcmp(node->name, (const xmlChar *)"style")) {
/* Prepare the third variant to apply style to the objects.
* The final style is similar to what we have in the style
@@ -1750,7 +1766,8 @@ read_defs (xmlNodePtr startnode,
} else if(!xmlStrcmp(node->name, (const xmlChar *)"pattern")) {
/* Patterns could be considered as groups, too. But Dia does not
* have the facility to apply them (yet?). */
- } else if(!xmlStrcmp(node->name, (const xmlChar *)"g")) {
+ } else if(!xmlStrcmp(node->name, (const xmlChar *)"g") ||
+ !xmlStrcmp(node->name, (const xmlChar *)"a")) {
/* just dive into */
DiaSvgStyle group_gs;
dia_svg_style_init (&group_gs, parent_gs);
@@ -1872,7 +1889,7 @@ import_file_svg(const gchar *filename, DiagramData *dia, DiaContext *ctx, void*
return import_svg (doc, dia, ctx, user_data);
}
-gboolean
+static gboolean
import_svg (xmlDocPtr doc, DiagramData *dia,
DiaContext *ctx, void *user_data)
{
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]