Code

bd0d4a4f5ff1dbb7c7af872f8ff6779a4ec20dba
[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/path-string.h"
42 /* This module parses an SVG path element into an RsvgBpathDef.
44    At present, there is no support for <marker> or any other contextual
45    information from the SVG file. The API will need to change rather
46    significantly to support these.
48    Reference: SVG working draft 3 March 2000, section 8.
49 */
51 #ifndef M_PI
52 #define M_PI 3.14159265358979323846
53 #endif  /*  M_PI  */
55 /* We are lazy ;-) (Lauris) */
56 #define rsvg_bpath_def_new gnome_canvas_bpath_def_new
57 #define rsvg_bpath_def_moveto gnome_canvas_bpath_def_moveto
58 #define rsvg_bpath_def_lineto gnome_canvas_bpath_def_lineto
59 #define rsvg_bpath_def_curveto gnome_canvas_bpath_def_curveto
60 #define rsvg_bpath_def_closepath gnome_canvas_bpath_def_closepath
62 struct RSVGParsePathCtx {
63     GnomeCanvasBpathDef *bpath;
64     double cpx, cpy;  /* current point */
65     double rpx, rpy;  /* reflection point (for 's' and 't' commands) */
66     double spx, spy;  /* beginning of current subpath point */
67     char cmd;         /* current command (lowercase) */
68     int param;        /* parameter number */
69     bool rel;         /* true if relative coords */
70     double params[7]; /* parameters that have been parsed */
71 };
73 static void rsvg_path_arc_segment(RSVGParsePathCtx *ctx,
74               double xc, double yc,
75               double th0, double th1,
76               double rx, double ry, double x_axis_rotation)
77 {
78     double sin_th, cos_th;
79     double a00, a01, a10, a11;
80     double x1, y1, x2, y2, x3, y3;
81     double t;
82     double th_half;
84     sin_th = sin (x_axis_rotation * (M_PI / 180.0));
85     cos_th = cos (x_axis_rotation * (M_PI / 180.0)); 
86     /* inverse transform compared with rsvg_path_arc */
87     a00 = cos_th * rx;
88     a01 = -sin_th * ry;
89     a10 = sin_th * rx;
90     a11 = cos_th * ry;
92     th_half = 0.5 * (th1 - th0);
93     t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
94     x1 = xc + cos (th0) - t * sin (th0);
95     y1 = yc + sin (th0) + t * cos (th0);
96     x3 = xc + cos (th1);
97     y3 = yc + sin (th1);
98     x2 = x3 + t * sin (th1);
99     y2 = y3 - t * cos (th1);
100     rsvg_bpath_def_curveto(ctx->bpath,
101                            a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
102                            a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
103                            a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
106 /**
107  * rsvg_path_arc: Add an RSVG arc to the path context.
108  * @ctx: Path context.
109  * @rx: Radius in x direction (before rotation).
110  * @ry: Radius in y direction (before rotation).
111  * @x_axis_rotation: Rotation angle for axes.
112  * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
113  * @sweep: 0 for "negative angle", 1 for "positive angle".
114  * @x: New x coordinate.
115  * @y: New y coordinate.
116  *
117  **/
118 static void rsvg_path_arc (RSVGParsePathCtx *ctx,
119                            double rx, double ry, double x_axis_rotation,
120                            int large_arc_flag, int sweep_flag,
121                            double x, double y)
123     double sin_th, cos_th;
124     double a00, a01, a10, a11;
125     double x0, y0, x1, y1, xc, yc;
126     double d, sfactor, sfactor_sq;
127     double th0, th1, th_arc;
128     double px, py, pl;
129     int i, n_segs;
131     sin_th = sin (x_axis_rotation * (M_PI / 180.0));
132     cos_th = cos (x_axis_rotation * (M_PI / 180.0));
134     /*                                                                            
135                                                                                   Correction of out-of-range radii as described in Appendix F.6.6:           
137                                                                                   1. Ensure radii are non-zero (Done?).                                      
138                                                                                   2. Ensure that radii are positive.                                         
139                                                                                   3. Ensure that radii are large enough.                                     
140     */                                                                            
142     if(rx < 0.0) rx = -rx;                                                        
143     if(ry < 0.0) ry = -ry;                                                        
145     px = cos_th * (ctx->cpx - x) * 0.5 + sin_th * (ctx->cpy - y) * 0.5;           
146     py = cos_th * (ctx->cpy - y) * 0.5 - sin_th * (ctx->cpx - x) * 0.5;           
147     pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);                           
149     if(pl > 1.0)                                                                  
150     {                                                                             
151         pl  = sqrt(pl);                                                           
152         rx *= pl;                                                                 
153         ry *= pl;                                                                 
154     }                                                                             
156     /* Proceed with computations as described in Appendix F.6.5 */                
158     a00 = cos_th / rx;
159     a01 = sin_th / rx;
160     a10 = -sin_th / ry;
161     a11 = cos_th / ry;
162     x0 = a00 * ctx->cpx + a01 * ctx->cpy;
163     y0 = a10 * ctx->cpx + a11 * ctx->cpy;
164     x1 = a00 * x + a01 * y;
165     y1 = a10 * x + a11 * y;
166     /* (x0, y0) is current point in transformed coordinate space.
167        (x1, y1) is new point in transformed coordinate space.
169        The arc fits a unit-radius circle in this space.
170     */
171     d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
172     sfactor_sq = 1.0 / d - 0.25;
173     if (sfactor_sq < 0) sfactor_sq = 0;
174     sfactor = sqrt (sfactor_sq);
175     if (sweep_flag == large_arc_flag) sfactor = -sfactor;
176     xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
177     yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
178     /* (xc, yc) is center of the circle. */
180     th0 = atan2 (y0 - yc, x0 - xc);
181     th1 = atan2 (y1 - yc, x1 - xc);
183     th_arc = th1 - th0;
184     if (th_arc < 0 && sweep_flag)
185         th_arc += 2 * M_PI;
186     else if (th_arc > 0 && !sweep_flag)
187         th_arc -= 2 * M_PI;
189     n_segs = (int) ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
191     for (i = 0; i < n_segs; i++) {
192         rsvg_path_arc_segment(ctx, xc, yc,
193                               th0 + i * th_arc / n_segs,
194                               th0 + (i + 1) * th_arc / n_segs,
195                               rx, ry, x_axis_rotation);
196     }
198     ctx->cpx = x;
199     ctx->cpy = y;
202 static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx)
204     double x1, y1, x2, y2, x3, y3;
206     switch (ctx->cmd) {
207     case 'm':
208         /* moveto */
209         if (ctx->param == 2)
210         {
211 #ifdef VERBOSE
212             g_print ("'m' moveto %g,%g\n",
213                      ctx->params[0], ctx->params[1]);
214 #endif
215             rsvg_bpath_def_moveto (ctx->bpath,
216                                    ctx->params[0], ctx->params[1]);
217             ctx->cpx = ctx->rpx = ctx->spx = ctx->params[0];
218             ctx->cpy = ctx->rpy = ctx->spy = ctx->params[1];
219             ctx->param = 0;
220             ctx->cmd = 'l';
221             /* Ref: http://www.w3.org/TR/SVG11/paths.html#PathDataMovetoCommands: "If a moveto is
222              * followed by multiple pairs of coordinates, the subsequent pairs are treated as
223              * implicit lineto commands." */
224         }
225         break;
227     case 'l':
228         /* lineto */
229         if (ctx->param == 2)
230         {
231 #ifdef VERBOSE
232             g_print ("'l' lineto %g,%g\n",
233                      ctx->params[0], ctx->params[1]);
234 #endif
235             rsvg_bpath_def_lineto (ctx->bpath,
236                                    ctx->params[0], ctx->params[1]);
237             ctx->cpx = ctx->rpx = ctx->params[0];
238             ctx->cpy = ctx->rpy = ctx->params[1];
239             ctx->param = 0;
240         }
241         break;
243     case 'c':
244         /* curveto */
245         if (ctx->param == 6)
246         {
247             x1 = ctx->params[0];
248             y1 = ctx->params[1];
249             x2 = ctx->params[2];
250             y2 = ctx->params[3];
251             x3 = ctx->params[4];
252             y3 = ctx->params[5];
253 #ifdef VERBOSE
254             g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
255                      x1, y1, x2, y2, x3, y3);
256 #endif
257             rsvg_bpath_def_curveto (ctx->bpath,
258                                     x1, y1, x2, y2, x3, y3);
259             ctx->rpx = x2;
260             ctx->rpy = y2;
261             ctx->cpx = x3;
262             ctx->cpy = y3;
263             ctx->param = 0;
264         }
265         break;
267     case 's':
268         /* smooth curveto */
269         if (ctx->param == 4)
270         {
271             x1 = 2 * ctx->cpx - ctx->rpx;
272             y1 = 2 * ctx->cpy - ctx->rpy;
273             x2 = ctx->params[0];
274             y2 = ctx->params[1];
275             x3 = ctx->params[2];
276             y3 = ctx->params[3];
277 #ifdef VERBOSE
278             g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
279                      x1, y1, x2, y2, x3, y3);
280 #endif
281             rsvg_bpath_def_curveto (ctx->bpath,
282                                     x1, y1, x2, y2, x3, y3);
283             ctx->rpx = x2;
284             ctx->rpy = y2;
285             ctx->cpx = x3;
286             ctx->cpy = y3;
287             ctx->param = 0;
288         }
289         break;
291     case 'h':
292         /* horizontal lineto */
293         if (ctx->param == 1) {
294 #ifdef VERBOSE
295             g_print ("'h' lineto %g,%g\n",
296                      ctx->params[0], ctx->cpy);
297 #endif
298             rsvg_bpath_def_lineto (ctx->bpath,
299                                    ctx->params[0], ctx->cpy);
300             ctx->cpx = ctx->rpx = ctx->params[0];
301             ctx->param = 0;
302         }
303         break;
305     case 'v':
306         /* vertical lineto */
307         if (ctx->param == 1) {
308 #ifdef VERBOSE
309             g_print ("'v' lineto %g,%g\n",
310                      ctx->cpx, ctx->params[0]);
311 #endif
312             rsvg_bpath_def_lineto (ctx->bpath,
313                                    ctx->cpx, ctx->params[0]);
314             ctx->cpy = ctx->rpy = ctx->params[0];
315             ctx->param = 0;
316         }
317         break;
319     case 'q':
320         /* quadratic bezier curveto */
322         /* non-normative reference:
323            http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
324         */
325         if (ctx->param == 4)
326         {
327             /* raise quadratic bezier to cubic */
328             x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
329             y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
330             x3 = ctx->params[2];
331             y3 = ctx->params[3];
332             x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
333             y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
334 #ifdef VERBOSE
335             g_print("'q' curveto %g,%g %g,%g, %g,%g\n",
336                     x1, y1, x2, y2, x3, y3);
337 #endif
338             rsvg_bpath_def_curveto(ctx->bpath,
339                                    x1, y1, x2, y2, x3, y3);
340             ctx->rpx = ctx->params[0];
341             ctx->rpy = ctx->params[1];
342             ctx->cpx = x3;
343             ctx->cpy = y3;
344             ctx->param = 0;
345         }
346         break;
348     case 't':
349         /* Truetype quadratic bezier curveto */
350         if (ctx->param == 2) {
351             double xc, yc; /* quadratic control point */
353             xc = 2 * ctx->cpx - ctx->rpx;
354             yc = 2 * ctx->cpy - ctx->rpy;
355             /* generate a quadratic bezier with control point = xc, yc */
356             x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
357             y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
358             x3 = ctx->params[0];
359             y3 = ctx->params[1];
360             x2 = (x3 + 2 * xc) * (1.0 / 3.0);
361             y2 = (y3 + 2 * yc) * (1.0 / 3.0);
362 #ifdef VERBOSE
363             g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
364                      x1, y1, x2, y2, x3, y3);
365 #endif
366             rsvg_bpath_def_curveto (ctx->bpath,
367                                     x1, y1, x2, y2, x3, y3);
368             ctx->rpx = xc;
369             ctx->rpy = yc;
370             ctx->cpx = x3;
371             ctx->cpy = y3;
372             ctx->param = 0;
373         }
374         break;
376     case 'a':
377         if (ctx->param == 7)
378         {
379             rsvg_path_arc(ctx,
380                           ctx->params[0], ctx->params[1], ctx->params[2],
381                           (int) ctx->params[3], (int) ctx->params[4],
382                           ctx->params[5], ctx->params[6]);
383             ctx->param = 0;
384         }
385         break;
387     default:
388         g_assert_not_reached();
389     }
392 static void rsvg_parse_path_do_closepath(RSVGParsePathCtx *const ctx, const char next_cmd)
394     g_assert(ctx->param == 0);
396     rsvg_bpath_def_closepath (ctx->bpath);
397     ctx->cpx = ctx->rpx = ctx->spx;
398     ctx->cpy = ctx->rpy = ctx->spy;
400     if (next_cmd != 0 && next_cmd != 'm') {
401         // This makes sure we do the right moveto if the closepath is followed by anything other than a moveto.
402         /* Ref: http://www.w3.org/TR/SVG11/paths.html#PathDataClosePathCommand: "If a
403          * "closepath" is followed immediately by a "moveto", then the "moveto" identifies
404          * the start point of the next subpath. If a "closepath" is followed immediately by
405          * any other command, then the next subpath starts at the same initial point as the
406          * current subpath." */
408         ctx->cmd = 'm';
409         ctx->params[0] = ctx->cpx;
410         ctx->params[1] = ctx->cpy;
411         ctx->param = 2;
412         rsvg_parse_path_do_cmd(ctx);
414         /* Any token after a closepath must be a command, not a parameter.  We enforce this
415          * by clearing cmd rather than leaving as 'm'. */
416         ctx->cmd = '\0';
417     }
420 static char const* rsvg_parse_unsigned_int(guint64 *val, char const *begin, bool zeroVal = true) {
421     if (zeroVal) *val = 0;
422     while('0' <= *begin && *begin <= '9') {
423         *val *= 10;
424         *val += *begin - '0';
425         begin++;
426     }
427     return begin;
430 static char const* rsvg_parse_sign(bool *neg, char const *begin) {
431     *neg = false;
432     if (*begin == '+') {
433         begin++;
434     } else if (*begin == '-') {
435         *neg = true;
436         begin++;
437     }
438     return begin;
441 static char const* rsvg_parse_int(gint64 *val, char const *begin) {
442     bool neg;
443     char const *begin_of_int = rsvg_parse_sign(&neg, begin);
444     char const *end_of_int = rsvg_parse_unsigned_int((guint64*)val, begin_of_int);
445     if (neg) *val = -(*val);
446     return end_of_int==begin_of_int ? begin : end_of_int;
449 static char const* rsvg_parse_unsigned_float(double *val, char const *begin) {
450     // A number is either one or more digits, optionally followed by a period and zero or more digits (and an exponent),
451     //                 or zero or more digits, followed by a period and one or more digits (and an exponent)
452     // See http://www.w3.org/TR/SVG/paths.html#PathDataBNF
453     guint64 intval;
454     int exp=0;
455     char const *begin_of_num = begin;
456     char const *end_of_num = rsvg_parse_unsigned_int(&intval, begin_of_num);
457     if (*end_of_num == '.') {
458         char const *begin_of_frac = end_of_num+1;
459         char const *end_of_frac = rsvg_parse_unsigned_int(&intval, begin_of_frac, false);
460         if (end_of_num != begin_of_num || end_of_frac != begin_of_frac) {
461             end_of_num = end_of_frac;
462             exp = -(int)(end_of_frac-begin_of_frac);
463         }
464     }
465     if (end_of_num != begin_of_num && (*end_of_num == 'e' || *end_of_num == 'E')) {
466         gint64 exponent;
467         char const *begin_of_exp = end_of_num+1;
468         char const *end_of_exp = rsvg_parse_int(&exponent, begin_of_exp);
469         if (end_of_exp != begin_of_exp) {
470             end_of_num = end_of_exp;
471             exp += (int)exponent;
472         }
473     }
475     *val = ( exp < 0
476              ? intval / pow(10, -exp)
477              : intval * pow(10, exp) );
478     return end_of_num;
481 static char const* rsvg_parse_float(double *val, char const *begin) {
482     bool neg;
483     char const *begin_of_num = rsvg_parse_sign(&neg, begin);
484     char const *end_of_num = rsvg_parse_unsigned_float(val, begin_of_num);
485     if (neg) *val = -(*val);
486     return end_of_num == begin_of_num ? begin : end_of_num;
489 static void rsvg_parse_path_data(RSVGParsePathCtx *ctx, char const *const begin) {
490     /* fixme: Do better error processing: e.g. at least stop parsing as soon as we find an error.
491      * At some point we'll need to do all of
492      * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
493      */
495     /* Comma is always allowed after a number token (so long as it's followed by another number:
496      * see require_number), and never allowed anywhere else.  Only one comma is allowed between
497      * neighbouring number tokens. */
498     bool comma_allowed = false;
500     /* After a command other than closepath, and after a comma, we require a number. */
501     bool require_number = false;
503     for (char const *cur = begin;; ++cur) {
504         int const c = *cur;
505         if (c <= ' ') {
506             switch (c) {
507                 case ' ':
508                 case '\t':
509                 case '\n':
510                 case '\r':
511                     /* wsp */
512                     break;
514                 case '\0':
515                     if (require_number || ctx->param) {
516                         goto error;
517                     }
518                     goto done;
520                 default:
521                     goto error;
522             }
523         } else if (c == ',') {
524             if (!comma_allowed) {
525                 goto error;
526             }
527             comma_allowed = false;
528             require_number = true;
529         } else if (c <= '9') {
530             if (!ctx->cmd || ctx->cmd == 'z') {
531                 goto error;
532             }
534             double val;
535             char const *const end = rsvg_parse_float(&val, cur);
536             if (cur == end) {
537                 goto error;
538             }
540             /* Special requirements for elliptical-arc arguments. */
541             if (ctx->cmd == 'a') {
542                 if (ctx->param < 2) {
543                     if (c <= '-') {
544                         /* Error: sign not allowed for first two params. */
545                         goto error;
546                     }
547                 } else if (ctx->param <= 4 && ctx->param >= 3) {
548                     if (end - cur != 1 || c < '0' || c > '1') {
549                         /* Error: flag must be either literally "0" or literally "1". */
550                         goto error;
551                     }
552                 }
553             }
555             if (ctx->rel) {
556                 /* Handle relative coordinates. */
557                 switch (ctx->cmd) {
558                 case 'l':
559                 case 'm':
560                 case 'c':
561                 case 's':
562                 case 'q':
563                 case 't':
564                     if ( ctx->param & 1 ) {
565                         val += ctx->cpy; /* odd param, y */
566                     } else {
567                         val += ctx->cpx; /* even param, x */
568                     }
569                     break;
570                 case 'a':
571                     /* rule: sixth and seventh are x and y, rest are not
572                        relative */
573                     if (ctx->param == 5)
574                         val += ctx->cpx;
575                     else if (ctx->param == 6)
576                         val += ctx->cpy;
577                     break;
578                 case 'h':
579                     /* rule: x-relative */
580                     val += ctx->cpx;
581                     break;
582                 case 'v':
583                     /* rule: y-relative */
584                     val += ctx->cpy;
585                     break;
586                 }
587             }
588             ctx->params[ctx->param++] = val;
589             rsvg_parse_path_do_cmd(ctx);
590             comma_allowed = true;
591             require_number = false;
592             cur = end - 1;
593         } else {
594             /* Command. */
595             if (require_number || ctx->param) {
596                 goto error;
597             }
598             char next_cmd;
599             if (c <= 'Z') {
600                 next_cmd = c + ('a' - 'A');
601                 ctx->rel = false;
602             } else {
603                 next_cmd = c;
604                 ctx->rel = true;
605             }
607             comma_allowed = false;
608             require_number = true;
609             switch (next_cmd) {
610                 case 'z':
611                     require_number = false;
612                 case 'm':
613                 case 'l':
614                 case 'h':
615                 case 'v':
616                 case 'c':
617                 case 's':
618                 case 'q':
619                 case 't':
620                 case 'a':
621                     /* valid command */
622                     break;
624                 default:
625                     goto error;
626             }
628             if (ctx->cmd == 'z') {
629                 /* Closepath is the only command that allows no arguments. */
630                 rsvg_parse_path_do_closepath(ctx, next_cmd);
631             }
632             ctx->cmd = next_cmd;
633         }
634     }
636 done:
637     if (ctx->cmd == 'z') {
638         rsvg_parse_path_do_closepath(ctx, 0);
639     }
640     return;
642 error:
643     /* todo: set an error indicator. */
644     goto done;
648 NArtBpath *sp_svg_read_path(gchar const *str)
650     RSVGParsePathCtx ctx;
651     NArtBpath *bpath;
653     ctx.bpath = gnome_canvas_bpath_def_new ();
654     ctx.cpx = 0.0;
655     ctx.cpy = 0.0;
656     ctx.cmd = 0;
657     ctx.param = 0;
659     rsvg_parse_path_data (&ctx, str);
661     gnome_canvas_bpath_def_art_finish (ctx.bpath);
663     bpath = g_new (NArtBpath, ctx.bpath->n_bpath);
664     memcpy (bpath, ctx.bpath->bpath, ctx.bpath->n_bpath * sizeof (NArtBpath));
665     g_assert ((bpath + ctx.bpath->n_bpath - 1)->code == NR_END);
666     gnome_canvas_bpath_def_unref (ctx.bpath);
668     return bpath;
671 gchar *sp_svg_write_path(NArtBpath const *bpath)
673     bool closed=false;
674     
675     g_return_val_if_fail (bpath != NULL, NULL);
677     Inkscape::SVG::PathString str;
679     for (int i = 0; bpath[i].code != NR_END; i++){
680         switch (bpath [i].code){
681         case NR_LINETO:
682             if (!closed || bpath[i+1].code == NR_LINETO || bpath[i+1].code == NR_CURVETO) {
683                 str.lineTo(bpath[i].x3, bpath[i].y3);
684             }
685             break;
687         case NR_CURVETO:
688             str.curveTo(bpath[i].x1, bpath[i].y1,
689                         bpath[i].x2, bpath[i].y2,
690                         bpath[i].x3, bpath[i].y3);
691             break;
693         case NR_MOVETO_OPEN:
694         case NR_MOVETO:
695             if (closed) {
696                 str.closePath();
697             }
698             closed = ( bpath[i].code == NR_MOVETO );
699             str.moveTo(bpath[i].x3, bpath[i].y3);
700             break;
702         default:
703             g_assert_not_reached ();
704         }
705     }
706     if (closed) {
707         str.closePath();
708     }
710     return g_strdup(str.c_str());
713 /*
714   Local Variables:
715   mode:c++
716   c-file-style:"stroustrup"
717   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
718   indent-tabs-mode:nil
719   fill-column:99
720   End:
721 */
722 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :