bd0d4a4f5ff1dbb7c7af872f8ff6779a4ec20dba
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);
104 }
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)
122 {
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;
200 }
202 static void rsvg_parse_path_do_cmd(RSVGParsePathCtx *ctx)
203 {
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 }
390 }
392 static void rsvg_parse_path_do_closepath(RSVGParsePathCtx *const ctx, const char next_cmd)
393 {
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 }
418 }
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;
428 }
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;
439 }
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;
447 }
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;
479 }
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;
487 }
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;
645 }
648 NArtBpath *sp_svg_read_path(gchar const *str)
649 {
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;
669 }
671 gchar *sp_svg_write_path(NArtBpath const *bpath)
672 {
673 bool closed=false;
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());
711 }
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 :