Code

Applying fixes for gcc 4.3 build issues (closes LP: #169115)
[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;
203 /* supply defaults for missing parameters, assuming relative coordinates
204    are to be interpreted as x,y */
205 static void rsvg_parse_path_default_xy(RSVGParsePathCtx *ctx, int n_params)
207     int i;
209     if (ctx->rel) {
210         for (i = ctx->param; i < n_params; i++) {
211             if (i > 2)
212                 ctx->params[i] = ctx->params[i - 2];
213             else if (i == 1)
214                 ctx->params[i] = ctx->cpy;
215             else if (i == 0)
216                 /* we shouldn't get here (usually ctx->param > 0 as
217                    precondition) */
218                 ctx->params[i] = ctx->cpx;
219         }
220     } else {
221         for (i = ctx->param; i < n_params; i++) {
222             ctx->params[i] = 0.0;
223         }
224     }
227 static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx, bool final)
229     double x1, y1, x2, y2, x3, y3;
231 #ifdef VERBOSE
232     int i;
234     g_print ("parse_path %c:", ctx->cmd);
235     for (i = 0; i < ctx->param; i++) {
236         g_print(" %f", ctx->params[i]);
237     }
238     g_print (final ? ".\n" : "\n");
239 #endif
241     switch (ctx->cmd) {
242     case 'm':
243         /* moveto */
244         if (ctx->param == 2 || final)
245         {
246             rsvg_parse_path_default_xy (ctx, 2);
247 #ifdef VERBOSE
248             g_print ("'m' moveto %g,%g\n",
249                      ctx->params[0], ctx->params[1]);
250 #endif
251             rsvg_bpath_def_moveto (ctx->bpath,
252                                    ctx->params[0], ctx->params[1]);
253             ctx->cpx = ctx->rpx = ctx->spx = ctx->params[0];
254             ctx->cpy = ctx->rpy = ctx->spy = ctx->params[1];
255             ctx->param = 0;
256             ctx->cmd = 'l';
257         }
258         break;
259     case 'l':
260         /* lineto */
261         if (ctx->param == 2 || final)
262         {
263             rsvg_parse_path_default_xy (ctx, 2);
264 #ifdef VERBOSE
265             g_print ("'l' lineto %g,%g\n",
266                      ctx->params[0], ctx->params[1]);
267 #endif
268             rsvg_bpath_def_lineto (ctx->bpath,
269                                    ctx->params[0], ctx->params[1]);
270             ctx->cpx = ctx->rpx = ctx->params[0];
271             ctx->cpy = ctx->rpy = ctx->params[1];
272             ctx->param = 0;
273         }
274         break;
275     case 'c':
276         /* curveto */
277         if (ctx->param == 6 || final )
278         {
279             rsvg_parse_path_default_xy (ctx, 6);
280             x1 = ctx->params[0];
281             y1 = ctx->params[1];
282             x2 = ctx->params[2];
283             y2 = ctx->params[3];
284             x3 = ctx->params[4];
285             y3 = ctx->params[5];
286 #ifdef VERBOSE
287             g_print ("'c' 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;
299     case 's':
300         /* smooth curveto */
301         if (ctx->param == 4 || final)
302         {
303             rsvg_parse_path_default_xy (ctx, 4);
304             x1 = 2 * ctx->cpx - ctx->rpx;
305             y1 = 2 * ctx->cpy - ctx->rpy;
306             x2 = ctx->params[0];
307             y2 = ctx->params[1];
308             x3 = ctx->params[2];
309             y3 = ctx->params[3];
310 #ifdef VERBOSE
311             g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
312                      x1, y1, x2, y2, x3, y3);
313 #endif
314             rsvg_bpath_def_curveto (ctx->bpath,
315                                     x1, y1, x2, y2, x3, y3);
316             ctx->rpx = x2;
317             ctx->rpy = y2;
318             ctx->cpx = x3;
319             ctx->cpy = y3;
320             ctx->param = 0;
321         }
322         break;
323     case 'h':
324         /* horizontal lineto */
325         if (ctx->param == 1) {
326 #ifdef VERBOSE
327             g_print ("'h' lineto %g,%g\n",
328                      ctx->params[0], ctx->cpy);
329 #endif
330             rsvg_bpath_def_lineto (ctx->bpath,
331                                    ctx->params[0], ctx->cpy);
332             ctx->cpx = ctx->rpx = ctx->params[0];
333             ctx->param = 0;
334         }
335         break;
336     case 'v':
337         /* vertical lineto */
338         if (ctx->param == 1) {
339 #ifdef VERBOSE
340             g_print ("'v' lineto %g,%g\n",
341                      ctx->cpx, ctx->params[0]);
342 #endif
343             rsvg_bpath_def_lineto (ctx->bpath,
344                                    ctx->cpx, ctx->params[0]);
345             ctx->cpy = ctx->rpy = ctx->params[0];
346             ctx->param = 0;
347         }
348         break;
349     case 'q':
350         /* quadratic bezier curveto */
352         /* non-normative reference:
353            http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
354         */
355         if (ctx->param == 4 || final)
356         {
357             rsvg_parse_path_default_xy (ctx, 4);
358             /* raise quadratic bezier to cubic */
359             x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
360             y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
361             x3 = ctx->params[2];
362             y3 = ctx->params[3];
363             x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
364             y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
365 #ifdef VERBOSE
366             g_print("'q' curveto %g,%g %g,%g, %g,%g\n",
367                     x1, y1, x2, y2, x3, y3);
368 #endif
369             rsvg_bpath_def_curveto(ctx->bpath,
370                                    x1, y1, x2, y2, x3, y3);
371             ctx->rpx = ctx->params[0];
372             ctx->rpy = ctx->params[1];
373             ctx->cpx = x3;
374             ctx->cpy = y3;
375             ctx->param = 0;
376         }
377         break;
378     case 't':
379         /* Truetype quadratic bezier curveto */
380         if (ctx->param == 2 || final) {
381             double xc, yc; /* quadratic control point */
383             xc = 2 * ctx->cpx - ctx->rpx;
384             yc = 2 * ctx->cpy - ctx->rpy;
385             /* generate a quadratic bezier with control point = xc, yc */
386             x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
387             y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
388             x3 = ctx->params[0];
389             y3 = ctx->params[1];
390             x2 = (x3 + 2 * xc) * (1.0 / 3.0);
391             y2 = (y3 + 2 * yc) * (1.0 / 3.0);
392 #ifdef VERBOSE
393             g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
394                      x1, y1, x2, y2, x3, y3);
395 #endif
396             rsvg_bpath_def_curveto (ctx->bpath,
397                                     x1, y1, x2, y2, x3, y3);
398             ctx->rpx = xc;
399             ctx->rpy = yc;
400             ctx->cpx = x3;
401             ctx->cpy = y3;
402             ctx->param = 0;
403         } else if (final) {
404             if (ctx->param > 2) {
405                 rsvg_parse_path_default_xy(ctx, 4);
406                 /* raise quadratic bezier to cubic */
407                 x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
408                 y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
409                 x3 = ctx->params[2];
410                 y3 = ctx->params[3];
411                 x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
412                 y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
413 #ifdef VERBOSE
414                 g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
415                          x1, y1, x2, y2, x3, y3);
416 #endif
417                 rsvg_bpath_def_curveto (ctx->bpath,
418                                         x1, y1, x2, y2, x3, y3);
419                 ctx->rpx = x2;
420                 ctx->rpy = y2;
421                 ctx->cpx = x3;
422                 ctx->cpy = y3;
423             } else {
424                 rsvg_parse_path_default_xy(ctx, 2);
425 #ifdef VERBOSE
426                 g_print ("'t' lineto %g,%g\n",
427                          ctx->params[0], ctx->params[1]);
428 #endif
429                 rsvg_bpath_def_lineto(ctx->bpath,
430                                       ctx->params[0], ctx->params[1]);
431                 ctx->cpx = ctx->rpx = ctx->params[0];
432                 ctx->cpy = ctx->rpy = ctx->params[1];
433             }
434             ctx->param = 0;
435         }
436         break;
437     case 'a':
438         if (ctx->param == 7 || final)
439         {
440             rsvg_path_arc(ctx,
441                           ctx->params[0], ctx->params[1], ctx->params[2],
442                           (int) ctx->params[3], (int) ctx->params[4],
443                           ctx->params[5], ctx->params[6]);
444             ctx->param = 0;
445         }
446         break;
447     default:
448         ctx->param = 0;
449     }
452 static void rsvg_parse_path_data(RSVGParsePathCtx *ctx, const char *data)
454     int i = 0;
455     double val = 0;
456     char c = 0;
457     bool in_num = false;
458     bool in_frac = false;
459     bool in_exp = false;
460     bool exp_wait_sign = false;
461     int sign = 0;
462     int exp = 0;
463     int exp_sign = 0;
464     double frac = 0.0;
466     /* fixme: Do better error processing: e.g. at least stop parsing as soon as we find an error.
467      * At some point we'll need to do all of
468      * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
469      */
470     for (i = 0; ; i++)
471     {
472         c = data[i];
473         if (c >= '0' && c <= '9')
474         {
475             /* digit */
476             if (in_num)
477             {
478                 if (in_exp)
479                 {
480                     exp = (exp * 10) + c - '0';
481                     exp_wait_sign = false;
482                 }
483                 else if (in_frac)
484                     val += (frac *= 0.1) * (c - '0');
485                 else
486                     val = (val * 10) + c - '0';
487             }
488             else
489             {
490                 in_num = true;
491                 assert(!in_frac && !in_exp);
492                 exp = 0;
493                 exp_sign = 1;
494                 exp_wait_sign = false;
495                 val = c - '0';
496                 sign = 1;
497             }
498         }
499         else if (c == '.' && !(in_frac || in_exp))
500         {
501             if (!in_num)
502             {
503                 in_num = true;
504                 assert(!in_exp);
505                 exp = 0;
506                 exp_sign = 1;
507                 exp_wait_sign = false;
508                 val = 0;
509                 sign = 1;
510             }
511             in_frac = true;
512             frac = 1;
513         }
514         else if ((c == 'E' || c == 'e') && in_num)
515         {
516             /* fixme: Should we add `&& !in_exp' to the above condition?
517              * It looks like the current code will parse `1e3e4' (as 1e4). */
518             in_exp = true;
519             exp_wait_sign = true;
520             exp = 0;
521             exp_sign = 1;
522         }
523         else if ((c == '+' || c == '-') && in_exp)
524         {
525             exp_sign = c == '+' ? 1 : -1;
526         }
527         else if (in_num)
528         {
529             /* end of number */
531             val *= sign * pow (10, exp_sign * exp);
532             if (ctx->rel)
533             {
534                 /* Handle relative coordinates. This switch statement attempts
535                    to determine _what_ the coords are relative to. This is
536                    underspecified in the 12 Apr working draft. */
537                 switch (ctx->cmd)
538                 {
539                 case 'l':
540                 case 'm':
541                 case 'c':
542                 case 's':
543                 case 'q':
544                 case 't':
545                     if ( ctx->param & 1 ) {
546                         val += ctx->cpy; /* odd param, y */
547                     } else {
548                         val += ctx->cpx; /* even param, x */
549                     }
550                     break;
551                 case 'a':
552                     /* rule: sixth and seventh are x and y, rest are not
553                        relative */
554                     if (ctx->param == 5)
555                         val += ctx->cpx;
556                     else if (ctx->param == 6)
557                         val += ctx->cpy;
558                     break;
559                 case 'h':
560                     /* rule: x-relative */
561                     val += ctx->cpx;
562                     break;
563                 case 'v':
564                     /* rule: y-relative */
565                     val += ctx->cpy;
566                     break;
567                 }
568             }
569             ctx->params[ctx->param++] = val;
570             rsvg_parse_path_do_cmd (ctx, false);
571             if (c=='.') {
572                 in_num = true;
573                 val = 0;
574                 in_frac = true;
575                 in_exp = false;
576                 frac = 1;
577             }
578             else {
579                 in_num = false;
580                 in_frac = false;
581                 in_exp = false;
582             }
583         }
585         if (c == '\0')
586             break;
587         else if ((c == '+' || c == '-') && !exp_wait_sign)
588         {
589             sign = c == '+' ? 1 : -1;;
590             val = 0;
591             in_num = true;
592             in_frac = false;
593             in_exp = false;
594             exp = 0;
595             exp_sign = 1;
596             exp_wait_sign = false;
597         }
598         else if (c == 'z' || c == 'Z')
599         {
600             if (ctx->param)
601                 rsvg_parse_path_do_cmd (ctx, true);
602             rsvg_bpath_def_closepath (ctx->bpath);
604             ctx->cmd = 'm';
605             ctx->params[0] = ctx->cpx = ctx->rpx = ctx->spx;
606             ctx->params[1] = ctx->cpy = ctx->rpy = ctx->spy;
607             ctx->param = 2;
608         }
609         else if (c >= 'A' && c <= 'Z' && c != 'E')
610         {
611             if (ctx->param)
612                 rsvg_parse_path_do_cmd (ctx, true);
613             ctx->cmd = c + 'a' - 'A';
614             ctx->rel = false;
615         }
616         else if (c >= 'a' && c <= 'z' && c != 'e')
617         {
618             if (ctx->param)
619                 rsvg_parse_path_do_cmd (ctx, true);
620             ctx->cmd = c;
621             ctx->rel = true;
622         }
623         /* else c _should_ be whitespace or , */
624     }
628 NArtBpath *sp_svg_read_path(gchar const *str)
630     RSVGParsePathCtx ctx;
631     NArtBpath *bpath;
633     ctx.bpath = gnome_canvas_bpath_def_new ();
634     ctx.cpx = 0.0;
635     ctx.cpy = 0.0;
636     ctx.cmd = 0;
637     ctx.param = 0;
639     rsvg_parse_path_data (&ctx, str);
641     if (ctx.param && ctx.cmd != 'm') {
642         rsvg_parse_path_do_cmd (&ctx, TRUE);
643     }
645     gnome_canvas_bpath_def_art_finish (ctx.bpath);
647     bpath = g_new (NArtBpath, ctx.bpath->n_bpath);
648     memcpy (bpath, ctx.bpath->bpath, ctx.bpath->n_bpath * sizeof (NArtBpath));
649     g_assert ((bpath + ctx.bpath->n_bpath - 1)->code == NR_END);
650     gnome_canvas_bpath_def_unref (ctx.bpath);
652     return bpath;
655 gchar *sp_svg_write_path(NArtBpath const *bpath)
657     Inkscape::SVGOStringStream os;
658     bool closed=false;
659     
660     g_return_val_if_fail (bpath != NULL, NULL);
662     Inkscape::SVG::PathString str;
664     for (int i = 0; bpath[i].code != NR_END; i++){
665         switch (bpath [i].code){
666         case NR_LINETO:
667             str.lineTo(bpath[i].x3, bpath[i].y3);
668             break;
670         case NR_CURVETO:
671             str.curveTo(bpath[i].x1, bpath[i].y1,
672                         bpath[i].x2, bpath[i].y2,
673                         bpath[i].x3, bpath[i].y3);
674             break;
676         case NR_MOVETO_OPEN:
677         case NR_MOVETO:
678             if (closed) {
679                 str.closePath();
680             }
681             closed = ( bpath[i].code == NR_MOVETO );
682             str.moveTo(bpath[i].x3, bpath[i].y3);
683             break;
685         default:
686             g_assert_not_reached ();
687         }
688     }
689     if (closed) {
690         str.closePath();
691     }
693     return g_strdup(str.c_str());
696 /*
697   Local Variables:
698   mode:c++
699   c-file-style:"stroustrup"
700   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
701   indent-tabs-mode:nil
702   fill-column:99
703   End:
704 */
705 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :