summaryrefslogtreecommitdiffstats
path: root/src/svg/svg-path.cpp
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/svg/svg-path.cpp
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/svg/svg-path.cpp')
-rw-r--r--src/svg/svg-path.cpp710
1 files changed, 710 insertions, 0 deletions
diff --git a/src/svg/svg-path.cpp b/src/svg/svg-path.cpp
new file mode 100644
index 000000000..6598a5731
--- /dev/null
+++ b/src/svg/svg-path.cpp
@@ -0,0 +1,710 @@
+#define __SP_SVG_PARSE_C__
+/*
+ svg-path.c: Parse SVG path element data into bezier path.
+
+ Copyright (C) 2000 Eazel, Inc.
+ Copyright (C) 2000 Lauris Kaplinski
+ Copyright (C) 2001 Ximian, Inc.
+
+ 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.
+
+ Authors:
+ Raph Levien <raph@artofcode.com>
+ Lauris Kaplinski <lauris@ximian.com>
+*/
+
+#include <cassert>
+#include <glib/gmem.h>
+#include <glib/gmessages.h>
+#include <glib/gstrfuncs.h>
+
+#include "libnr/n-art-bpath.h"
+#include "gnome-canvas-bpath-util.h"
+#include "stringstream.h"
+
+
+/* This module parses an SVG path element into an RsvgBpathDef.
+
+ At present, there is no support for <marker> or any other contextual
+ information from the SVG file. The API will need to change rather
+ significantly to support these.
+
+ Reference: SVG working draft 3 March 2000, section 8.
+*/
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+/* We are lazy ;-) (Lauris) */
+#define rsvg_bpath_def_new gnome_canvas_bpath_def_new
+#define rsvg_bpath_def_moveto gnome_canvas_bpath_def_moveto
+#define rsvg_bpath_def_lineto gnome_canvas_bpath_def_lineto
+#define rsvg_bpath_def_curveto gnome_canvas_bpath_def_curveto
+#define rsvg_bpath_def_closepath gnome_canvas_bpath_def_closepath
+
+struct RSVGParsePathCtx {
+ GnomeCanvasBpathDef *bpath;
+ double cpx, cpy; /* current point */
+ double rpx, rpy; /* reflection point (for 's' and 't' commands) */
+ double spx, spy; /* beginning of current subpath point */
+ char cmd; /* current command (lowercase) */
+ int param; /* parameter number */
+ gboolean rel; /* true if relative coords */
+ double params[7]; /* parameters that have been parsed */
+};
+
+static void rsvg_path_arc_segment(RSVGParsePathCtx *ctx,
+ double xc, double yc,
+ double th0, double th1,
+ double rx, double ry, double x_axis_rotation)
+{
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x1, y1, x2, y2, x3, y3;
+ double t;
+ double th_half;
+
+ sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+ cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+ /* inverse transform compared with rsvg_path_arc */
+ a00 = cos_th * rx;
+ a01 = -sin_th * ry;
+ a10 = sin_th * rx;
+ a11 = cos_th * ry;
+
+ th_half = 0.5 * (th1 - th0);
+ t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
+ x1 = xc + cos (th0) - t * sin (th0);
+ y1 = yc + sin (th0) + t * cos (th0);
+ x3 = xc + cos (th1);
+ y3 = yc + sin (th1);
+ x2 = x3 + t * sin (th1);
+ y2 = y3 - t * cos (th1);
+ rsvg_bpath_def_curveto(ctx->bpath,
+ a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
+ a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
+ a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
+}
+
+/**
+ * rsvg_path_arc: Add an RSVG arc to the path context.
+ * @ctx: Path context.
+ * @rx: Radius in x direction (before rotation).
+ * @ry: Radius in y direction (before rotation).
+ * @x_axis_rotation: Rotation angle for axes.
+ * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
+ * @sweep: 0 for "negative angle", 1 for "positive angle".
+ * @x: New x coordinate.
+ * @y: New y coordinate.
+ *
+ **/
+static void rsvg_path_arc (RSVGParsePathCtx *ctx,
+ double rx, double ry, double x_axis_rotation,
+ int large_arc_flag, int sweep_flag,
+ double x, double y)
+{
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x0, y0, x1, y1, xc, yc;
+ double d, sfactor, sfactor_sq;
+ double th0, th1, th_arc;
+ double px, py, pl;
+ int i, n_segs;
+
+ sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+ cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+
+ /*
+ Correction of out-of-range radii as described in Appendix F.6.6:
+
+ 1. Ensure radii are non-zero (Done?).
+ 2. Ensure that radii are positive.
+ 3. Ensure that radii are large enough.
+ */
+
+ if(rx < 0.0) rx = -rx;
+ if(ry < 0.0) ry = -ry;
+
+ px = cos_th * (ctx->cpx - x) * 0.5 + sin_th * (ctx->cpy - y) * 0.5;
+ py = cos_th * (ctx->cpy - y) * 0.5 - sin_th * (ctx->cpx - x) * 0.5;
+ pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
+
+ if(pl > 1.0)
+ {
+ pl = sqrt(pl);
+ rx *= pl;
+ ry *= pl;
+ }
+
+ /* Proceed with computations as described in Appendix F.6.5 */
+
+ a00 = cos_th / rx;
+ a01 = sin_th / rx;
+ a10 = -sin_th / ry;
+ a11 = cos_th / ry;
+ x0 = a00 * ctx->cpx + a01 * ctx->cpy;
+ y0 = a10 * ctx->cpx + a11 * ctx->cpy;
+ x1 = a00 * x + a01 * y;
+ y1 = a10 * x + a11 * y;
+ /* (x0, y0) is current point in transformed coordinate space.
+ (x1, y1) is new point in transformed coordinate space.
+
+ The arc fits a unit-radius circle in this space.
+ */
+ d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
+ sfactor_sq = 1.0 / d - 0.25;
+ if (sfactor_sq < 0) sfactor_sq = 0;
+ sfactor = sqrt (sfactor_sq);
+ if (sweep_flag == large_arc_flag) sfactor = -sfactor;
+ xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
+ yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
+ /* (xc, yc) is center of the circle. */
+
+ th0 = atan2 (y0 - yc, x0 - xc);
+ th1 = atan2 (y1 - yc, x1 - xc);
+
+ th_arc = th1 - th0;
+ if (th_arc < 0 && sweep_flag)
+ th_arc += 2 * M_PI;
+ else if (th_arc > 0 && !sweep_flag)
+ th_arc -= 2 * M_PI;
+
+ n_segs = (int) ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
+
+ for (i = 0; i < n_segs; i++) {
+ rsvg_path_arc_segment(ctx, xc, yc,
+ th0 + i * th_arc / n_segs,
+ th0 + (i + 1) * th_arc / n_segs,
+ rx, ry, x_axis_rotation);
+ }
+
+ ctx->cpx = x;
+ ctx->cpy = y;
+}
+
+
+/* supply defaults for missing parameters, assuming relative coordinates
+ are to be interpreted as x,y */
+static void rsvg_parse_path_default_xy(RSVGParsePathCtx *ctx, int n_params)
+{
+ int i;
+
+ if (ctx->rel) {
+ for (i = ctx->param; i < n_params; i++) {
+ if (i > 2)
+ ctx->params[i] = ctx->params[i - 2];
+ else if (i == 1)
+ ctx->params[i] = ctx->cpy;
+ else if (i == 0)
+ /* we shouldn't get here (usually ctx->param > 0 as
+ precondition) */
+ ctx->params[i] = ctx->cpx;
+ }
+ } else {
+ for (i = ctx->param; i < n_params; i++) {
+ ctx->params[i] = 0.0;
+ }
+ }
+}
+
+static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx, gboolean final)
+{
+ double x1, y1, x2, y2, x3, y3;
+
+#ifdef VERBOSE
+ int i;
+
+ g_print ("parse_path %c:", ctx->cmd);
+ for (i = 0; i < ctx->param; i++) {
+ g_print(" %f", ctx->params[i]);
+ }
+ g_print (final ? ".\n" : "\n");
+#endif
+
+ switch (ctx->cmd) {
+ case 'm':
+ /* moveto */
+ if (ctx->param == 2
+ || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 2);
+#ifdef VERBOSE
+ g_print ("'m' moveto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_moveto (ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->spx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->spy = ctx->params[1];
+ ctx->param = 0;
+ ctx->cmd = 'l';
+ }
+ break;
+ case 'l':
+ /* lineto */
+ if (ctx->param == 2
+ || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 2);
+#ifdef VERBOSE
+ g_print ("'l' lineto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->params[1];
+ ctx->param = 0;
+ }
+ break;
+ case 'c':
+ /* curveto */
+ if ( ( ctx->param == 6 )
+ || final )
+ {
+ rsvg_parse_path_default_xy (ctx, 6);
+ x1 = ctx->params[0];
+ y1 = ctx->params[1];
+ x2 = ctx->params[2];
+ y2 = ctx->params[3];
+ x3 = ctx->params[4];
+ y3 = ctx->params[5];
+#ifdef VERBOSE
+ g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 's':
+ /* smooth curveto */
+ if ( ( ctx->param == 4 )
+ || final )
+ {
+ rsvg_parse_path_default_xy (ctx, 4);
+ x1 = 2 * ctx->cpx - ctx->rpx;
+ y1 = 2 * ctx->cpy - ctx->rpy;
+ x2 = ctx->params[0];
+ y2 = ctx->params[1];
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+#ifdef VERBOSE
+ g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 'h':
+ /* horizontal lineto */
+ if (ctx->param == 1) {
+#ifdef VERBOSE
+ g_print ("'h' lineto %g,%g\n",
+ ctx->params[0], ctx->cpy);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->params[0], ctx->cpy);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->param = 0;
+ }
+ break;
+ case 'v':
+ /* vertical lineto */
+ if (ctx->param == 1) {
+#ifdef VERBOSE
+ g_print ("'v' lineto %g,%g\n",
+ ctx->cpx, ctx->params[0]);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->cpx, ctx->params[0]);
+ ctx->cpy = ctx->rpy = ctx->params[0];
+ ctx->param = 0;
+ }
+ break;
+ case 'q':
+ /* quadratic bezier curveto */
+
+ /* non-normative reference:
+ http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
+ */
+ if (ctx->param == 4 || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 4);
+ /* raise quadratic bezier to cubic */
+ x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+ x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print("'q' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto(ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = ctx->params[0];
+ ctx->rpy = ctx->params[1];
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 't':
+ /* Truetype quadratic bezier curveto */
+ if (ctx->param == 2 || final) {
+ double xc, yc; /* quadratic control point */
+
+ xc = 2 * ctx->cpx - ctx->rpx;
+ yc = 2 * ctx->cpy - ctx->rpy;
+ /* generate a quadratic bezier with control point = xc, yc */
+ x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
+ x3 = ctx->params[0];
+ y3 = ctx->params[1];
+ x2 = (x3 + 2 * xc) * (1.0 / 3.0);
+ y2 = (y3 + 2 * yc) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = xc;
+ ctx->rpy = yc;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ } else if (final) {
+ if (ctx->param > 2) {
+ rsvg_parse_path_default_xy(ctx, 4);
+ /* raise quadratic bezier to cubic */
+ x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+ x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ } else {
+ rsvg_parse_path_default_xy(ctx, 2);
+#ifdef VERBOSE
+ g_print ("'t' lineto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_lineto(ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->params[1];
+ }
+ ctx->param = 0;
+ }
+ break;
+ case 'a':
+ if (ctx->param == 7 || final)
+ {
+ rsvg_path_arc(ctx,
+ ctx->params[0], ctx->params[1], ctx->params[2],
+ (int) ctx->params[3], (int) ctx->params[4],
+ ctx->params[5], ctx->params[6]);
+ ctx->param = 0;
+ }
+ break;
+ default:
+ ctx->param = 0;
+ }
+}
+
+static void rsvg_parse_path_data(RSVGParsePathCtx *ctx, const char *data)
+{
+ int i = 0;
+ double val = 0;
+ char c = 0;
+ gboolean in_num = FALSE;
+ gboolean in_frac = FALSE;
+ gboolean in_exp = FALSE;
+ gboolean exp_wait_sign = FALSE;
+ int sign = 0;
+ int exp = 0;
+ int exp_sign = 0;
+ double frac = 0.0;
+
+ /* fixme: Do better error processing: e.g. at least stop parsing as soon as we find an error.
+ * At some point we'll need to do all of
+ * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
+ */
+ for (i = 0; ; i++)
+ {
+ c = data[i];
+ if (c >= '0' && c <= '9')
+ {
+ /* digit */
+ if (in_num)
+ {
+ if (in_exp)
+ {
+ exp = (exp * 10) + c - '0';
+ exp_wait_sign = FALSE;
+ }
+ else if (in_frac)
+ val += (frac *= 0.1) * (c - '0');
+ else
+ val = (val * 10) + c - '0';
+ }
+ else
+ {
+ in_num = TRUE;
+ assert(!in_frac && !in_exp);
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ val = c - '0';
+ sign = 1;
+ }
+ }
+ else if (c == '.' && !(in_frac || in_exp))
+ {
+ if (!in_num)
+ {
+ in_num = TRUE;
+ assert(!in_exp);
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ val = 0;
+ sign = 1;
+ }
+ in_frac = TRUE;
+ frac = 1;
+ }
+ else if ((c == 'E' || c == 'e') && in_num)
+ {
+ /* fixme: Should we add `&& !in_exp' to the above condition?
+ * It looks like the current code will parse `1e3e4' (as 1e4). */
+ in_exp = TRUE;
+ exp_wait_sign = TRUE;
+ exp = 0;
+ exp_sign = 1;
+ }
+ else if ((c == '+' || c == '-') && in_exp)
+ {
+ exp_sign = c == '+' ? 1 : -1;
+ }
+ else if (in_num)
+ {
+ /* end of number */
+
+ val *= sign * pow (10, exp_sign * exp);
+ if (ctx->rel)
+ {
+ /* Handle relative coordinates. This switch statement attempts
+ to determine _what_ the coords are relative to. This is
+ underspecified in the 12 Apr working draft. */
+ switch (ctx->cmd)
+ {
+ case 'l':
+ case 'm':
+ case 'c':
+ case 's':
+ case 'q':
+ case 't':
+ if ( ctx->param & 1 ) {
+ val += ctx->cpy; /* odd param, y */
+ } else {
+ val += ctx->cpx; /* even param, x */
+ }
+ break;
+ case 'a':
+ /* rule: sixth and seventh are x and y, rest are not
+ relative */
+ if (ctx->param == 5)
+ val += ctx->cpx;
+ else if (ctx->param == 6)
+ val += ctx->cpy;
+ break;
+ case 'h':
+ /* rule: x-relative */
+ val += ctx->cpx;
+ break;
+ case 'v':
+ /* rule: y-relative */
+ val += ctx->cpy;
+ break;
+ }
+ }
+ ctx->params[ctx->param++] = val;
+ rsvg_parse_path_do_cmd (ctx, FALSE);
+ if (c=='.') {
+ in_num = TRUE;
+ val = 0;
+ in_frac = TRUE;
+ in_exp = FALSE;
+ frac = 1;
+ }
+ else {
+ in_num = FALSE;
+ in_frac = FALSE;
+ in_exp = FALSE;
+ }
+ }
+
+ if (c == '\0')
+ break;
+ else if ((c == '+' || c == '-') && !exp_wait_sign)
+ {
+ sign = c == '+' ? 1 : -1;;
+ val = 0;
+ in_num = TRUE;
+ in_frac = FALSE;
+ in_exp = FALSE;
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ }
+ else if (c == 'z' || c == 'Z')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ rsvg_bpath_def_closepath (ctx->bpath);
+
+ ctx->cmd = 'm';
+ ctx->params[0] = ctx->cpx = ctx->rpx = ctx->spx;
+ ctx->params[1] = ctx->cpy = ctx->rpy = ctx->spy;
+ ctx->param = 2;
+ }
+ else if (c >= 'A' && c <= 'Z' && c != 'E')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ ctx->cmd = c + 'a' - 'A';
+ ctx->rel = FALSE;
+ }
+ else if (c >= 'a' && c <= 'z' && c != 'e')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ ctx->cmd = c;
+ ctx->rel = TRUE;
+ }
+ /* else c _should_ be whitespace or , */
+ }
+}
+
+
+NArtBpath *sp_svg_read_path(gchar const *str)
+{
+ RSVGParsePathCtx ctx;
+ NArtBpath *bpath;
+
+ ctx.bpath = gnome_canvas_bpath_def_new ();
+ ctx.cpx = 0.0;
+ ctx.cpy = 0.0;
+ ctx.cmd = 0;
+ ctx.param = 0;
+
+ rsvg_parse_path_data (&ctx, str);
+
+ if (ctx.param && ctx.cmd != 'm') {
+ rsvg_parse_path_do_cmd (&ctx, TRUE);
+ }
+
+ gnome_canvas_bpath_def_art_finish (ctx.bpath);
+
+ bpath = g_new (NArtBpath, ctx.bpath->n_bpath);
+ memcpy (bpath, ctx.bpath->bpath, ctx.bpath->n_bpath * sizeof (NArtBpath));
+ g_assert ((bpath + ctx.bpath->n_bpath - 1)->code == NR_END);
+ gnome_canvas_bpath_def_unref (ctx.bpath);
+
+ return bpath;
+}
+
+gchar *sp_svg_write_path(NArtBpath const *bpath)
+{
+ Inkscape::SVGOStringStream os;
+ bool closed=false;
+
+ g_return_val_if_fail (bpath != NULL, NULL);
+
+ for (int i = 0; bpath[i].code != NR_END; i++){
+ if (i) {
+ os << " ";
+ }
+ switch (bpath [i].code){
+ case NR_LINETO:
+ os << "L " << bpath[i].x3 << "," << bpath[i].y3;
+ break;
+
+ case NR_CURVETO:
+ os << "C " << bpath[i].x1 << "," << bpath[i].y1
+ << " " << bpath[i].x2 << "," << bpath[i].y2
+ << " " << bpath[i].x3 << "," << bpath[i].y3;
+ break;
+
+ case NR_MOVETO_OPEN:
+ case NR_MOVETO:
+ if (closed) {
+ os << "z ";
+ }
+ closed = ( bpath[i].code == NR_MOVETO );
+ os << "M " << bpath[i].x3 << "," << bpath[i].y3;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ if (closed) {
+ os << " z ";
+ }
+
+// std::string s = os.str();
+// gchar *ret = g_strdup(s.c_str());
+// delete (s);
+// return ret;
+ return g_strdup (os.str().c_str());
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :