[dia] svg: extend dia_svg_parse_path() to support multiple move-to



commit 1541c6a42401098cd39c7fba9fdb7c3b13994bcf
Author: Hans Breuer <hans breuer org>
Date:   Tue Oct 2 23:04:17 2012 +0200

    svg: extend dia_svg_parse_path() to support multiple move-to
    
    The SVG importer just turns these into 'Standard - Path' when
    necessary. This gives almost perfect round-trip with render-test
    saved by cairo.
    
    The Shape import works as before, i.e. it is splitting the
    multiple move-to into single drawing operations as before.

 lib/dia_svg.c               |   37 ++++++++++++++++++++-----------------
 lib/dia_svg.h               |    3 ++-
 objects/custom/shape_info.c |    7 +++++--
 plug-ins/svg/svg-import.c   |   30 ++++++++++++++++++++++++++++--
 4 files changed, 55 insertions(+), 22 deletions(-)
---
diff --git a/lib/dia_svg.c b/lib/dia_svg.c
index fa3236b..0980532 100644
--- a/lib/dia_svg.c
+++ b/lib/dia_svg.c
@@ -646,11 +646,12 @@ _path_arc(GArray *points, double cpx, double cpy,
 /* routine to chomp off the start of the string */
 #define path_chomp(path) while (path[0]!='\0'&&strchr(" \t\n\r,", path[0])) path++
 
-/** Takes SVG path content and converts it in an array of BezPoint.
+/*!
+ * \brief Takes SVG path content and converts it in an array of BezPoint.
  *
- *  SVG pathes can contain multiple MOVE_TO commands while Dia's bezier
- *  object can only contain one so you may need to call this function
- *  multiple times.
+ * SVG pathes can contain multiple MOVE_TO commands while Dia's bezier
+ * object can only contain one so you may need to call this function
+ * multiple times.
  *
  * @param path_str A string describing an SVG path.
  * @param unparsed The position in `path_str' where parsing ended, or NULL if
@@ -658,8 +659,7 @@ _path_arc(GArray *points, double cpx, double cpy,
  *                 calling the function until it is fully parsed.
  * @param closed Whether the path was closed.
  * @param current_point to retain it over splitting
- * @returns Array of BezPoint objects, or NULL if an error occurred.
- *          The caller is responsible for freeing the array.
+ * @return TRUE if there is any useful data in parsed to points
  * @bug This function is way too long (324 lines). So dont touch it. please!
  * Shouldn't we try to turn straight lines, simple arc, polylines and
  * zigzaglines into their appropriate objects?  Could either be done by
@@ -668,8 +668,9 @@ _path_arc(GArray *points, double cpx, double cpy,
  * NOPE: Dia is capable to handle beziers and the file has given us some so 
  * WHY should be break it in to pieces ???
  */
-GArray*
-dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed, Point *current_point)
+gboolean
+dia_svg_parse_path(GArray *points, const gchar *path_str, gchar **unparsed,
+		   gboolean *closed, Point *current_point)
 {
   enum {
     PATH_MOVE, PATH_LINE, PATH_HLINE, PATH_VLINE, PATH_CURVE,
@@ -678,10 +679,11 @@ dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed, Po
   Point last_point = {0.0, 0.0};
   Point last_control = {0.0, 0.0};
   gboolean last_relative = FALSE;
-  GArray *points;
   BezPoint bez = { 0, };
   gchar *path = (gchar *)path_str;
   gboolean need_next_element = FALSE;
+  /* we can grow the same array in multiple steps */
+  gsize points_at_start = points->len;
 
   *closed = FALSE;
   *unparsed = NULL;
@@ -690,9 +692,6 @@ dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed, Po
   if (current_point)
     last_point = *current_point;
 
-  points = g_array_new(FALSE, FALSE, sizeof(BezPoint));
-  g_array_set_size(points, 0);
-
   path_chomp(path);
   while (path[0] != '\0') {
 #ifdef DEBUG_CUSTOM
@@ -703,7 +702,7 @@ dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed, Po
     case 'M':
 #undef MULTI_MOVE_BEZIER /* Dia XML serialization can't cope with it */
 #ifndef MULTI_MOVE_BEZIER
-      if (points->len > 0) {
+      if (points->len - points_at_start > 0) {
 	need_next_element = TRUE;
 	goto MORETOPARSE;
       }
@@ -838,7 +837,7 @@ dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed, Po
     switch (last_type) {
     case PATH_MOVE:
 #ifndef MULTI_MOVE_BEZIER
-      if (points->len > 1)
+      if (points->len - points_at_start > 1)
 	g_warning ("Only first point should be 'move'");
 #endif
       bez.type = BEZ_MOVE_TO;
@@ -1008,11 +1007,13 @@ dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed, Po
       }
       break;
     case PATH_CLOSE:
-      /* close the path with a line */
-      if (last_open.x != last_point.x || last_open.y != last_point.y) {
+      /* close the path with a line - second condition to ignore single close */
+      if (   (last_open.x != last_point.x || last_open.y != last_point.y)
+	  && (points->len != points_at_start)) {
 	bez.type = BEZ_LINE_TO;
 	bez.p1 = last_open;
 	g_array_append_val(points, bez);
+	last_point = last_open;
       }
       *closed = TRUE;
 #ifndef MULTI_MOVE_BEZIER
@@ -1026,6 +1027,8 @@ MORETOPARSE:
       /* check if there really is more to be parsed */
       if (path[0] != 0)
 	*unparsed = path;
+      else
+	*unparsed = NULL;
       break; /* while */
     }
   }
@@ -1039,7 +1042,7 @@ MORETOPARSE:
   }
   if (current_point)
     *current_point = last_point;
-  return points;
+  return (points->len > 1);
 }
 
 DiaMatrix *
diff --git a/lib/dia_svg.h b/lib/dia_svg.h
index b421c42..f872b18 100644
--- a/lib/dia_svg.h
+++ b/lib/dia_svg.h
@@ -62,7 +62,8 @@ void dia_svg_style_copy (DiaSvgStyle *dest, DiaSvgStyle *src);
 gboolean dia_svg_parse_color(const gchar *str, Color *color);
 void dia_svg_parse_style(xmlNodePtr node, DiaSvgStyle *s, real user_scale);
 /* parse the svg sub format for pathes int an array of BezPoint */
-GArray *dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed, Point *current_point);
+gboolean dia_svg_parse_path(GArray *points, const gchar *path_str, gchar **unparsed,
+			    gboolean *closed, Point *current_point);
 DiaMatrix *dia_svg_parse_transform(const gchar *trans, real scale);
 gchar *dia_svg_from_matrix(const DiaMatrix *matrix, real scale);
 
