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