Code

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