diff --git a/objects/custom/shape_info.c b/objects/custom/shape_info.c
index 53efb25..6a3e757 100644
--- a/objects/custom/shape_info.c
+++ b/objects/custom/shape_info.c
@@ -159,8 +159,11 @@ parse_path(ShapeInfo *info, const char *path_str, DiaSvgStyle *s, const char* fi
   gboolean closed = FALSE;
   Point current_point = {0.0, 0.0};
 
+  points = g_array_new(FALSE, FALSE, sizeof(BezPoint));
+  g_array_set_size(points, 0);
   do {
-    points = dia_svg_parse_path (pathdata, &unparsed, &closed, &current_point);
+    if (!dia_svg_parse_path (points, pathdata, &unparsed, &closed, &current_point))
+      break;
 
     if (points->len > 0) {
       if (g_array_index(points, BezPoint, 0).type != BEZ_MOVE_TO) {
@@ -493,7 +496,7 @@ parse_svg_node(ShapeInfo *info, xmlNodePtr node, xmlNsPtr svg_ns,
           image->image = dia_image_load(imgfn);
 	}
         if (!image->image)
-          g_warning("failed to load image file %s", imgfn ? imgfn : "(data:)");
+          g_debug("failed to load image file %s", imgfn ? imgfn : "(data:)");
         g_free(imgfn);
         xmlFree(str);
       }
diff --git a/plug-ins/svg/svg-import.c b/plug-ins/svg/svg-import.c
index bb43920..8cdfcf5 100644
--- a/plug-ins/svg/svg-import.c
+++ b/plug-ins/svg/svg-import.c
@@ -328,6 +328,7 @@ read_path_svg(xmlNodePtr node, DiaSvgStyle *parent_style, GList *list, DiaContex
     gint i;
     DiaMatrix *matrix = NULL;
     Point current_point = {0.0, 0.0};
+    gboolean use_stdpath = FALSE;
 
     str = xmlGetProp(node, (const xmlChar *)"transform");
     if (str) {
@@ -337,25 +338,50 @@ read_path_svg(xmlNodePtr node, DiaSvgStyle *parent_style, GList *list, DiaContex
 
     str = xmlGetProp(node, (const xmlChar *)"d");
     pathdata = (char *)str;
+    bezpoints = g_array_new(FALSE, FALSE, sizeof(BezPoint));
+    g_array_set_size(bezpoints, 0);
     do {
-      bezpoints = dia_svg_parse_path (pathdata, &unparsed, &closed, &current_point);
+      int first = bezpoints->len;
+      if (!dia_svg_parse_path (bezpoints, pathdata, &unparsed, &closed, &current_point))
+        break;
 
       if (!closed) {
 	/* expensive way to possibly close the path */
 	DiaSvgStyle *gs = g_new0(DiaSvgStyle, 1);
+	BezPoint bp;
 
 	dia_svg_style_init (gs, NULL);
 	dia_svg_parse_style(node, gs, user_scale);
 	if (gs->font)
           dia_font_unref (gs->font);
 	closed = (gs->fill != DIA_SVG_COLOUR_NONE);
+	/* if we close it here add an explicit line-to */
+	if (closed) {
+	  bp.type = BEZ_LINE_TO;
+	  bp.p1 = g_array_index(bezpoints, BezPoint, first).p1;
+	  g_array_append_val (bezpoints, bp);
+	}
         g_free(gs);
       }
-      if (bezpoints && bezpoints->len > 0) {
+      if (unparsed) {
+        use_stdpath = TRUE;
+      } else if (bezpoints && bezpoints->len > 0) {
+	/* A stray 'z' can produce extra runs without adding any new BEZ_MOVE_TO.
+	 * To have the optimum representaion with Dia's objects we check again.
+	 */
+	if (use_stdpath) {
+	  int move_tos = 0;
+	  for (i = 0; i < bezpoints->len; ++i)
+	    if (g_array_index(bezpoints, BezPoint, i).type == BEZ_MOVE_TO)
+	      ++move_tos;
+	  use_stdpath = (move_tos > 1);
+	}
         if (g_array_index(bezpoints, BezPoint, 0).type != BEZ_MOVE_TO) {
           dia_context_add_message(ctx, _("Invalid path data.\n"
 					 "svg:path data must start with moveto."));
 	  break;
+	} else if (use_stdpath) {
+	  otype = object_get_type("Standard - Path");
         } else if (!closed)
 	  otype = object_get_type("Standard - BezierLine");
         else



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