UML class fix bigtime.
[dia.git] / lib / dia_svg.c
blobcbf9b133e29626aa0f97cac640b7eb74597ebd57
1 /* dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * dia_svg.c -- Refactoring by Hans Breuer from :
6 * Custom Objects -- objects defined in XML rather than C.
7 * Copyright (C) 1999 James Henstridge.
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (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
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "config.h"
26 #include <string.h>
27 #include <stdlib.h>
29 #include <pango/pango-attributes.h>
31 #include "dia_svg.h"
33 void
34 dia_svg_style_init (DiaSvgStyle *gs, DiaSvgStyle *parent_style)
36 g_return_if_fail (gs);
37 gs->stroke = parent_style ? parent_style->stroke : (-1);
38 gs->line_width = parent_style ? parent_style->line_width : 0.0;
39 gs->linestyle = parent_style ? parent_style->linestyle : LINESTYLE_SOLID;
40 gs->dashlength = parent_style ? parent_style->dashlength : 1;
41 gs->fill = parent_style ? parent_style->fill : (-1);
42 gs->font = (parent_style && parent_style->font) ? dia_font_ref(parent_style->font) : NULL;
43 gs->font_height = parent_style ? parent_style->font_height : 0.8;
44 gs->alignment = parent_style ? parent_style->alignment : ALIGN_LEFT;
47 void
48 dia_svg_style_copy (DiaSvgStyle *dest, DiaSvgStyle *src)
50 g_return_if_fail (dest && src);
52 dest->stroke = src->stroke;
53 dest->line_width = src->line_width;
54 dest->linestyle = src->linestyle;
55 dest->dashlength = src->dashlength;
56 dest->fill = src->fill;
57 if (dest->font)
58 dia_font_unref (dest->font);
59 dest->font = src->font ? dia_font_ref(src->font) : NULL;
60 dest->font_height = src->font_height;
61 dest->alignment = src->alignment;
64 static gboolean
65 _parse_color (gint32 *color, const char *str)
67 if (str[0] == '#')
68 *color = strtol(str+1, NULL, 16) & 0xffffff;
69 else if (0 == strncmp(str, "none", 4))
70 *color = DIA_SVG_COLOUR_NONE;
71 else if (0 == strncmp(str, "foreground", 10) || 0 == strncmp(str, "fg", 2) ||
72 0 == strncmp(str, "inverse", 7))
73 *color = DIA_SVG_COLOUR_FOREGROUND;
74 else if (0 == strncmp(str, "background", 10) || 0 == strncmp(str, "bg", 2) ||
75 0 == strncmp(str, "default", 7))
76 *color = DIA_SVG_COLOUR_BACKGROUND;
77 else if (0 == strcmp(str, "text"))
78 *color = DIA_SVG_COLOUR_TEXT;
79 else if (0 == strncmp(str, "rgb(", 4)) {
80 int r = 0, g = 0, b = 0;
81 if (3 == sscanf (str+4, "%d,%d,%d", &r, &g, &b))
82 *color = ((r<<16) & 0xFF0000) | ((g<<8) & 0xFF00) | (b & 0xFF);
83 else
84 return FALSE;
85 } else {
86 /* Pango needs null terminated strings, so we just use it as a fallback */
87 PangoColor pc;
88 char* se = strchr (str, ';');
90 if (!se) {
91 if (pango_color_parse (&pc, str))
92 *color = ((pc.red >> 8) << 16) | ((pc.green >> 8) << 8) | (pc.blue >> 8);
93 else
94 return FALSE;
95 } else {
96 gchar* sz = g_strndup (str, se - str);
97 gboolean ret = pango_color_parse (&pc, str);
99 if (ret)
100 *color = ((pc.red >> 8) << 16) | ((pc.green >> 8) << 8) | (pc.blue >> 8);
101 g_free (sz);
102 return ret;
105 return TRUE;
108 enum
110 FONT_NAME_LENGTH_MAX = 40
114 * This function not only parses the style attribute of the given node
115 * it also extracts some of the style properties directly.
117 void
118 dia_svg_parse_style(xmlNodePtr node, DiaSvgStyle *s)
120 xmlChar *str;
121 gchar temp[FONT_NAME_LENGTH_MAX+1]; /* font-family names will be limited to 40 characters */
122 int i = 0;
123 gboolean over = FALSE;
124 char *family = NULL, *style = NULL, *weight = NULL;
126 str = xmlGetProp(node, "style");
128 if (str) {
129 gchar *ptr = (gchar *)str;
130 while (ptr[0] != '\0') {
131 /* skip white space at start */
132 while (ptr[0] != '\0' && g_ascii_isspace(ptr[0])) ptr++;
133 if (ptr[0] == '\0') break;
135 if (!strncmp("font-family:", ptr, 12)) {
136 ptr += 12;
137 while ((ptr[0] != '\0') && g_ascii_isspace(ptr[0])) ptr++;
138 i = 0; over = FALSE;
139 while (ptr[0] != '\0' && ptr[0] != ';' && !over) {
140 if (i < FONT_NAME_LENGTH_MAX) {
141 temp[i] = ptr[0];
142 } else over = TRUE;
143 i++;
144 ptr++;
146 temp[i] = '\0';
148 if (!over) family = g_strdup(temp);
149 } else if (!strncmp("font-weight:", ptr, 12)) {
150 ptr += 12;
151 while ((ptr[0] != '\0') && g_ascii_isspace(ptr[0])) ptr++;
152 i = 0; over = FALSE;
153 while (ptr[0] != '\0' && ptr[0] != ';' && !over) {
154 if (i < FONT_NAME_LENGTH_MAX) {
155 temp[i] = ptr[0];
156 } else over = TRUE;
157 i++;
158 ptr++;
160 temp[i] = '\0';
162 if (!over) weight = g_strdup(temp);
163 } else if (!strncmp("font-style:", ptr, 11)) {
164 ptr += 11;
165 while ((ptr[0] != '\0') && g_ascii_isspace(ptr[0])) ptr++;
166 i = 0; over = FALSE;
167 while (ptr[0] != '\0' && ptr[0] != ';' && !over) {
168 if (i < FONT_NAME_LENGTH_MAX) {
169 temp[i] = ptr[0];
170 } else over = TRUE;
171 i++;
172 ptr++;
174 temp[i] = '\0';
176 if (!over) style = g_strdup(temp);
177 } else if (!strncmp("font-size:", ptr, 10)) {
178 ptr += 10;
179 while ((ptr[0] != '\0') && g_ascii_isspace(ptr[0])) ptr++;
180 i = 0; over = FALSE;
181 while (ptr[0] != '\0' && ptr[0] != ';' && !over) {
182 if (i < FONT_NAME_LENGTH_MAX) {
183 temp[i] = ptr[0];
184 } else over = TRUE;
185 i++;
186 ptr++;
188 temp[i] = '\0';
190 if (!over) {
191 s->font_height = g_ascii_strtod(temp, NULL);
193 } else if (!strncmp("text-anchor:", ptr, 12)) {
194 ptr += 12;
195 while ((ptr[0] != '\0') && g_ascii_isspace(ptr[0])) ptr++;
196 if (!strncmp(ptr, "start", 5))
197 s->alignment = ALIGN_LEFT;
198 else if (!strncmp(ptr, "end", 3))
199 s->alignment = ALIGN_RIGHT;
200 else if (!strncmp(ptr, "middle", 6))
201 s->alignment = ALIGN_CENTER;
203 } else if (!strncmp("stroke-width:", ptr, 13)) {
204 ptr += 13;
205 s->line_width = g_ascii_strtod(ptr, &ptr);
206 } else if (!strncmp("stroke:", ptr, 7)) {
207 ptr += 7;
208 while ((ptr[0] != '\0') && g_ascii_isspace(ptr[0])) ptr++;
209 if (ptr[0] == '\0') break;
211 _parse_color (&s->stroke, ptr);
212 } else if (!strncmp("fill:", ptr, 5)) {
213 ptr += 5;
214 while (ptr[0] != '\0' && g_ascii_isspace(ptr[0])) ptr++;
215 if (ptr[0] == '\0') break;
217 _parse_color (&s->fill, ptr);
218 } else if (!strncmp("stroke-linecap:", ptr, 15)) {
219 ptr += 15;
220 while (ptr[0] != '\0' && g_ascii_isspace(ptr[0])) ptr++;
221 if (ptr[0] == '\0') break;
223 if (!strncmp(ptr, "butt", 4))
224 s->linecap = LINECAPS_BUTT;
225 else if (!strncmp(ptr, "round", 5))
226 s->linecap = LINECAPS_ROUND;
227 else if (!strncmp(ptr, "square", 6) || !strncmp(ptr, "projecting", 10))
228 s->linecap = LINECAPS_PROJECTING;
229 else if (!strncmp(ptr, "default", 7))
230 s->linecap = DIA_SVG_LINECAPS_DEFAULT;
231 } else if (!strncmp("stroke-linejoin:", ptr, 16)) {
232 ptr += 16;
233 while (ptr[0] != '\0' && g_ascii_isspace(ptr[0])) ptr++;
234 if (ptr[0] == '\0') break;
236 if (!strncmp(ptr, "miter", 5))
237 s->linejoin = LINEJOIN_MITER;
238 else if (!strncmp(ptr, "round", 5))
239 s->linejoin = LINEJOIN_ROUND;
240 else if (!strncmp(ptr, "bevel", 5))
241 s->linejoin = LINEJOIN_BEVEL;
242 else if (!strncmp(ptr, "default", 7))
243 s->linejoin = DIA_SVG_LINEJOIN_DEFAULT;
244 } else if (!strncmp("stroke-pattern:", ptr, 15)) {
245 ptr += 15;
246 while (ptr[0] != '\0' && g_ascii_isspace(ptr[0])) ptr++;
247 if (ptr[0] == '\0') break;
249 if (!strncmp(ptr, "solid", 5))
250 s->linestyle = LINESTYLE_SOLID;
251 else if (!strncmp(ptr, "dashed", 6))
252 s->linestyle = LINESTYLE_DASHED;
253 else if (!strncmp(ptr, "dash-dot", 8))
254 s->linestyle = LINESTYLE_DASH_DOT;
255 else if (!strncmp(ptr, "dash-dot-dot", 12))
256 s->linestyle = LINESTYLE_DASH_DOT_DOT;
257 else if (!strncmp(ptr, "dotted", 6))
258 s->linestyle = LINESTYLE_DOTTED;
259 else if (!strncmp(ptr, "default", 7))
260 s->linestyle = DIA_SVG_LINESTYLE_DEFAULT;
261 /* XXX: deal with a real pattern */
262 } else if (!strncmp("stroke-dashlength:", ptr, 18)) {
263 ptr += 18;
264 while (ptr[0] != '\0' && g_ascii_isspace(ptr[0])) ptr++;
265 if (ptr[0] == '\0') break;
267 if (!strncmp(ptr, "default", 7))
268 s->dashlength = 1.0;
269 else {
270 s->dashlength = g_ascii_strtod(ptr, &ptr);
272 } else if (!strncmp("stroke-dasharray:", ptr, 17)) {
273 /* FIXME? do we need to read an array here (not clear from
274 * Dia's usage); do we need to set the linestyle depending
275 * on the array's size ? --hb
277 s->linestyle = LINESTYLE_DASHED;
278 ptr += 17;
279 while (ptr[0] != '\0' && g_ascii_isspace(ptr[0])) ptr++;
280 if (ptr[0] == '\0') break;
282 if (!strncmp(ptr, "default", 7))
283 s->dashlength = 1.0;
284 else {
285 s->dashlength = g_ascii_strtod(ptr, &ptr);
289 /* skip up to the next attribute */
290 while (ptr[0] != '\0' && ptr[0] != ';' && ptr[0] != '\n') ptr++;
291 if (ptr[0] != '\0') ptr++;
293 xmlFree(str);
296 /* ugly svg variations, it is allowed to give style properties without
297 * the style attribute, i.e. direct attributes
299 str = xmlGetProp(node, "fill");
300 if (str) {
301 _parse_color (&s->fill, str);
302 xmlFree(str);
304 str = xmlGetProp(node, "stroke");
305 if (str) {
306 _parse_color (&s->stroke, str);
307 xmlFree(str);
309 str = xmlGetProp(node, "stroke-width");
310 if (str) {
311 s->line_width = g_ascii_strtod(str, NULL);
312 xmlFree(str);
315 if (family || style || weight) {
316 if (s->font)
317 dia_font_unref (s->font);
318 s->font = dia_font_new_from_style(DIA_FONT_SANS,s->font_height/*bogus*/);
319 if (family) {
320 dia_font_set_any_family(s->font,family);
321 g_free(family);
323 if (style) {
324 dia_font_set_slant_from_string(s->font,style);
325 g_free(style);
327 if (weight) {
328 dia_font_set_weight_from_string(s->font,weight);
329 g_free(weight);
335 * Code stolen from (and adapted)
336 * http://www.inkscape.org/doc/doxygen/html/svg-path_8cpp.php#a7
337 * which may have got it from rsvg, hop it is correct ;)
339 static void
340 _path_arc_segment (GArray* points,
341 real xc, real yc,
342 real th0, real th1,
343 real rx, real ry,
344 real x_axis_rotation,
345 Point *last_p2)
347 BezPoint bez;
348 real sin_th, cos_th;
349 real a00, a01, a10, a11;
350 real x1, y1, x2, y2, x3, y3;
351 real t;
352 real th_half;
354 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
355 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
356 /* inverse transform compared with rsvg_path_arc */
357 a00 = cos_th * rx;
358 a01 = -sin_th * ry;
359 a10 = sin_th * rx;
360 a11 = cos_th * ry;
362 th_half = 0.5 * (th1 - th0);
363 t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half);
364 x1 = xc + cos (th0) - t * sin (th0);
365 y1 = yc + sin (th0) + t * cos (th0);
366 x3 = xc + cos (th1);
367 y3 = yc + sin (th1);
368 x2 = x3 + t * sin (th1);
369 y2 = y3 - t * cos (th1);
371 bez.type = BEZ_CURVE_TO;
372 bez.p1.x = a00 * x1 + a01 * y1;
373 bez.p1.y = a10 * x1 + a11 * y1;
374 bez.p2.x = a00 * x2 + a01 * y2;
375 bez.p2.y = a10 * x2 + a11 * y2;
376 bez.p3.x = a00 * x3 + a01 * y3;
377 bez.p3.y = a10 * x3 + a11 * y3;
379 *last_p2 = bez.p2;
381 g_array_append_val(points, bez);
384 static void
385 _path_arc (GArray *points, double cpx, double cpy,
386 double rx, double ry, double x_axis_rotation,
387 int large_arc_flag, int sweep_flag,
388 double x, double y,
389 Point *last_p2)
391 double sin_th, cos_th;
392 double a00, a01, a10, a11;
393 double x0, y0, x1, y1, xc, yc;
394 double d, sfactor, sfactor_sq;
395 double th0, th1, th_arc;
396 double px, py, pl;
397 int i, n_segs;
399 sin_th = sin (x_axis_rotation * (M_PI / 180.0));
400 cos_th = cos (x_axis_rotation * (M_PI / 180.0));
403 * Correction of out-of-range radii as described in Appendix F.6.6:
405 * 1. Ensure radii are non-zero (Done?).
406 * 2. Ensure that radii are positive.
407 * 3. Ensure that radii are large enough.
410 if(rx < 0.0) rx = -rx;
411 if(ry < 0.0) ry = -ry;
413 px = cos_th * (cpx - x) * 0.5 + sin_th * (cpy - y) * 0.5;
414 py = cos_th * (cpy - y) * 0.5 - sin_th * (cpx - x) * 0.5;
415 pl = (px * px) / (rx * rx) + (py * py) / (ry * ry);
417 if(pl > 1.0)
419 pl = sqrt(pl);
420 rx *= pl;
421 ry *= pl;
424 /* Proceed with computations as described in Appendix F.6.5 */
426 a00 = cos_th / rx;
427 a01 = sin_th / rx;
428 a10 = -sin_th / ry;
429 a11 = cos_th / ry;
430 x0 = a00 * cpx + a01 * cpy;
431 y0 = a10 * cpx + a11 * cpy;
432 x1 = a00 * x + a01 * y;
433 y1 = a10 * x + a11 * y;
434 /* (x0, y0) is current point in transformed coordinate space.
435 (x1, y1) is new point in transformed coordinate space.
437 The arc fits a unit-radius circle in this space.
439 d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
440 sfactor_sq = 1.0 / d - 0.25;
441 if (sfactor_sq < 0) sfactor_sq = 0;
442 sfactor = sqrt (sfactor_sq);
443 if (sweep_flag == large_arc_flag) sfactor = -sfactor;
444 xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
445 yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
446 /* (xc, yc) is center of the circle. */
448 th0 = atan2 (y0 - yc, x0 - xc);
449 th1 = atan2 (y1 - yc, x1 - xc);
451 th_arc = th1 - th0;
452 if (th_arc < 0 && sweep_flag)
453 th_arc += 2 * M_PI;
454 else if (th_arc > 0 && !sweep_flag)
455 th_arc -= 2 * M_PI;
457 n_segs = (int) ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
459 for (i = 0; i < n_segs; i++) {
460 _path_arc_segment(points, xc, yc,
461 th0 + i * th_arc / n_segs,
462 th0 + (i + 1) * th_arc / n_segs,
463 rx, ry, x_axis_rotation,
464 last_p2);
468 /* routine to chomp off the start of the string */
469 #define path_chomp(path) while (path[0]!='\0'&&strchr(" \t\n\r,", path[0])) path++
472 * Takes SVG path content and converts it in an array of BezPoint.
473 * The caller is responsible to free/consume the returned array.
474 * Returns NULL on error.
476 * SVG pathes can contain multiple MOVE_TO commands while Dia's bezier
477 * object can only contain one so you may need to call this function
478 * multiple times.
480 GArray*
481 dia_svg_parse_path(const gchar *path_str, gchar **unparsed, gboolean *closed)
483 enum {
484 PATH_MOVE, PATH_LINE, PATH_HLINE, PATH_VLINE, PATH_CURVE,
485 PATH_SMOOTHCURVE, PATH_ARC, PATH_CLOSE } last_type = PATH_MOVE;
486 Point last_open = {0.0, 0.0};
487 Point last_point = {0.0, 0.0};
488 Point last_control = {0.0, 0.0};
489 gboolean last_relative = FALSE;
490 GArray *points;
491 BezPoint bez;
492 gchar *path = (gchar *)path_str;
493 gboolean need_next_element = FALSE;
495 *closed = FALSE;
496 *unparsed = NULL;
498 points = g_array_new(FALSE, FALSE, sizeof(BezPoint));
499 g_array_set_size(points, 0);
501 path_chomp(path);
502 while (path[0] != '\0') {
503 #ifdef DEBUG_CUSTOM
504 g_print("Path: %s\n", path);
505 #endif
506 /* check for a new command */
507 switch (path[0]) {
508 case 'M':
509 if (points->len > 0) {
510 need_next_element = TRUE;
511 goto MORETOPARSE;
513 path++;
514 path_chomp(path);
515 last_type = PATH_MOVE;
516 last_relative = FALSE;
517 break;
518 case 'm':
519 if (points->len > 0)
520 goto MORETOPARSE;
521 path++;
522 path_chomp(path);
523 last_type = PATH_MOVE;
524 last_relative = TRUE;
525 break;
526 case 'L':
527 path++;
528 path_chomp(path);
529 last_type = PATH_LINE;
530 last_relative = FALSE;
531 break;
532 case 'l':
533 path++;
534 path_chomp(path);
535 last_type = PATH_LINE;
536 last_relative = TRUE;
537 break;
538 case 'H':
539 path++;
540 path_chomp(path);
541 last_type = PATH_HLINE;
542 last_relative = FALSE;
543 break;
544 case 'h':
545 path++;
546 path_chomp(path);
547 last_type = PATH_HLINE;
548 last_relative = TRUE;
549 break;
550 case 'V':
551 path++;
552 path_chomp(path);
553 last_type = PATH_VLINE;
554 last_relative = FALSE;
555 break;
556 case 'v':
557 path++;
558 path_chomp(path);
559 last_type = PATH_VLINE;
560 last_relative = TRUE;
561 break;
562 case 'C':
563 path++;
564 path_chomp(path);
565 last_type = PATH_CURVE;
566 last_relative = FALSE;
567 break;
568 case 'c':
569 path++;
570 path_chomp(path);
571 last_type = PATH_CURVE;
572 last_relative = TRUE;
573 break;
574 case 'S':
575 path++;
576 path_chomp(path);
577 last_type = PATH_SMOOTHCURVE;
578 last_relative = FALSE;
579 break;
580 case 's':
581 path++;
582 path_chomp(path);
583 last_type = PATH_SMOOTHCURVE;
584 last_relative = TRUE;
585 break;
586 case 'Z':
587 case 'z':
588 path++;
589 path_chomp(path);
590 last_type = PATH_CLOSE;
591 last_relative = FALSE;
592 break;
593 case 'A':
594 path++;
595 path_chomp(path);
596 last_type = PATH_ARC;
597 last_relative = FALSE;
598 break;
599 case 'a':
600 path++;
601 path_chomp(path);
602 last_type = PATH_ARC;
603 last_relative = TRUE;
604 break;
605 case '0':
606 case '1':
607 case '2':
608 case '3':
609 case '4':
610 case '5':
611 case '6':
612 case '7':
613 case '8':
614 case '9':
615 case '.':
616 case '+':
617 case '-':
618 if (last_type == PATH_CLOSE) {
619 g_warning("parse_path: argument given for implicite close path");
620 /* consume one number so we don't fall into an infinite loop */
621 while (path != '\0' && strchr("0123456789.+-", path[0])) path++;
622 path_chomp(path);
623 *closed = TRUE;
624 need_next_element = TRUE;
625 goto MORETOPARSE;
627 break;
628 default:
629 g_warning("unsupported path code '%c'", path[0]);
630 path++;
631 path_chomp(path);
632 break;
635 /* actually parse the path component */
636 switch (last_type) {
637 case PATH_MOVE:
638 bez.type = BEZ_MOVE_TO;
639 bez.p1.x = g_ascii_strtod(path, &path);
640 path_chomp(path);
641 bez.p1.y = g_ascii_strtod(path, &path);
642 path_chomp(path);
643 if (last_relative) {
644 bez.p1.x += last_point.x;
645 bez.p1.y += last_point.y;
647 last_point = bez.p1;
648 last_control = bez.p1;
649 last_open = bez.p1;
650 g_array_append_val(points, bez);
651 break;
652 case PATH_LINE:
653 bez.type = BEZ_LINE_TO;
654 bez.p1.x = g_ascii_strtod(path, &path);
655 path_chomp(path);
656 bez.p1.y = g_ascii_strtod(path, &path);
657 path_chomp(path);
658 if (last_relative) {
659 bez.p1.x += last_point.x;
660 bez.p1.y += last_point.y;
662 last_point = bez.p1;
663 last_control = bez.p1;
665 g_array_append_val(points, bez);
666 break;
667 case PATH_HLINE:
668 bez.type = BEZ_LINE_TO;
669 bez.p1.x = g_ascii_strtod(path, &path);
670 path_chomp(path);
671 bez.p1.y = last_point.y;
672 if (last_relative)
673 bez.p1.x += last_point.x;
674 last_point = bez.p1;
675 last_control = bez.p1;
677 g_array_append_val(points, bez);
678 break;
679 case PATH_VLINE:
680 bez.type = BEZ_LINE_TO;
681 bez.p1.x = last_point.x;
682 bez.p1.y = g_ascii_strtod(path, &path);
683 path_chomp(path);
684 if (last_relative)
685 bez.p1.y += last_point.y;
686 last_point = bez.p1;
687 last_control = bez.p1;
689 g_array_append_val(points, bez);
690 break;
691 case PATH_CURVE:
692 bez.type = BEZ_CURVE_TO;
693 bez.p1.x = g_ascii_strtod(path, &path);
694 path_chomp(path);
695 bez.p1.y = g_ascii_strtod(path, &path);
696 path_chomp(path);
697 bez.p2.x = g_ascii_strtod(path, &path);
698 path_chomp(path);
699 bez.p2.y = g_ascii_strtod(path, &path);
700 path_chomp(path);
701 bez.p3.x = g_ascii_strtod(path, &path);
702 path_chomp(path);
703 bez.p3.y = g_ascii_strtod(path, &path);
704 path_chomp(path);
705 if (last_relative) {
706 bez.p1.x += last_point.x;
707 bez.p1.y += last_point.y;
708 bez.p2.x += last_point.x;
709 bez.p2.y += last_point.y;
710 bez.p3.x += last_point.x;
711 bez.p3.y += last_point.y;
713 last_point = bez.p3;
714 last_control = bez.p2;
716 g_array_append_val(points, bez);
717 break;
718 case PATH_SMOOTHCURVE:
719 bez.type = BEZ_CURVE_TO;
720 bez.p1.x = 2 * last_point.x - last_control.x;
721 bez.p1.y = 2 * last_point.y - last_control.y;
722 bez.p2.x = g_ascii_strtod(path, &path);
723 path_chomp(path);
724 bez.p2.y = g_ascii_strtod(path, &path);
725 path_chomp(path);
726 bez.p3.x = g_ascii_strtod(path, &path);
727 path_chomp(path);
728 bez.p3.y = g_ascii_strtod(path, &path);
729 path_chomp(path);
730 if (last_relative) {
731 bez.p2.x += last_point.x;
732 bez.p2.y += last_point.y;
733 bez.p3.x += last_point.x;
734 bez.p3.y += last_point.y;
736 last_point = bez.p3;
737 last_control = bez.p2;
739 g_array_append_val(points, bez);
740 break;
741 case PATH_ARC :
743 real rx, ry;
744 real xrot;
745 int largearc, sweep;
746 Point dest, dest_c;
748 rx = g_ascii_strtod(path, &path);
749 path_chomp(path);
750 ry = g_ascii_strtod(path, &path);
751 path_chomp(path);
752 xrot = g_ascii_strtod(path, &path);
753 path_chomp(path);
755 largearc = (int)g_ascii_strtod(path, &path);
756 path_chomp(path);
757 sweep = (int)g_ascii_strtod(path, &path);
758 path_chomp(path);
760 dest.x = g_ascii_strtod(path, &path);
761 path_chomp(path);
762 dest.y = g_ascii_strtod(path, &path);
763 path_chomp(path);
765 if (last_relative) {
766 dest.x += last_point.x;
767 dest.y += last_point.y;
770 _path_arc (points, last_point.x, last_point.y,
771 rx, ry, xrot, largearc, sweep, dest.x, dest.y,
772 &dest_c);
773 last_point = dest;
774 last_control = dest_c;
776 break;
777 case PATH_CLOSE:
778 /* close the path with a line */
779 if (last_open.x != last_point.x || last_open.y != last_point.y) {
780 bez.type = BEZ_LINE_TO;
781 bez.p1 = last_open;
782 g_array_append_val(points, bez);
784 *closed = TRUE;
785 need_next_element = TRUE;
787 /* get rid of any ignorable characters */
788 path_chomp(path);
789 MORETOPARSE:
790 if (need_next_element) {
791 /* check if there really is mor to be parsed */
792 if (path[0] != 0)
793 *unparsed = path;
794 break; /* while */
798 /* avoid returning an array with only one point (I'd say the exporter
799 * producing such is rather broken, but *our* bezier creation code
800 * would crash on it.
802 if (points->len < 2) {
803 g_array_set_size(points, 0);
805 return points;