3deb6105c6cb9de869fbb6c0fb595cc44306b8eb
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/sbasis-to-bezier.h>
45 #include <2geom/svg-path.h>
46 #include <2geom/svg-path-parser.h>
47 #include <2geom/exception.h>
49 /* This module parses an SVG path element into an RsvgBpathDef.
51 At present, there is no support for <marker> or any other contextual
52 information from the SVG file. The API will need to change rather
53 significantly to support these.
55 Reference: SVG working draft 3 March 2000, section 8.
56 */
58 #ifndef M_PI
59 #define M_PI 3.14159265358979323846
60 #endif /* M_PI */
62 /* We are lazy ;-) (Lauris) */
63 #define rsvg_bpath_def_new gnome_canvas_bpath_def_new
64 #define rsvg_bpath_def_moveto gnome_canvas_bpath_def_moveto
65 #define rsvg_bpath_def_lineto gnome_canvas_bpath_def_lineto
66 #define rsvg_bpath_def_curveto gnome_canvas_bpath_def_curveto
67 #define rsvg_bpath_def_closepath gnome_canvas_bpath_def_closepath
69 struct RSVGParsePathCtx {
70 GnomeCanvasBpathDef *bpath;
71 double cpx, cpy; /* current point */
72 double rpx, rpy; /* reflection point (for 's' and 't' commands) */
73 double spx, spy; /* beginning of current subpath point */
74 char cmd; /* current command (lowercase) */
75 int param; /* parameter number */
76 bool rel; /* true if relative coords */
77 double params[7]; /* parameters that have been parsed */
78 };
80 static void rsvg_path_arc_segment(RSVGParsePathCtx *ctx,
81 double xc, double yc,
82 double th0, double th1,
83 double rx, double ry, double x_axis_rotation)
84 {
85 double sin_th, cos_th;
86 double a00, a01, a10, a11;
87 double x1, y1, x2, y2, x3, y3;
88 double t;
89 double th_half;
91 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
92 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
93 /* inverse transform compared with rsvg_path_arc */
94 a00 = cos_th * rx;
95 a01 = -sin_th * ry;
96 a10 = sin_th * rx;
97 a11 = cos_th * ry;
99 th_half = 0.5 * (th1 - th0);
100 t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
101 x1 = xc + cos (th0) - t * sin (th0);
102 y1 = yc + sin (th0) + t * cos (th0);
103 x3 = xc + cos (th1);
104 y3 = yc + sin (th1);
105 x2 = x3 + t * sin (th1);
106 y2 = y3 - t * cos (th1);
107 rsvg_bpath_def_curveto(ctx->bpath,
108 a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
109 a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
110 a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
111 }
113 /**
114 * rsvg_path_arc: Add an RSVG arc to the path context.
115 * @ctx: Path context.
116 * @rx: Radius in x direction (before rotation).
117 * @ry: Radius in y direction (before rotation).
118 * @x_axis_rotation: Rotation angle for axes.
119 * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
120 * @sweep: 0 for "negative angle", 1 for "positive angle".
121 * @x: New x coordinate.
122 * @y: New y coordinate.
123 *
124 **/
125 static void rsvg_path_arc (RSVGParsePathCtx *ctx,
126 double rx, double ry, double x_axis_rotation,
127 int large_arc_flag, int sweep_flag,
128 double x, double y)
129 {
130 double sin_th, cos_th;
131 double a00, a01, a10, a11;
132 double x0, y0, x1, y1, xc, yc;
133 double d, sfactor, sfactor_sq;
134 double th0, th1, th_arc;
135 double px, py, pl;
136 int i, n_segs;
138 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
139 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
141 /*
142 Correction of out-of-range radii as described in Appendix F.6.6:
144 1. Ensure radii are non-zero (Done?).
145 2. Ensure that radii are positive.
146 3. Ensure that radii are large enough.
147 */
149 if(rx < 0.0) rx = -rx;
150 if(ry < 0.0) ry = -ry;
152 px = cos_th * (ctx->cpx - x) * 0.5 + sin_th * (ctx->cpy - y) * 0.5;
153 py = cos_th * (ctx->cpy - y) * 0.5 - sin_th * (ctx->cpx - x) * 0.5;
154 pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
156 if(pl > 1.0)
157 {
158 pl = sqrt(pl);
159 rx *= pl;
160 ry *= pl;
161 }
163 /* Proceed with computations as described in Appendix F.6.5 */
165 a00 = cos_th / rx;
166 a01 = sin_th / rx;
167 a10 = -sin_th / ry;
168 a11 = cos_th / ry;
169 x0 = a00 * ctx->cpx + a01 * ctx->cpy;
170 y0 = a10 * ctx->cpx + a11 * ctx->cpy;
171 x1 = a00 * x + a01 * y;
172 y1 = a10 * x + a11 * y;
173 /* (x0, y0) is current point in transformed coordinate space.
174 (x1, y1) is new point in transformed coordinate space.
176 The arc fits a unit-radius circle in this space.
177 */
178 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
179 sfactor_sq = 1.0 / d - 0.25;
180 if (sfactor_sq < 0) sfactor_sq = 0;
181 sfactor = sqrt (sfactor_sq);
182 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
183 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
184 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
185 /* (xc, yc) is center of the circle. */
187 th0 = atan2 (y0 - yc, x0 - xc);
188 th1 = atan2 (y1 - yc, x1 - xc);
190 th_arc = th1 - th0;
191 if (th_arc < 0 && sweep_flag)
192 th_arc += 2 * M_PI;
193 else if (th_arc > 0 && !sweep_flag)
194 th_arc -= 2 * M_PI;
196 n_segs = (int) ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
198 for (i = 0; i < n_segs; i++) {
199 rsvg_path_arc_segment(ctx, xc, yc,
200 th0 + i * th_arc / n_segs,
201 th0 + (i + 1) * th_arc / n_segs,
202 rx, ry, x_axis_rotation);
203 }
205 ctx->cpx = x;
206 ctx->cpy = y;
207 }
209 static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx)
210 {
211 double x1, y1, x2, y2, x3, y3;
213 switch (ctx->cmd) {
214 case 'm':
215 /* moveto */
216 if (ctx->param == 2)
217 {
218 #ifdef VERBOSE
219 g_print ("'m' moveto %g,%g\n",
220 ctx->params[0], ctx->params[1]);
221 #endif
222 rsvg_bpath_def_moveto (ctx->bpath,
223 ctx->params[0], ctx->params[1]);
224 ctx->cpx = ctx->rpx = ctx->spx = ctx->params[0];
225 ctx->cpy = ctx->rpy = ctx->spy = ctx->params[1];
226 ctx->param = 0;
227 ctx->cmd = 'l';
228 /* Ref: http://www.w3.org/TR/SVG11/paths.html#PathDataMovetoCommands: "If a moveto is
229 * followed by multiple pairs of coordinates, the subsequent pairs are treated as
230 * implicit lineto commands." */
231 }
232 break;
234 case 'l':
235 /* lineto */
236 if (ctx->param == 2)
237 {
238 #ifdef VERBOSE
239 g_print ("'l' lineto %g,%g\n",
240 ctx->params[0], ctx->params[1]);
241 #endif
242 rsvg_bpath_def_lineto (ctx->bpath,
243 ctx->params[0], ctx->params[1]);
244 ctx->cpx = ctx->rpx = ctx->params[0];
245 ctx->cpy = ctx->rpy = ctx->params[1];
246 ctx->param = 0;
247 }
248 break;
250 case 'c':
251 /* curveto */
252 if (ctx->param == 6)
253 {
254 x1 = ctx->params[0];
255 y1 = ctx->params[1];
256 x2 = ctx->params[2];
257 y2 = ctx->params[3];
258 x3 = ctx->params[4];
259 y3 = ctx->params[5];
260 #ifdef VERBOSE
261 g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
262 x1, y1, x2, y2, x3, y3);
263 #endif
264 rsvg_bpath_def_curveto (ctx->bpath,
265 x1, y1, x2, y2, x3, y3);
266 ctx->rpx = x2;
267 ctx->rpy = y2;
268 ctx->cpx = x3;
269 ctx->cpy = y3;
270 ctx->param = 0;
271 }
272 break;
274 case 's':
275 /* smooth curveto */
276 if (ctx->param == 4)
277 {
278 x1 = 2 * ctx->cpx - ctx->rpx;
279 y1 = 2 * ctx->cpy - ctx->rpy;
280 x2 = ctx->params[0];
281 y2 = ctx->params[1];
282 x3 = ctx->params[2];
283 y3 = ctx->params[3];
284 #ifdef VERBOSE
285 g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
286 x1, y1, x2, y2, x3, y3);
287 #endif
288 rsvg_bpath_def_curveto (ctx->bpath,
289 x1, y1, x2, y2, x3, y3);
290 ctx->rpx = x2;
291 ctx->rpy = y2;
292 ctx->cpx = x3;
293 ctx->cpy = y3;
294 ctx->param = 0;
295 }
296 break;
298 case 'h':
299 /* horizontal lineto */
300 if (ctx->param == 1) {
301 #ifdef VERBOSE
302 g_print ("'h' lineto %g,%g\n",
303 ctx->params[0], ctx->cpy);
304 #endif
305 rsvg_bpath_def_lineto (ctx->bpath,
306 ctx->params[0], ctx->cpy);
307 ctx->cpx = ctx->rpx = ctx->params[0];
308 ctx->param = 0;
309 }
310 break;
312 case 'v':
313 /* vertical lineto */
314 if (ctx->param == 1) {
315 #ifdef VERBOSE
316 g_print ("'v' lineto %g,%g\n",
317 ctx->cpx, ctx->params[0]);
318 #endif
319 rsvg_bpath_def_lineto (ctx->bpath,
320 ctx->cpx, ctx->params[0]);
321 ctx->cpy = ctx->rpy = ctx->params[0];
322 ctx->param = 0;
323 }
324 break;
326 case 'q':
327 /* quadratic bezier curveto */
329 /* non-normative reference:
330 http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
331 */
332 if (ctx->param == 4)
333 {
334 /* raise quadratic bezier to cubic */
335 x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
336 y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
337 x3 = ctx->params[2];
338 y3 = ctx->params[3];
339 x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
340 y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
341 #ifdef VERBOSE
342 g_print("'q' curveto %g,%g %g,%g, %g,%g\n",
343 x1, y1, x2, y2, x3, y3);
344 #endif
345 rsvg_bpath_def_curveto(ctx->bpath,
346 x1, y1, x2, y2, x3, y3);
347 ctx->rpx = ctx->params[0];
348 ctx->rpy = ctx->params[1];
349 ctx->cpx = x3;
350 ctx->cpy = y3;
351 ctx->param = 0;
352 }
353 break;
355 case 't':
356 /* Truetype quadratic bezier curveto */
357 if (ctx->param == 2) {
358 double xc, yc; /* quadratic control point */
360 xc = 2 * ctx->cpx - ctx->rpx;
361 yc = 2 * ctx->cpy - ctx->rpy;
362 /* generate a quadratic bezier with control point = xc, yc */
363 x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
364 y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
365 x3 = ctx->params[0];
366 y3 = ctx->params[1];
367 x2 = (x3 + 2 * xc) * (1.0 / 3.0);
368 y2 = (y3 + 2 * yc) * (1.0 / 3.0);
369 #ifdef VERBOSE
370 g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
371 x1, y1, x2, y2, x3, y3);
372 #endif
373 rsvg_bpath_def_curveto (ctx->bpath,
374 x1, y1, x2, y2, x3, y3);
375 ctx->rpx = xc;
376 ctx->rpy = yc;
377 ctx->cpx = x3;
378 ctx->cpy = y3;
379 ctx->param = 0;
380 }
381 break;
383 case 'a':
384 if (ctx->param == 7)
385 {
386 rsvg_path_arc(ctx,
387 ctx->params[0], ctx->params[1], ctx->params[2],
388 (int) ctx->params[3], (int) ctx->params[4],
389 ctx->params[5], ctx->params[6]);
390 ctx->param = 0;
391 }
392 break;
394 default:
395 g_assert_not_reached();
396 }
397 }
399 static void rsvg_parse_path_do_closepath(RSVGParsePathCtx *const ctx, const char next_cmd)
400 {
401 g_assert(ctx->param == 0);
403 rsvg_bpath_def_closepath (ctx->bpath);
404 ctx->cpx = ctx->rpx = ctx->spx;
405 ctx->cpy = ctx->rpy = ctx->spy;
407 if (next_cmd != 0 && next_cmd != 'm') {
408 // This makes sure we do the right moveto if the closepath is followed by anything other than a moveto.
409 /* Ref: http://www.w3.org/TR/SVG11/paths.html#PathDataClosePathCommand: "If a
410 * "closepath" is followed immediately by a "moveto", then the "moveto" identifies
411 * the start point of the next subpath. If a "closepath" is followed immediately by
412 * any other command, then the next subpath starts at the same initial point as the
413 * current subpath." */
415 ctx->cmd = 'm';
416 ctx->params[0] = ctx->cpx;
417 ctx->params[1] = ctx->cpy;
418 ctx->param = 2;
419 rsvg_parse_path_do_cmd(ctx);
421 /* Any token after a closepath must be a command, not a parameter. We enforce this
422 * by clearing cmd rather than leaving as 'm'. */
423 ctx->cmd = '\0';
424 }
425 }
427 static char const* rsvg_parse_unsigned_int(guint64 *val, char const *begin, bool zeroVal = true) {
428 if (zeroVal) *val = 0;
429 while('0' <= *begin && *begin <= '9') {
430 *val *= 10;
431 *val += *begin - '0';
432 begin++;
433 }
434 return begin;
435 }
437 static char const* rsvg_parse_sign(bool *neg, char const *begin) {
438 *neg = false;
439 if (*begin == '+') {
440 begin++;
441 } else if (*begin == '-') {
442 *neg = true;
443 begin++;
444 }
445 return begin;
446 }
448 static char const* rsvg_parse_int(gint64 *val, char const *begin) {
449 bool neg;
450 char const *begin_of_int = rsvg_parse_sign(&neg, begin);
451 char const *end_of_int = rsvg_parse_unsigned_int((guint64*)val, begin_of_int);
452 if (neg) *val = -(*val);
453 return end_of_int==begin_of_int ? begin : end_of_int;
454 }
456 static char const* rsvg_parse_unsigned_float(double *val, char const *begin) {
457 // A number is either one or more digits, optionally followed by a period and zero or more digits (and an exponent),
458 // or zero or more digits, followed by a period and one or more digits (and an exponent)
459 // See http://www.w3.org/TR/SVG/paths.html#PathDataBNF
460 guint64 intval;
461 int exp=0;
462 char const *begin_of_num = begin;
463 char const *end_of_num = rsvg_parse_unsigned_int(&intval, begin_of_num);
464 if (*end_of_num == '.') {
465 char const *begin_of_frac = end_of_num+1;
466 char const *end_of_frac = rsvg_parse_unsigned_int(&intval, begin_of_frac, false);
467 if (end_of_num != begin_of_num || end_of_frac != begin_of_frac) {
468 end_of_num = end_of_frac;
469 exp = -(int)(end_of_frac-begin_of_frac);
470 }
471 }
472 if (end_of_num != begin_of_num && (*end_of_num == 'e' || *end_of_num == 'E')) {
473 gint64 exponent;
474 char const *begin_of_exp = end_of_num+1;
475 char const *end_of_exp = rsvg_parse_int(&exponent, begin_of_exp);
476 if (end_of_exp != begin_of_exp) {
477 end_of_num = end_of_exp;
478 exp += (int)exponent;
479 }
480 }
482 *val = ( exp < 0
483 ? intval / pow(10, -exp)
484 : intval * pow(10, exp) );
485 return end_of_num;
486 }
488 static char const* rsvg_parse_float(double *val, char const *begin) {
489 bool neg;
490 char const *begin_of_num = rsvg_parse_sign(&neg, begin);
491 char const *end_of_num = rsvg_parse_unsigned_float(val, begin_of_num);
492 if (neg) *val = -(*val);
493 return end_of_num == begin_of_num ? begin : end_of_num;
494 }
496 static void rsvg_parse_path_data(RSVGParsePathCtx *ctx, char const *const begin) {
497 /* fixme: Do better error processing: e.g. at least stop parsing as soon as we find an error.
498 * At some point we'll need to do all of
499 * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
500 */
502 /* Comma is always allowed after a number token (so long as it's followed by another number:
503 * see require_number), and never allowed anywhere else. Only one comma is allowed between
504 * neighbouring number tokens. */
505 bool comma_allowed = false;
507 /* After a command other than closepath, and after a comma, we require a number. */
508 bool require_number = false;
510 for (char const *cur = begin;; ++cur) {
511 int const c = *cur;
512 if (c <= ' ') {
513 switch (c) {
514 case ' ':
515 case '\t':
516 case '\n':
517 case '\r':
518 /* wsp */
519 break;
521 case '\0':
522 if (require_number || ctx->param) {
523 goto error;
524 }
525 goto done;
527 default:
528 goto error;
529 }
530 } else if (c == ',') {
531 if (!comma_allowed) {
532 goto error;
533 }
534 comma_allowed = false;
535 require_number = true;
536 } else if (c <= '9') {
537 if (!ctx->cmd || ctx->cmd == 'z') {
538 goto error;
539 }
541 double val;
542 char const *const end = rsvg_parse_float(&val, cur);
543 if (cur == end) {
544 goto error;
545 }
547 /* Special requirements for elliptical-arc arguments. */
548 if (ctx->cmd == 'a') {
549 if (ctx->param < 2) {
550 if (c <= '-') {
551 /* Error: sign not allowed for first two params. */
552 goto error;
553 }
554 } else if (ctx->param <= 4 && ctx->param >= 3) {
555 if (end - cur != 1 || c < '0' || c > '1') {
556 /* Error: flag must be either literally "0" or literally "1". */
557 goto error;
558 }
559 }
560 }
562 if (ctx->rel) {
563 /* Handle relative coordinates. */
564 switch (ctx->cmd) {
565 case 'l':
566 case 'm':
567 case 'c':
568 case 's':
569 case 'q':
570 case 't':
571 if ( ctx->param & 1 ) {
572 val += ctx->cpy; /* odd param, y */
573 } else {
574 val += ctx->cpx; /* even param, x */
575 }
576 break;
577 case 'a':
578 /* rule: sixth and seventh are x and y, rest are not
579 relative */
580 if (ctx->param == 5)
581 val += ctx->cpx;
582 else if (ctx->param == 6)
583 val += ctx->cpy;
584 break;
585 case 'h':
586 /* rule: x-relative */
587 val += ctx->cpx;
588 break;
589 case 'v':
590 /* rule: y-relative */
591 val += ctx->cpy;
592 break;
593 }
594 }
595 ctx->params[ctx->param++] = val;
596 rsvg_parse_path_do_cmd(ctx);
597 comma_allowed = true;
598 require_number = false;
599 cur = end - 1;
600 } else {
601 /* Command. */
602 if (require_number || ctx->param) {
603 goto error;
604 }
605 char next_cmd;
606 if (c <= 'Z') {
607 next_cmd = c + ('a' - 'A');
608 ctx->rel = false;
609 } else {
610 next_cmd = c;
611 ctx->rel = true;
612 }
614 comma_allowed = false;
615 require_number = true;
616 switch (next_cmd) {
617 case 'z':
618 require_number = false;
619 case 'm':
620 case 'l':
621 case 'h':
622 case 'v':
623 case 'c':
624 case 's':
625 case 'q':
626 case 't':
627 case 'a':
628 /* valid command */
629 break;
631 default:
632 goto error;
633 }
635 if (ctx->cmd == 'z') {
636 /* Closepath is the only command that allows no arguments. */
637 rsvg_parse_path_do_closepath(ctx, next_cmd);
638 }
639 ctx->cmd = next_cmd;
640 }
641 }
643 done:
644 if (ctx->cmd == 'z') {
645 rsvg_parse_path_do_closepath(ctx, 0);
646 }
647 return;
649 error:
650 /* todo: set an error indicator. */
651 goto done;
652 }
655 NArtBpath *sp_svg_read_path(gchar const *str)
656 {
657 RSVGParsePathCtx ctx;
658 NArtBpath *bpath;
660 ctx.bpath = gnome_canvas_bpath_def_new ();
661 ctx.cpx = 0.0;
662 ctx.cpy = 0.0;
663 ctx.cmd = 0;
664 ctx.param = 0;
666 rsvg_parse_path_data (&ctx, str);
668 gnome_canvas_bpath_def_art_finish (ctx.bpath);
670 bpath = g_new (NArtBpath, ctx.bpath->n_bpath);
671 memcpy (bpath, ctx.bpath->bpath, ctx.bpath->n_bpath * sizeof (NArtBpath));
672 g_assert ((bpath + ctx.bpath->n_bpath - 1)->code == NR_END);
673 gnome_canvas_bpath_def_unref (ctx.bpath);
675 return bpath;
676 }
678 /*
679 * Parses the path in str. When an error is found in the pathstring, this method
680 * returns a truncated path up to where the error was found in the pathstring.
681 * Returns an empty PathVector when str==NULL
682 */
683 Geom::PathVector sp_svg_read_pathv(char const * str)
684 {
685 Geom::PathVector pathv;
686 if (!str)
687 return pathv; // return empty pathvector when str == NULL
690 typedef std::back_insert_iterator<Geom::PathVector> Inserter;
691 Inserter iter(pathv);
692 Geom::SVGPathGenerator<Inserter> generator(iter);
694 try {
695 Geom::parse_svg_path(str, generator);
696 }
697 catch (Geom::SVGPathParseError e) {
698 generator.finish();
699 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));
700 }
702 return pathv;
703 }
705 gchar *sp_svg_write_path(NArtBpath const *bpath)
706 {
707 bool closed=false;
709 g_return_val_if_fail (bpath != NULL, NULL);
711 Inkscape::SVG::PathString str;
713 for (int i = 0; bpath[i].code != NR_END; i++){
714 switch (bpath [i].code){
715 case NR_LINETO:
716 if (!closed || bpath[i+1].code == NR_LINETO || bpath[i+1].code == NR_CURVETO) {
717 str.lineTo(bpath[i].x3, bpath[i].y3);
718 }
719 break;
721 case NR_CURVETO:
722 str.curveTo(bpath[i].x1, bpath[i].y1,
723 bpath[i].x2, bpath[i].y2,
724 bpath[i].x3, bpath[i].y3);
725 break;
727 case NR_MOVETO_OPEN:
728 case NR_MOVETO:
729 if (closed) {
730 str.closePath();
731 }
732 closed = ( bpath[i].code == NR_MOVETO );
733 str.moveTo(bpath[i].x3, bpath[i].y3);
734 break;
736 default:
737 g_assert_not_reached ();
738 }
739 }
740 if (closed) {
741 str.closePath();
742 }
744 return g_strdup(str.c_str());
745 }
747 static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve const * c) {
748 if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const *>(c)) {
749 str.lineTo( (*line_segment)[1][0], (*line_segment)[1][1] );
750 }
751 else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const *>(c)) {
752 str.quadTo( (*quadratic_bezier)[1][0], (*quadratic_bezier)[1][1],
753 (*quadratic_bezier)[2][0], (*quadratic_bezier)[2][1] );
754 }
755 else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(c)) {
756 str.curveTo( (*cubic_bezier)[1][0], (*cubic_bezier)[1][1],
757 (*cubic_bezier)[2][0], (*cubic_bezier)[2][1],
758 (*cubic_bezier)[3][0], (*cubic_bezier)[3][1] );
759 }
760 else if(Geom::EllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(c)) {
761 str.arcTo( svg_elliptical_arc->ray(0), svg_elliptical_arc->ray(1),
762 svg_elliptical_arc->rotation_angle(),
763 svg_elliptical_arc->large_arc_flag(), svg_elliptical_arc->sweep_flag(),
764 svg_elliptical_arc->finalPoint() );
765 /* else if(Geom::HLineSegment const *hline_segment = dynamic_cast<Geom::HLineSegment const *>(c)) {
766 str.horizontalLineTo( ... );
767 }
768 else if(Geom::VLineSegment const *vline_segment = dynamic_cast<Geom::VLineSegment const *>(c)) {
769 str.verticalLineTo( ... ); */
770 } else {
771 //this case handles sbasis as well as all other curve types
772 Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c->toSBasis(), 0.1);
774 //recurse to convert the new path resulting from the sbasis to svgd
775 for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
776 sp_svg_write_curve(str, &(*iter));
777 }
778 }
779 }
781 gchar * sp_svg_write_path(Geom::PathVector const &p) {
782 Inkscape::SVG::PathString str;
784 for(Geom::PathVector::const_iterator pit = p.begin(); pit != p.end(); pit++) {
785 str.moveTo( pit->initialPoint()[0], pit->initialPoint()[1] );
787 for(Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); cit++) {
788 sp_svg_write_curve(str, &(*cit));
789 }
791 if (pit->closed()) {
792 str.closePath();
793 }
794 }
796 return g_strdup(str.c_str());
797 }
799 /*
800 Local Variables:
801 mode:c++
802 c-file-style:"stroustrup"
803 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
804 indent-tabs-mode:nil
805 fill-column:99
806 End:
807 */
808 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :