Code

update to 2geom rev. 1507
[inkscape.git] / src / svg / svg-path.cpp
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);
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)
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;
211 static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx)
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     }
401 static void rsvg_parse_path_do_closepath(RSVGParsePathCtx *const ctx, const char next_cmd)
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     }
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;
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;
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;
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;
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;
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;
657 NArtBpath *sp_svg_read_path(gchar const *str)
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;
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)
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;
707 gchar *sp_svg_write_path(NArtBpath const *bpath)
709     bool closed=false;
710     
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());
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     }
787 gchar * sp_svg_write_path(Geom::PathVector const &p) {
788     Inkscape::SVG::PathString str;
790     for(Geom::PathVector::const_iterator pit = p.begin(); pit != p.end(); pit++) {
791         str.moveTo( pit->initialPoint()[0], pit->initialPoint()[1] );
793         for(Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); cit++) {
794             sp_svg_write_curve(str, &(*cit));
795         }
797         if (pit->closed()) {
798             str.closePath();
799         }
800     }
802     return g_strdup(str.c_str());
805 /*
806   Local Variables:
807   mode:c++
808   c-file-style:"stroustrup"
809   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
810   indent-tabs-mode:nil
811   fill-column:99
812   End:
813 */
814 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :