Code

glib/gtestutils.h is not available on gutsy, so switching to include of
[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 <cassert>
30 #include <glib/gmem.h>
31 #include <glib/gmessages.h>
32 #include <glib/gstrfuncs.h>
33 #include <glib.h> // g_assert()
35 #include "libnr/n-art-bpath.h"
36 #include "gnome-canvas-bpath-util.h"
37 #include "svg/path-string.h"
40 /* This module parses an SVG path element into an RsvgBpathDef.
42    At present, there is no support for <marker> or any other contextual
43    information from the SVG file. The API will need to change rather
44    significantly to support these.
46    Reference: SVG working draft 3 March 2000, section 8.
47 */
49 #ifndef M_PI
50 #define M_PI 3.14159265358979323846
51 #endif  /*  M_PI  */
53 /* We are lazy ;-) (Lauris) */
54 #define rsvg_bpath_def_new gnome_canvas_bpath_def_new
55 #define rsvg_bpath_def_moveto gnome_canvas_bpath_def_moveto
56 #define rsvg_bpath_def_lineto gnome_canvas_bpath_def_lineto
57 #define rsvg_bpath_def_curveto gnome_canvas_bpath_def_curveto
58 #define rsvg_bpath_def_closepath gnome_canvas_bpath_def_closepath
60 struct RSVGParsePathCtx {
61     GnomeCanvasBpathDef *bpath;
62     double cpx, cpy;  /* current point */
63     double rpx, rpy;  /* reflection point (for 's' and 't' commands) */
64     double spx, spy;  /* beginning of current subpath point */
65     char cmd;         /* current command (lowercase) */
66     int param;        /* parameter number */
67     bool rel;         /* true if relative coords */
68     double params[7]; /* parameters that have been parsed */
69 };
71 static void rsvg_path_arc_segment(RSVGParsePathCtx *ctx,
72               double xc, double yc,
73               double th0, double th1,
74               double rx, double ry, double x_axis_rotation)
75 {
76     double sin_th, cos_th;
77     double a00, a01, a10, a11;
78     double x1, y1, x2, y2, x3, y3;
79     double t;
80     double th_half;
82     sin_th = sin (x_axis_rotation * (M_PI / 180.0));
83     cos_th = cos (x_axis_rotation * (M_PI / 180.0)); 
84     /* inverse transform compared with rsvg_path_arc */
85     a00 = cos_th * rx;
86     a01 = -sin_th * ry;
87     a10 = sin_th * rx;
88     a11 = cos_th * ry;
90     th_half = 0.5 * (th1 - th0);
91     t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
92     x1 = xc + cos (th0) - t * sin (th0);
93     y1 = yc + sin (th0) + t * cos (th0);
94     x3 = xc + cos (th1);
95     y3 = yc + sin (th1);
96     x2 = x3 + t * sin (th1);
97     y2 = y3 - t * cos (th1);
98     rsvg_bpath_def_curveto(ctx->bpath,
99                            a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
100                            a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
101                            a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
104 /**
105  * rsvg_path_arc: Add an RSVG arc to the path context.
106  * @ctx: Path context.
107  * @rx: Radius in x direction (before rotation).
108  * @ry: Radius in y direction (before rotation).
109  * @x_axis_rotation: Rotation angle for axes.
110  * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
111  * @sweep: 0 for "negative angle", 1 for "positive angle".
112  * @x: New x coordinate.
113  * @y: New y coordinate.
114  *
115  **/
116 static void rsvg_path_arc (RSVGParsePathCtx *ctx,
117                            double rx, double ry, double x_axis_rotation,
118                            int large_arc_flag, int sweep_flag,
119                            double x, double y)
121     double sin_th, cos_th;
122     double a00, a01, a10, a11;
123     double x0, y0, x1, y1, xc, yc;
124     double d, sfactor, sfactor_sq;
125     double th0, th1, th_arc;
126     double px, py, pl;
127     int i, n_segs;
129     sin_th = sin (x_axis_rotation * (M_PI / 180.0));
130     cos_th = cos (x_axis_rotation * (M_PI / 180.0));
132     /*                                                                            
133                                                                                   Correction of out-of-range radii as described in Appendix F.6.6:           
135                                                                                   1. Ensure radii are non-zero (Done?).                                      
136                                                                                   2. Ensure that radii are positive.                                         
137                                                                                   3. Ensure that radii are large enough.                                     
138     */                                                                            
140     if(rx < 0.0) rx = -rx;                                                        
141     if(ry < 0.0) ry = -ry;                                                        
143     px = cos_th * (ctx->cpx - x) * 0.5 + sin_th * (ctx->cpy - y) * 0.5;           
144     py = cos_th * (ctx->cpy - y) * 0.5 - sin_th * (ctx->cpx - x) * 0.5;           
145     pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);                           
147     if(pl > 1.0)                                                                  
148     {                                                                             
149         pl  = sqrt(pl);                                                           
150         rx *= pl;                                                                 
151         ry *= pl;                                                                 
152     }                                                                             
154     /* Proceed with computations as described in Appendix F.6.5 */                
156     a00 = cos_th / rx;
157     a01 = sin_th / rx;
158     a10 = -sin_th / ry;
159     a11 = cos_th / ry;
160     x0 = a00 * ctx->cpx + a01 * ctx->cpy;
161     y0 = a10 * ctx->cpx + a11 * ctx->cpy;
162     x1 = a00 * x + a01 * y;
163     y1 = a10 * x + a11 * y;
164     /* (x0, y0) is current point in transformed coordinate space.
165        (x1, y1) is new point in transformed coordinate space.
167        The arc fits a unit-radius circle in this space.
168     */
169     d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
170     sfactor_sq = 1.0 / d - 0.25;
171     if (sfactor_sq < 0) sfactor_sq = 0;
172     sfactor = sqrt (sfactor_sq);
173     if (sweep_flag == large_arc_flag) sfactor = -sfactor;
174     xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
175     yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
176     /* (xc, yc) is center of the circle. */
178     th0 = atan2 (y0 - yc, x0 - xc);
179     th1 = atan2 (y1 - yc, x1 - xc);
181     th_arc = th1 - th0;
182     if (th_arc < 0 && sweep_flag)
183         th_arc += 2 * M_PI;
184     else if (th_arc > 0 && !sweep_flag)
185         th_arc -= 2 * M_PI;
187     n_segs = (int) ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
189     for (i = 0; i < n_segs; i++) {
190         rsvg_path_arc_segment(ctx, xc, yc,
191                               th0 + i * th_arc / n_segs,
192                               th0 + (i + 1) * th_arc / n_segs,
193                               rx, ry, x_axis_rotation);
194     }
196     ctx->cpx = x;
197     ctx->cpy = y;
201 /* supply defaults for missing parameters, assuming relative coordinates
202    are to be interpreted as x,y */
203 static void rsvg_parse_path_default_xy(RSVGParsePathCtx *ctx, int n_params)
205     int i;
207     if (ctx->rel) {
208         for (i = ctx->param; i < n_params; i++) {
209             if (i > 2)
210                 ctx->params[i] = ctx->params[i - 2];
211             else if (i == 1)
212                 ctx->params[i] = ctx->cpy;
213             else if (i == 0)
214                 /* we shouldn't get here (usually ctx->param > 0 as
215                    precondition) */
216                 ctx->params[i] = ctx->cpx;
217         }
218     } else {
219         for (i = ctx->param; i < n_params; i++) {
220             ctx->params[i] = 0.0;
221         }
222     }
225 static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx, bool final)
227     double x1, y1, x2, y2, x3, y3;
229 #ifdef VERBOSE
230     int i;
232     g_print ("parse_path %c:", ctx->cmd);
233     for (i = 0; i < ctx->param; i++) {
234         g_print(" %f", ctx->params[i]);
235     }
236     g_print (final ? ".\n" : "\n");
237 #endif
239     switch (ctx->cmd) {
240     case 'm':
241         /* moveto */
242         if (ctx->param == 2 || final)
243         {
244             rsvg_parse_path_default_xy (ctx, 2);
245 #ifdef VERBOSE
246             g_print ("'m' moveto %g,%g\n",
247                      ctx->params[0], ctx->params[1]);
248 #endif
249             rsvg_bpath_def_moveto (ctx->bpath,
250                                    ctx->params[0], ctx->params[1]);
251             ctx->cpx = ctx->rpx = ctx->spx = ctx->params[0];
252             ctx->cpy = ctx->rpy = ctx->spy = ctx->params[1];
253             ctx->param = 0;
254             ctx->cmd = 'l';
255         }
256         break;
257     case 'l':
258         /* lineto */
259         if (ctx->param == 2 || final)
260         {
261             rsvg_parse_path_default_xy (ctx, 2);
262 #ifdef VERBOSE
263             g_print ("'l' lineto %g,%g\n",
264                      ctx->params[0], ctx->params[1]);
265 #endif
266             rsvg_bpath_def_lineto (ctx->bpath,
267                                    ctx->params[0], ctx->params[1]);
268             ctx->cpx = ctx->rpx = ctx->params[0];
269             ctx->cpy = ctx->rpy = ctx->params[1];
270             ctx->param = 0;
271         }
272         break;
273     case 'c':
274         /* curveto */
275         if (ctx->param == 6 || final )
276         {
277             rsvg_parse_path_default_xy (ctx, 6);
278             x1 = ctx->params[0];
279             y1 = ctx->params[1];
280             x2 = ctx->params[2];
281             y2 = ctx->params[3];
282             x3 = ctx->params[4];
283             y3 = ctx->params[5];
284 #ifdef VERBOSE
285             g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
286                      x1, y1, x2, y2, x3, y3);
287 #endif
288             rsvg_bpath_def_curveto (ctx->bpath,
289                                     x1, y1, x2, y2, x3, y3);
290             ctx->rpx = x2;
291             ctx->rpy = y2;
292             ctx->cpx = x3;
293             ctx->cpy = y3;
294             ctx->param = 0;
295         }
296         break;
297     case 's':
298         /* smooth curveto */
299         if (ctx->param == 4 || final)
300         {
301             rsvg_parse_path_default_xy (ctx, 4);
302             x1 = 2 * ctx->cpx - ctx->rpx;
303             y1 = 2 * ctx->cpy - ctx->rpy;
304             x2 = ctx->params[0];
305             y2 = ctx->params[1];
306             x3 = ctx->params[2];
307             y3 = ctx->params[3];
308 #ifdef VERBOSE
309             g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
310                      x1, y1, x2, y2, x3, y3);
311 #endif
312             rsvg_bpath_def_curveto (ctx->bpath,
313                                     x1, y1, x2, y2, x3, y3);
314             ctx->rpx = x2;
315             ctx->rpy = y2;
316             ctx->cpx = x3;
317             ctx->cpy = y3;
318             ctx->param = 0;
319         }
320         break;
321     case 'h':
322         /* horizontal lineto */
323         if (ctx->param == 1) {
324 #ifdef VERBOSE
325             g_print ("'h' lineto %g,%g\n",
326                      ctx->params[0], ctx->cpy);
327 #endif
328             rsvg_bpath_def_lineto (ctx->bpath,
329                                    ctx->params[0], ctx->cpy);
330             ctx->cpx = ctx->rpx = ctx->params[0];
331             ctx->param = 0;
332         }
333         break;
334     case 'v':
335         /* vertical lineto */
336         if (ctx->param == 1) {
337 #ifdef VERBOSE
338             g_print ("'v' lineto %g,%g\n",
339                      ctx->cpx, ctx->params[0]);
340 #endif
341             rsvg_bpath_def_lineto (ctx->bpath,
342                                    ctx->cpx, ctx->params[0]);
343             ctx->cpy = ctx->rpy = ctx->params[0];
344             ctx->param = 0;
345         }
346         break;
347     case 'q':
348         /* quadratic bezier curveto */
350         /* non-normative reference:
351            http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
352         */
353         if (ctx->param == 4 || final)
354         {
355             rsvg_parse_path_default_xy (ctx, 4);
356             /* raise quadratic bezier to cubic */
357             x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
358             y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
359             x3 = ctx->params[2];
360             y3 = ctx->params[3];
361             x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
362             y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
363 #ifdef VERBOSE
364             g_print("'q' curveto %g,%g %g,%g, %g,%g\n",
365                     x1, y1, x2, y2, x3, y3);
366 #endif
367             rsvg_bpath_def_curveto(ctx->bpath,
368                                    x1, y1, x2, y2, x3, y3);
369             ctx->rpx = ctx->params[0];
370             ctx->rpy = ctx->params[1];
371             ctx->cpx = x3;
372             ctx->cpy = y3;
373             ctx->param = 0;
374         }
375         break;
376     case 't':
377         /* Truetype quadratic bezier curveto */
378         if (ctx->param == 2 || final) {
379             double xc, yc; /* quadratic control point */
381             xc = 2 * ctx->cpx - ctx->rpx;
382             yc = 2 * ctx->cpy - ctx->rpy;
383             /* generate a quadratic bezier with control point = xc, yc */
384             x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
385             y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
386             x3 = ctx->params[0];
387             y3 = ctx->params[1];
388             x2 = (x3 + 2 * xc) * (1.0 / 3.0);
389             y2 = (y3 + 2 * yc) * (1.0 / 3.0);
390 #ifdef VERBOSE
391             g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
392                      x1, y1, x2, y2, x3, y3);
393 #endif
394             rsvg_bpath_def_curveto (ctx->bpath,
395                                     x1, y1, x2, y2, x3, y3);
396             ctx->rpx = xc;
397             ctx->rpy = yc;
398             ctx->cpx = x3;
399             ctx->cpy = y3;
400             ctx->param = 0;
401         } else if (final) {
402             if (ctx->param > 2) {
403                 rsvg_parse_path_default_xy(ctx, 4);
404                 /* raise quadratic bezier to cubic */
405                 x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
406                 y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
407                 x3 = ctx->params[2];
408                 y3 = ctx->params[3];
409                 x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
410                 y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
411 #ifdef VERBOSE
412                 g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
413                          x1, y1, x2, y2, x3, y3);
414 #endif
415                 rsvg_bpath_def_curveto (ctx->bpath,
416                                         x1, y1, x2, y2, x3, y3);
417                 ctx->rpx = x2;
418                 ctx->rpy = y2;
419                 ctx->cpx = x3;
420                 ctx->cpy = y3;
421             } else {
422                 rsvg_parse_path_default_xy(ctx, 2);
423 #ifdef VERBOSE
424                 g_print ("'t' lineto %g,%g\n",
425                          ctx->params[0], ctx->params[1]);
426 #endif
427                 rsvg_bpath_def_lineto(ctx->bpath,
428                                       ctx->params[0], ctx->params[1]);
429                 ctx->cpx = ctx->rpx = ctx->params[0];
430                 ctx->cpy = ctx->rpy = ctx->params[1];
431             }
432             ctx->param = 0;
433         }
434         break;
435     case 'a':
436         if (ctx->param == 7 || final)
437         {
438             rsvg_path_arc(ctx,
439                           ctx->params[0], ctx->params[1], ctx->params[2],
440                           (int) ctx->params[3], (int) ctx->params[4],
441                           ctx->params[5], ctx->params[6]);
442             ctx->param = 0;
443         }
444         break;
445     default:
446         ctx->param = 0;
447     }
450 static void rsvg_parse_path_data(RSVGParsePathCtx *ctx, const char *data)
452     int i = 0;
453     double val = 0;
454     char c = 0;
455     bool in_num = false;
456     bool in_frac = false;
457     bool in_exp = false;
458     bool exp_wait_sign = false;
459     int sign = 0;
460     int exp = 0;
461     int exp_sign = 0;
462     double frac = 0.0;
464     /* fixme: Do better error processing: e.g. at least stop parsing as soon as we find an error.
465      * At some point we'll need to do all of
466      * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
467      */
468     for (i = 0; ; i++)
469     {
470         c = data[i];
471         if (c >= '0' && c <= '9')
472         {
473             /* digit */
474             if (in_num)
475             {
476                 if (in_exp)
477                 {
478                     exp = (exp * 10) + c - '0';
479                     exp_wait_sign = false;
480                 }
481                 else if (in_frac)
482                     val += (frac *= 0.1) * (c - '0');
483                 else
484                     val = (val * 10) + c - '0';
485             }
486             else
487             {
488                 in_num = true;
489                 assert(!in_frac && !in_exp);
490                 exp = 0;
491                 exp_sign = 1;
492                 exp_wait_sign = false;
493                 val = c - '0';
494                 sign = 1;
495             }
496         }
497         else if (c == '.' && !(in_frac || in_exp))
498         {
499             if (!in_num)
500             {
501                 in_num = true;
502                 assert(!in_exp);
503                 exp = 0;
504                 exp_sign = 1;
505                 exp_wait_sign = false;
506                 val = 0;
507                 sign = 1;
508             }
509             in_frac = true;
510             frac = 1;
511         }
512         else if ((c == 'E' || c == 'e') && in_num)
513         {
514             /* fixme: Should we add `&& !in_exp' to the above condition?
515              * It looks like the current code will parse `1e3e4' (as 1e4). */
516             in_exp = true;
517             exp_wait_sign = true;
518             exp = 0;
519             exp_sign = 1;
520         }
521         else if ((c == '+' || c == '-') && in_exp)
522         {
523             exp_sign = c == '+' ? 1 : -1;
524         }
525         else if (in_num)
526         {
527             /* end of number */
529             val *= sign * pow (10, exp_sign * exp);
530             if (ctx->rel)
531             {
532                 /* Handle relative coordinates. This switch statement attempts
533                    to determine _what_ the coords are relative to. This is
534                    underspecified in the 12 Apr working draft. */
535                 switch (ctx->cmd)
536                 {
537                 case 'l':
538                 case 'm':
539                 case 'c':
540                 case 's':
541                 case 'q':
542                 case 't':
543                     if ( ctx->param & 1 ) {
544                         val += ctx->cpy; /* odd param, y */
545                     } else {
546                         val += ctx->cpx; /* even param, x */
547                     }
548                     break;
549                 case 'a':
550                     /* rule: sixth and seventh are x and y, rest are not
551                        relative */
552                     if (ctx->param == 5)
553                         val += ctx->cpx;
554                     else if (ctx->param == 6)
555                         val += ctx->cpy;
556                     break;
557                 case 'h':
558                     /* rule: x-relative */
559                     val += ctx->cpx;
560                     break;
561                 case 'v':
562                     /* rule: y-relative */
563                     val += ctx->cpy;
564                     break;
565                 }
566             }
567             ctx->params[ctx->param++] = val;
568             rsvg_parse_path_do_cmd (ctx, false);
569             if (c=='.') {
570                 in_num = true;
571                 val = 0;
572                 in_frac = true;
573                 in_exp = false;
574                 frac = 1;
575             }
576             else {
577                 in_num = false;
578                 in_frac = false;
579                 in_exp = false;
580             }
581         }
583         if (c == '\0')
584             break;
585         else if ((c == '+' || c == '-') && !exp_wait_sign)
586         {
587             sign = c == '+' ? 1 : -1;;
588             val = 0;
589             in_num = true;
590             in_frac = false;
591             in_exp = false;
592             exp = 0;
593             exp_sign = 1;
594             exp_wait_sign = false;
595         }
596         else if (c == 'z' || c == 'Z')
597         {
598             if (ctx->param)
599                 rsvg_parse_path_do_cmd (ctx, true);
600             rsvg_bpath_def_closepath (ctx->bpath);
602             ctx->cmd = 'm';
603             ctx->params[0] = ctx->cpx = ctx->rpx = ctx->spx;
604             ctx->params[1] = ctx->cpy = ctx->rpy = ctx->spy;
605             ctx->param = 2;
606         }
607         else if (c >= 'A' && c <= 'Z' && c != 'E')
608         {
609             if (ctx->param)
610                 rsvg_parse_path_do_cmd (ctx, true);
611             ctx->cmd = c + 'a' - 'A';
612             ctx->rel = false;
613         }
614         else if (c >= 'a' && c <= 'z' && c != 'e')
615         {
616             if (ctx->param)
617                 rsvg_parse_path_do_cmd (ctx, true);
618             ctx->cmd = c;
619             ctx->rel = true;
620         }
621         /* else c _should_ be whitespace or , */
622     }
626 NArtBpath *sp_svg_read_path(gchar const *str)
628     RSVGParsePathCtx ctx;
629     NArtBpath *bpath;
631     ctx.bpath = gnome_canvas_bpath_def_new ();
632     ctx.cpx = 0.0;
633     ctx.cpy = 0.0;
634     ctx.cmd = 0;
635     ctx.param = 0;
637     rsvg_parse_path_data (&ctx, str);
639     if (ctx.param && ctx.cmd != 'm') {
640         rsvg_parse_path_do_cmd (&ctx, TRUE);
641     }
643     gnome_canvas_bpath_def_art_finish (ctx.bpath);
645     bpath = g_new (NArtBpath, ctx.bpath->n_bpath);
646     memcpy (bpath, ctx.bpath->bpath, ctx.bpath->n_bpath * sizeof (NArtBpath));
647     g_assert ((bpath + ctx.bpath->n_bpath - 1)->code == NR_END);
648     gnome_canvas_bpath_def_unref (ctx.bpath);
650     return bpath;
653 gchar *sp_svg_write_path(NArtBpath const *bpath)
655     Inkscape::SVGOStringStream os;
656     bool closed=false;
657     
658     g_return_val_if_fail (bpath != NULL, NULL);
660     Inkscape::SVG::PathString str;
662     for (int i = 0; bpath[i].code != NR_END; i++){
663         switch (bpath [i].code){
664         case NR_LINETO:
665             str.lineTo(bpath[i].x3, bpath[i].y3);
666             break;
668         case NR_CURVETO:
669             str.curveTo(bpath[i].x1, bpath[i].y1,
670                         bpath[i].x2, bpath[i].y2,
671                         bpath[i].x3, bpath[i].y3);
672             break;
674         case NR_MOVETO_OPEN:
675         case NR_MOVETO:
676             if (closed) {
677                 str.closePath();
678             }
679             closed = ( bpath[i].code == NR_MOVETO );
680             str.moveTo(bpath[i].x3, bpath[i].y3);
681             break;
683         default:
684             g_assert_not_reached ();
685         }
686     }
687     if (closed) {
688         str.closePath();
689     }
691     return g_strdup(str.c_str());
694 /*
695   Local Variables:
696   mode:c++
697   c-file-style:"stroustrup"
698   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
699   indent-tabs-mode:nil
700   fill-column:99
701   End:
702 */
703 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :