81eb7da59ba659e07bfc5b1e142e4e7fafac7f9d
1 #define __SP_SVG_PARSE_C__
2 /*
3 svg-path.c: Parse SVG path element data into bezier path.
5 Copyright (C) 2000 Eazel, Inc.
6 Copyright (C) 2000 Lauris Kaplinski
7 Copyright (C) 2001 Ximian, Inc.
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public
20 License along with this program; if not, write to the
21 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
24 Authors:
25 Raph Levien <raph@artofcode.com>
26 Lauris Kaplinski <lauris@ximian.com>
27 */
29 #include <cstring>
30 #include <string>
31 #include <cassert>
32 #include <glib/gmem.h>
33 #include <glib/gmessages.h>
34 #include <glib/gstrfuncs.h>
35 #include <glib.h> // g_assert()
37 #include "libnr/n-art-bpath.h"
38 #include "gnome-canvas-bpath-util.h"
39 #include "svg/svg.h"
40 #include "svg/path-string.h"
42 #include <2geom/pathvector.h>
43 #include <2geom/path.h>
44 #include <2geom/curves.h>
45 #include <2geom/sbasis-to-bezier.h>
46 #include <2geom/svg-path.h>
47 #include <2geom/svg-path-parser.h>
48 #include <2geom/exception.h>
51 /* This module parses an SVG path element into an RsvgBpathDef.
53 At present, there is no support for <marker> or any other contextual
54 information from the SVG file. The API will need to change rather
55 significantly to support these.
57 Reference: SVG working draft 3 March 2000, section 8.
58 */
60 #ifndef M_PI
61 #define M_PI 3.14159265358979323846
62 #endif /* M_PI */
64 /* We are lazy ;-) (Lauris) */
65 #define rsvg_bpath_def_new gnome_canvas_bpath_def_new
66 #define rsvg_bpath_def_moveto gnome_canvas_bpath_def_moveto
67 #define rsvg_bpath_def_lineto gnome_canvas_bpath_def_lineto
68 #define rsvg_bpath_def_curveto gnome_canvas_bpath_def_curveto
69 #define rsvg_bpath_def_closepath gnome_canvas_bpath_def_closepath
71 struct RSVGParsePathCtx {
72 GnomeCanvasBpathDef *bpath;
73 double cpx, cpy; /* current point */
74 double rpx, rpy; /* reflection point (for 's' and 't' commands) */
75 double spx, spy; /* beginning of current subpath point */
76 char cmd; /* current command (lowercase) */
77 int param; /* parameter number */
78 bool rel; /* true if relative coords */
79 double params[7]; /* parameters that have been parsed */
80 };
82 static void rsvg_path_arc_segment(RSVGParsePathCtx *ctx,
83 double xc, double yc,
84 double th0, double th1,
85 double rx, double ry, double x_axis_rotation)
86 {
87 double sin_th, cos_th;
88 double a00, a01, a10, a11;
89 double x1, y1, x2, y2, x3, y3;
90 double t;
91 double th_half;
93 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
94 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
95 /* inverse transform compared with rsvg_path_arc */
96 a00 = cos_th * rx;
97 a01 = -sin_th * ry;
98 a10 = sin_th * rx;
99 a11 = cos_th * ry;
101 th_half = 0.5 * (th1 - th0);
102 t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
103 x1 = xc + cos (th0) - t * sin (th0);
104 y1 = yc + sin (th0) + t * cos (th0);
105 x3 = xc + cos (th1);
106 y3 = yc + sin (th1);
107 x2 = x3 + t * sin (th1);
108 y2 = y3 - t * cos (th1);
109 rsvg_bpath_def_curveto(ctx->bpath,
110 a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
111 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
112 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
113 }
115 /**
116 * rsvg_path_arc: Add an RSVG arc to the path context.
117 * @ctx: Path context.
118 * @rx: Radius in x direction (before rotation).
119 * @ry: Radius in y direction (before rotation).
120 * @x_axis_rotation: Rotation angle for axes.
121 * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
122 * @sweep: 0 for "negative angle", 1 for "positive angle".
123 * @x: New x coordinate.
124 * @y: New y coordinate.
125 *
126 **/
127 static void rsvg_path_arc (RSVGParsePathCtx *ctx,
128 double rx, double ry, double x_axis_rotation,
129 int large_arc_flag, int sweep_flag,
130 double x, double y)
131 {
132 double sin_th, cos_th;
133 double a00, a01, a10, a11;
134 double x0, y0, x1, y1, xc, yc;
135 double d, sfactor, sfactor_sq;
136 double th0, th1, th_arc;
137 double px, py, pl;
138 int i, n_segs;
140 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
141 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
143 /*
144 Correction of out-of-range radii as described in Appendix F.6.6:
146 1. Ensure radii are non-zero (Done?).
147 2. Ensure that radii are positive.
148 3. Ensure that radii are large enough.
149 */
151 if(rx < 0.0) rx = -rx;
152 if(ry < 0.0) ry = -ry;
154 px = cos_th * (ctx->cpx - x) * 0.5 + sin_th * (ctx->cpy - y) * 0.5;
155 py = cos_th * (ctx->cpy - y) * 0.5 - sin_th * (ctx->cpx - x) * 0.5;
156 pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
158 if(pl > 1.0)
159 {
160 pl = sqrt(pl);
161 rx *= pl;
162 ry *= pl;
163 }
165 /* Proceed with computations as described in Appendix F.6.5 */
167 a00 = cos_th / rx;
168 a01 = sin_th / rx;
169 a10 = -sin_th / ry;
170 a11 = cos_th / ry;
171 x0 = a00 * ctx->cpx + a01 * ctx->cpy;
172 y0 = a10 * ctx->cpx + a11 * ctx->cpy;
173 x1 = a00 * x + a01 * y;
174 y1 = a10 * x + a11 * y;
175 /* (x0, y0) is current point in transformed coordinate space.
176 (x1, y1) is new point in transformed coordinate space.
178 The arc fits a unit-radius circle in this space.
179 */
180 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
181 sfactor_sq = 1.0 / d - 0.25;
182 if (sfactor_sq < 0) sfactor_sq = 0;
183 sfactor = sqrt (sfactor_sq);
184 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
185 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
186 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
187 /* (xc, yc) is center of the circle. */
189 th0 = atan2 (y0 - yc, x0 - xc);
190 th1 = atan2 (y1 - yc, x1 - xc);
192 th_arc = th1 - th0;
193 if (th_arc < 0 && sweep_flag)
194 th_arc += 2 * M_PI;
195 else if (th_arc > 0 && !sweep_flag)
196 th_arc -= 2 * M_PI;
198 n_segs = (int) ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
200 for (i = 0; i < n_segs; i++) {
201 rsvg_path_arc_segment(ctx, xc, yc,
202 th0 + i * th_arc / n_segs,
203 th0 + (i + 1) * th_arc / n_segs,
204 rx, ry, x_axis_rotation);
205 }
207 ctx->cpx = x;
208 ctx->cpy = y;
209 }
211 static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx)
212 {
213 double x1, y1, x2, y2, x3, y3;
215 switch (ctx->cmd) {
216 case 'm':
217 /* moveto */
218 if (ctx->param == 2)
219 {
220 #ifdef VERBOSE
221 g_print ("'m' moveto %g,%g\n",
222 ctx->params[0], ctx->params[1]);
223 #endif
224 rsvg_bpath_def_moveto (ctx->bpath,
225 ctx->params[0], ctx->params[1]);
226 ctx->cpx = ctx->rpx = ctx->spx = ctx->params[0];
227 ctx->cpy = ctx->rpy = ctx->spy = ctx->params[1];
228 ctx->param = 0;
229 ctx->cmd = 'l';
230 /* Ref: http://www.w3.org/TR/SVG11/paths.html#PathDataMovetoCommands: "If a moveto is
231 * followed by multiple pairs of coordinates, the subsequent pairs are treated as
232 * implicit lineto commands." */
233 }
234 break;
236 case 'l':
237 /* lineto */
238 if (ctx->param == 2)
239 {
240 #ifdef VERBOSE
241 g_print ("'l' lineto %g,%g\n",
242 ctx->params[0], ctx->params[1]);
243 #endif
244 rsvg_bpath_def_lineto (ctx->bpath,
245 ctx->params[0], ctx->params[1]);
246 ctx->cpx = ctx->rpx = ctx->params[0];
247 ctx->cpy = ctx->rpy = ctx->params[1];
248 ctx->param = 0;
249 }
250 break;
252 case 'c':
253 /* curveto */
254 if (ctx->param == 6)
255 {
256 x1 = ctx->params[0];
257 y1 = ctx->params[1];
258 x2 = ctx->params[2];
259 y2 = ctx->params[3];
260 x3 = ctx->params[4];
261 y3 = ctx->params[5];
262 #ifdef VERBOSE
263 g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
264 x1, y1, x2, y2, x3, y3);
265 #endif
266 rsvg_bpath_def_curveto (ctx->bpath,
267 x1, y1, x2, y2, x3, y3);
268 ctx->rpx = x2;
269 ctx->rpy = y2;
270 ctx->cpx = x3;
271 ctx->cpy = y3;
272 ctx->param = 0;
273 }
274 break;
276 case 's':
277 /* smooth curveto */
278 if (ctx->param == 4)
279 {
280 x1 = 2 * ctx->cpx - ctx->rpx;
281 y1 = 2 * ctx->cpy - ctx->rpy;
282 x2 = ctx->params[0];
283 y2 = ctx->params[1];
284 x3 = ctx->params[2];
285 y3 = ctx->params[3];
286 #ifdef VERBOSE
287 g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
288 x1, y1, x2, y2, x3, y3);
289 #endif
290 rsvg_bpath_def_curveto (ctx->bpath,
291 x1, y1, x2, y2, x3, y3);
292 ctx->rpx = x2;
293 ctx->rpy = y2;
294 ctx->cpx = x3;
295 ctx->cpy = y3;
296 ctx->param = 0;
297 }
298 break;
300 case 'h':
301 /* horizontal lineto */
302 if (ctx->param == 1) {
303 #ifdef VERBOSE
304 g_print ("'h' lineto %g,%g\n",
305 ctx->params[0], ctx->cpy);
306 #endif
307 rsvg_bpath_def_lineto (ctx->bpath,
308 ctx->params[0], ctx->cpy);
309 ctx->cpx = ctx->rpx = ctx->params[0];
310 ctx->param = 0;
311 }
312 break;
314 case 'v':
315 /* vertical lineto */
316 if (ctx->param == 1) {
317 #ifdef VERBOSE
318 g_print ("'v' lineto %g,%g\n",
319 ctx->cpx, ctx->params[0]);
320 #endif
321 rsvg_bpath_def_lineto (ctx->bpath,
322 ctx->cpx, ctx->params[0]);
323 ctx->cpy = ctx->rpy = ctx->params[0];
324 ctx->param = 0;
325 }
326 break;
328 case 'q':
329 /* quadratic bezier curveto */
331 /* non-normative reference:
332 http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
333 */
334 if (ctx->param == 4)
335 {
336 /* raise quadratic bezier to cubic */
337 x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
338 y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
339 x3 = ctx->params[2];
340 y3 = ctx->params[3];
341 x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
342 y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
343 #ifdef VERBOSE
344 g_print("'q' curveto %g,%g %g,%g, %g,%g\n",
345 x1, y1, x2, y2, x3, y3);
346 #endif
347 rsvg_bpath_def_curveto(ctx->bpath,
348 x1, y1, x2, y2, x3, y3);
349 ctx->rpx = ctx->params[0];
350 ctx->rpy = ctx->params[1];
351 ctx->cpx = x3;
352 ctx->cpy = y3;
353 ctx->param = 0;
354 }
355 break;
357 case 't':
358 /* Truetype quadratic bezier curveto */
359 if (ctx->param == 2) {
360 double xc, yc; /* quadratic control point */
362 xc = 2 * ctx->cpx - ctx->rpx;
363 yc = 2 * ctx->cpy - ctx->rpy;
364 /* generate a quadratic bezier with control point = xc, yc */
365 x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
366 y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
367 x3 = ctx->params[0];
368 y3 = ctx->params[1];
369 x2 = (x3 + 2 * xc) * (1.0 / 3.0);
370 y2 = (y3 + 2 * yc) * (1.0 / 3.0);
371 #ifdef VERBOSE
372 g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
373 x1, y1, x2, y2, x3, y3);
374 #endif
375 rsvg_bpath_def_curveto (ctx->bpath,
376 x1, y1, x2, y2, x3, y3);
377 ctx->rpx = xc;
378 ctx->rpy = yc;
379 ctx->cpx = x3;
380 ctx->cpy = y3;
381 ctx->param = 0;
382 }
383 break;
385 case 'a':
386 if (ctx->param == 7)
387 {
388 rsvg_path_arc(ctx,
389 ctx->params[0], ctx->params[1], ctx->params[2],
390 (int) ctx->params[3], (int) ctx->params[4],
391 ctx->params[5], ctx->params[6]);
392 ctx->param = 0;
393 }
394 break;
396 default:
397 g_assert_not_reached();
398 }
399 }
401 static void rsvg_parse_path_do_closepath(RSVGParsePathCtx *const ctx, const char next_cmd)
402 {
403 g_assert(ctx->param == 0);
405 rsvg_bpath_def_closepath (ctx->bpath);
406 ctx->cpx = ctx->rpx = ctx->spx;
407 ctx->cpy = ctx->rpy = ctx->spy;
409 if (next_cmd != 0 && next_cmd != 'm') {
410 // This makes sure we do the right moveto if the closepath is followed by anything other than a moveto.
411 /* Ref: http://www.w3.org/TR/SVG11/paths.html#PathDataClosePathCommand: "If a
412 * "closepath" is followed immediately by a "moveto", then the "moveto" identifies
413 * the start point of the next subpath. If a "closepath" is followed immediately by
414 * any other command, then the next subpath starts at the same initial point as the
415 * current subpath." */
417 ctx->cmd = 'm';
418 ctx->params[0] = ctx->cpx;
419 ctx->params[1] = ctx->cpy;
420 ctx->param = 2;
421 rsvg_parse_path_do_cmd(ctx);
423 /* Any token after a closepath must be a command, not a parameter. We enforce this
424 * by clearing cmd rather than leaving as 'm'. */
425 ctx->cmd = '\0';
426 }
427 }
429 static char const* rsvg_parse_unsigned_int(guint64 *val, char const *begin, bool zeroVal = true) {
430 if (zeroVal) *val = 0;
431 while('0' <= *begin && *begin <= '9') {
432 *val *= 10;
433 *val += *begin - '0';
434 begin++;
435 }
436 return begin;
437 }
439 static char const* rsvg_parse_sign(bool *neg, char const *begin) {
440 *neg = false;
441 if (*begin == '+') {
442 begin++;
443 } else if (*begin == '-') {
444 *neg = true;
445 begin++;
446 }
447 return begin;
448 }
450 static char const* rsvg_parse_int(gint64 *val, char const *begin) {
451 bool neg;
452 char const *begin_of_int = rsvg_parse_sign(&neg, begin);
453 char const *end_of_int = rsvg_parse_unsigned_int((guint64*)val, begin_of_int);
454 if (neg) *val = -(*val);
455 return end_of_int==begin_of_int ? begin : end_of_int;
456 }
458 static char const* rsvg_parse_unsigned_float(double *val, char const *begin) {
459 // A number is either one or more digits, optionally followed by a period and zero or more digits (and an exponent),
460 // or zero or more digits, followed by a period and one or more digits (and an exponent)
461 // See http://www.w3.org/TR/SVG/paths.html#PathDataBNF
462 guint64 intval;
463 int exp=0;
464 char const *begin_of_num = begin;
465 char const *end_of_num = rsvg_parse_unsigned_int(&intval, begin_of_num);
466 if (*end_of_num == '.') {
467 char const *begin_of_frac = end_of_num+1;
468 char const *end_of_frac = rsvg_parse_unsigned_int(&intval, begin_of_frac, false);
469 if (end_of_num != begin_of_num || end_of_frac != begin_of_frac) {
470 end_of_num = end_of_frac;
471 exp = -(int)(end_of_frac-begin_of_frac);
472 }
473 }
474 if (end_of_num != begin_of_num && (*end_of_num == 'e' || *end_of_num == 'E')) {
475 gint64 exponent;
476 char const *begin_of_exp = end_of_num+1;
477 char const *end_of_exp = rsvg_parse_int(&exponent, begin_of_exp);
478 if (end_of_exp != begin_of_exp) {
479 end_of_num = end_of_exp;
480 exp += (int)exponent;
481 }
482 }
484 *val = ( exp < 0
485 ? intval / pow(10, -exp)
486 : intval * pow(10, exp) );
487 return end_of_num;
488 }
490 static char const* rsvg_parse_float(double *val, char const *begin) {
491 bool neg;
492 char const *begin_of_num = rsvg_parse_sign(&neg, begin);
493 char const *end_of_num = rsvg_parse_unsigned_float(val, begin_of_num);
494 if (neg) *val = -(*val);
495 return end_of_num == begin_of_num ? begin : end_of_num;
496 }
498 static void rsvg_parse_path_data(RSVGParsePathCtx *ctx, char const *const begin) {
499 /* fixme: Do better error processing: e.g. at least stop parsing as soon as we find an error.
500 * At some point we'll need to do all of
501 * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
502 */
504 /* Comma is always allowed after a number token (so long as it's followed by another number:
505 * see require_number), and never allowed anywhere else. Only one comma is allowed between
506 * neighbouring number tokens. */
507 bool comma_allowed = false;
509 /* After a command other than closepath, and after a comma, we require a number. */
510 bool require_number = false;
512 for (char const *cur = begin;; ++cur) {
513 int const c = *cur;
514 if (c <= ' ') {
515 switch (c) {
516 case ' ':
517 case '\t':
518 case '\n':
519 case '\r':
520 /* wsp */
521 break;
523 case '\0':
524 if (require_number || ctx->param) {
525 goto error;
526 }
527 goto done;
529 default:
530 goto error;
531 }
532 } else if (c == ',') {
533 if (!comma_allowed) {
534 goto error;
535 }
536 comma_allowed = false;
537 require_number = true;
538 } else if (c <= '9') {
539 if (!ctx->cmd || ctx->cmd == 'z') {
540 goto error;
541 }
543 double val;
544 char const *const end = rsvg_parse_float(&val, cur);
545 if (cur == end) {
546 goto error;
547 }
549 /* Special requirements for elliptical-arc arguments. */
550 if (ctx->cmd == 'a') {
551 if (ctx->param < 2) {
552 if (c <= '-') {
553 /* Error: sign not allowed for first two params. */
554 goto error;
555 }
556 } else if (ctx->param <= 4 && ctx->param >= 3) {
557 if (end - cur != 1 || c < '0' || c > '1') {
558 /* Error: flag must be either literally "0" or literally "1". */
559 goto error;
560 }
561 }
562 }
564 if (ctx->rel) {
565 /* Handle relative coordinates. */
566 switch (ctx->cmd) {
567 case 'l':
568 case 'm':
569 case 'c':
570 case 's':
571 case 'q':
572 case 't':
573 if ( ctx->param & 1 ) {
574 val += ctx->cpy; /* odd param, y */
575 } else {
576 val += ctx->cpx; /* even param, x */
577 }
578 break;
579 case 'a':
580 /* rule: sixth and seventh are x and y, rest are not
581 relative */
582 if (ctx->param == 5)
583 val += ctx->cpx;
584 else if (ctx->param == 6)
585 val += ctx->cpy;
586 break;
587 case 'h':
588 /* rule: x-relative */
589 val += ctx->cpx;
590 break;
591 case 'v':
592 /* rule: y-relative */
593 val += ctx->cpy;
594 break;
595 }
596 }
597 ctx->params[ctx->param++] = val;
598 rsvg_parse_path_do_cmd(ctx);
599 comma_allowed = true;
600 require_number = false;
601 cur = end - 1;
602 } else {
603 /* Command. */
604 if (require_number || ctx->param) {
605 goto error;
606 }
607 char next_cmd;
608 if (c <= 'Z') {
609 next_cmd = c + ('a' - 'A');
610 ctx->rel = false;
611 } else {
612 next_cmd = c;
613 ctx->rel = true;
614 }
616 comma_allowed = false;
617 require_number = true;
618 switch (next_cmd) {
619 case 'z':
620 require_number = false;
621 case 'm':
622 case 'l':
623 case 'h':
624 case 'v':
625 case 'c':
626 case 's':
627 case 'q':
628 case 't':
629 case 'a':
630 /* valid command */
631 break;
633 default:
634 goto error;
635 }
637 if (ctx->cmd == 'z') {
638 /* Closepath is the only command that allows no arguments. */
639 rsvg_parse_path_do_closepath(ctx, next_cmd);
640 }
641 ctx->cmd = next_cmd;
642 }
643 }
645 done:
646 if (ctx->cmd == 'z') {
647 rsvg_parse_path_do_closepath(ctx, 0);
648 }
649 return;
651 error:
652 /* todo: set an error indicator. */
653 goto done;
654 }
657 NArtBpath *sp_svg_read_path(gchar const *str)
658 {
659 RSVGParsePathCtx ctx;
660 NArtBpath *bpath;
662 ctx.bpath = gnome_canvas_bpath_def_new ();
663 ctx.cpx = 0.0;
664 ctx.cpy = 0.0;
665 ctx.cmd = 0;
666 ctx.param = 0;
668 rsvg_parse_path_data (&ctx, str);
670 gnome_canvas_bpath_def_art_finish (ctx.bpath);
672 bpath = g_new (NArtBpath, ctx.bpath->n_bpath);
673 memcpy (bpath, ctx.bpath->bpath, ctx.bpath->n_bpath * sizeof (NArtBpath));
674 g_assert ((bpath + ctx.bpath->n_bpath - 1)->code == NR_END);
675 gnome_canvas_bpath_def_unref (ctx.bpath);
677 return bpath;
678 }
680 /*
681 * Parses the path in str. When an error is found in the pathstring, this method
682 * returns a truncated path up to where the error was found in the pathstring.
683 * Returns an empty PathVector when str==NULL
684 */
685 Geom::PathVector sp_svg_read_pathv(char const * str)
686 {
687 Geom::PathVector pathv;
688 if (!str)
689 return pathv; // return empty pathvector when str == NULL
692 typedef std::back_insert_iterator<Geom::PathVector> Inserter;
693 Inserter iter(pathv);
694 Geom::SVGPathGenerator<Inserter> generator(iter);
696 try {
697 Geom::parse_svg_path(str, generator);
698 }
699 catch (Geom::SVGPathParseError e) {
700 generator.finish();
701 g_warning("Malformed SVG path, truncated path up to where error was found.\n Input path=\"%s\"\n Parsed path=\"%s\"", str, sp_svg_write_path(pathv));
702 }
704 return pathv;
705 }
707 gchar *sp_svg_write_path(NArtBpath const *bpath)
708 {
709 bool closed=false;
711 g_return_val_if_fail (bpath != NULL, NULL);
713 Inkscape::SVG::PathString str;
715 for (int i = 0; bpath[i].code != NR_END; i++){
716 switch (bpath [i].code){
717 case NR_LINETO:
718 if (!closed || bpath[i+1].code == NR_LINETO || bpath[i+1].code == NR_CURVETO) {
719 str.lineTo(bpath[i].x3, bpath[i].y3);
720 }
721 break;
723 case NR_CURVETO:
724 str.curveTo(bpath[i].x1, bpath[i].y1,
725 bpath[i].x2, bpath[i].y2,
726 bpath[i].x3, bpath[i].y3);
727 break;
729 case NR_MOVETO_OPEN:
730 case NR_MOVETO:
731 if (closed) {
732 str.closePath();
733 }
734 closed = ( bpath[i].code == NR_MOVETO );
735 str.moveTo(bpath[i].x3, bpath[i].y3);
736 break;
738 default:
739 g_assert_not_reached ();
740 }
741 }
742 if (closed) {
743 str.closePath();
744 }
746 return g_strdup(str.c_str());
747 }
749 static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve const * c) {
750 if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const *>(c)) {
751 // don't serialize stitch segments
752 if (!dynamic_cast<Geom::Path::StitchSegment const *>(c)) {
753 str.lineTo( (*line_segment)[1][0], (*line_segment)[1][1] );
754 }
755 }
756 else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const *>(c)) {
757 str.quadTo( (*quadratic_bezier)[1][0], (*quadratic_bezier)[1][1],
758 (*quadratic_bezier)[2][0], (*quadratic_bezier)[2][1] );
759 }
760 else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(c)) {
761 str.curveTo( (*cubic_bezier)[1][0], (*cubic_bezier)[1][1],
762 (*cubic_bezier)[2][0], (*cubic_bezier)[2][1],
763 (*cubic_bezier)[3][0], (*cubic_bezier)[3][1] );
764 }
765 else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc const *>(c)) {
766 str.arcTo( svg_elliptical_arc->ray(0), svg_elliptical_arc->ray(1),
767 svg_elliptical_arc->rotation_angle(),
768 svg_elliptical_arc->large_arc_flag(), svg_elliptical_arc->sweep_flag(),
769 svg_elliptical_arc->finalPoint() );
770 }
771 else if(Geom::HLineSegment const *hline_segment = dynamic_cast<Geom::HLineSegment const *>(c)) {
772 str.horizontalLineTo( hline_segment->finalPoint()[0] );
773 }
774 else if(Geom::VLineSegment const *vline_segment = dynamic_cast<Geom::VLineSegment const *>(c)) {
775 str.verticalLineTo( vline_segment->finalPoint()[1] );
776 } else {
777 //this case handles sbasis as well as all other curve types
778 Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c->toSBasis(), 0.1);
780 //recurse to convert the new path resulting from the sbasis to svgd
781 for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
782 sp_svg_write_curve(str, &(*iter));
783 }
784 }
785 }
787 static void sp_svg_write_path(Inkscape::SVG::PathString & str, Geom::Path const & p) {
788 str.moveTo( p.initialPoint()[0], p.initialPoint()[1] );
790 for(Geom::Path::const_iterator cit = p.begin(); cit != p.end_open(); cit++) {
791 sp_svg_write_curve(str, &(*cit));
792 }
794 if (p.closed()) {
795 str.closePath();
796 }
797 }
799 gchar * sp_svg_write_path(Geom::PathVector const &p) {
800 Inkscape::SVG::PathString str;
802 for(Geom::PathVector::const_iterator pit = p.begin(); pit != p.end(); pit++) {
803 sp_svg_write_path(str, *pit);
804 }
806 return g_strdup(str.c_str());
807 }
809 gchar * sp_svg_write_path(Geom::Path const &p) {
810 Inkscape::SVG::PathString str;
812 sp_svg_write_path(str, p);
814 return g_strdup(str.c_str());
815 }
817 /*
818 Local Variables:
819 mode:c++
820 c-file-style:"stroustrup"
821 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
822 indent-tabs-mode:nil
823 fill-column:99
824 End:
825 */
826 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :