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.
29 #include <pango/pango-attributes.h>
33 /** Initialize a style object from another style object or defaults.
34 * @param gs An SVG style object to initialize.
35 * @param parent_style An SVG style object to copy values from, or NULL,
36 * in which case defaults will be used.
39 dia_svg_style_init(DiaSvgStyle
*gs
, DiaSvgStyle
*parent_style
)
41 g_return_if_fail (gs
);
42 gs
->stroke
= parent_style
? parent_style
->stroke
: (-1);
43 gs
->line_width
= parent_style
? parent_style
->line_width
: 0.0;
44 gs
->linestyle
= parent_style
? parent_style
->linestyle
: LINESTYLE_SOLID
;
45 gs
->dashlength
= parent_style
? parent_style
->dashlength
: 1;
46 gs
->fill
= parent_style
? parent_style
->fill
: (-1);
47 gs
->linecap
= parent_style
? parent_style
->linecap
: DIA_SVG_LINECAPS_DEFAULT
;
48 gs
->linejoin
= parent_style
? parent_style
->linejoin
: DIA_SVG_LINEJOIN_DEFAULT
;
49 gs
->linestyle
= parent_style
? parent_style
->linestyle
: DIA_SVG_LINESTYLE_DEFAULT
;
50 gs
->font
= (parent_style
&& parent_style
->font
) ? dia_font_ref(parent_style
->font
) : NULL
;
51 gs
->font_height
= parent_style
? parent_style
->font_height
: 0.8;
52 gs
->alignment
= parent_style
? parent_style
->alignment
: ALIGN_LEFT
;
55 /** Copy style values from one SVG style object to another.
56 * @param dest SVG style object to copy to.
57 * @param src SVG style object to copy from.
60 dia_svg_style_copy(DiaSvgStyle
*dest
, DiaSvgStyle
*src
)
62 g_return_if_fail (dest
&& src
);
64 dest
->stroke
= src
->stroke
;
65 dest
->line_width
= src
->line_width
;
66 dest
->linestyle
= src
->linestyle
;
67 dest
->dashlength
= src
->dashlength
;
68 dest
->fill
= src
->fill
;
70 dia_font_unref (dest
->font
);
71 dest
->font
= src
->font
? dia_font_ref(src
->font
) : NULL
;
72 dest
->font_height
= src
->font_height
;
73 dest
->alignment
= src
->alignment
;
76 /** Parse an SVG color description.
77 * @param color A place to store the color information (0RGB)
78 * @param str An SVG color description string to parse.
79 * @return TRUE if parsing was successful.
80 * Shouldn't we use an actual Dia Color object as return value?
81 * Would require that the DiaSvgStyle object uses that, too. If we did that,
82 * we could even return the color object directly, and we would be able to use
83 * >8 bits per channel.
84 * But we would not be able to handle named colors anymore ...
87 _parse_color(gint32
*color
, const char *str
)
90 *color
= strtol(str
+1, NULL
, 16) & 0xffffff;
91 else if (0 == strncmp(str
, "none", 4))
92 *color
= DIA_SVG_COLOUR_NONE
;
93 else if (0 == strncmp(str
, "foreground", 10) || 0 == strncmp(str
, "fg", 2) ||
94 0 == strncmp(str
, "inverse", 7))
95 *color
= DIA_SVG_COLOUR_FOREGROUND
;
96 else if (0 == strncmp(str
, "background", 10) || 0 == strncmp(str
, "bg", 2) ||
97 0 == strncmp(str
, "default", 7))
98 *color
= DIA_SVG_COLOUR_BACKGROUND
;
99 else if (0 == strcmp(str
, "text"))
100 *color
= DIA_SVG_COLOUR_TEXT
;
101 else if (0 == strncmp(str
, "rgb(", 4)) {
102 int r
= 0, g
= 0, b
= 0;
103 if (3 == sscanf (str
+4, "%d,%d,%d", &r
, &g
, &b
))
104 *color
= ((r
<<16) & 0xFF0000) | ((g
<<8) & 0xFF00) | (b
& 0xFF);
108 /* Pango needs null terminated strings, so we just use it as a fallback */
110 char* se
= strchr (str
, ';');
113 if (pango_color_parse (&pc
, str
))
114 *color
= ((pc
.red
>> 8) << 16) | ((pc
.green
>> 8) << 8) | (pc
.blue
>> 8);
118 gchar
* sz
= g_strndup (str
, se
- str
);
119 gboolean ret
= pango_color_parse (&pc
, str
);
122 *color
= ((pc
.red
>> 8) << 16) | ((pc
.green
>> 8) << 8) | (pc
.blue
>> 8);
132 FONT_NAME_LENGTH_MAX
= 40
135 /** This function not only parses the style attribute of the given node
136 * it also extracts some of the style properties directly.
137 * @param node An XML node to parse a style from.
138 * @param s The SVG style object to fill out. This should previously be
139 * initialized to some default values.
140 * @bug This function is way too long (213 lines). So dont touch it :)
143 dia_svg_parse_style(xmlNodePtr node
, DiaSvgStyle
*s
)
146 gchar temp
[FONT_NAME_LENGTH_MAX
+1]; /* font-family names will be limited to 40 characters */
148 gboolean over
= FALSE
;
149 char *family
= NULL
, *style
= NULL
, *weight
= NULL
;
151 str
= xmlGetProp(node
, "style");
154 gchar
*ptr
= (gchar
*)str
;
155 while (ptr
[0] != '\0') {
156 /* skip white space at start */
157 while (ptr
[0] != '\0' && g_ascii_isspace(ptr
[0])) ptr
++;
158 if (ptr
[0] == '\0') break;
160 if (!strncmp("font-family:", ptr
, 12)) {
162 while ((ptr
[0] != '\0') && g_ascii_isspace(ptr
[0])) ptr
++;
164 while (ptr
[0] != '\0' && ptr
[0] != ';' && !over
) {
165 if (i
< FONT_NAME_LENGTH_MAX
) {
173 if (!over
) family
= g_strdup(temp
);
174 } else if (!strncmp("font-weight:", ptr
, 12)) {
176 while ((ptr
[0] != '\0') && g_ascii_isspace(ptr
[0])) ptr
++;
178 while (ptr
[0] != '\0' && ptr
[0] != ';' && !over
) {
179 if (i
< FONT_NAME_LENGTH_MAX
) {
187 if (!over
) weight
= g_strdup(temp
);
188 } else if (!strncmp("font-style:", ptr
, 11)) {
190 while ((ptr
[0] != '\0') && g_ascii_isspace(ptr
[0])) ptr
++;
192 while (ptr
[0] != '\0' && ptr
[0] != ';' && !over
) {
193 if (i
< FONT_NAME_LENGTH_MAX
) {
201 if (!over
) style
= g_strdup(temp
);
202 } else if (!strncmp("font-size:", ptr
, 10)) {
204 while ((ptr
[0] != '\0') && g_ascii_isspace(ptr
[0])) ptr
++;
206 while (ptr
[0] != '\0' && ptr
[0] != ';' && !over
) {
207 if (i
< FONT_NAME_LENGTH_MAX
) {
216 s
->font_height
= g_ascii_strtod(temp
, NULL
);
218 } else if (!strncmp("text-anchor:", ptr
, 12)) {
220 while ((ptr
[0] != '\0') && g_ascii_isspace(ptr
[0])) ptr
++;
221 if (!strncmp(ptr
, "start", 5))
222 s
->alignment
= ALIGN_LEFT
;
223 else if (!strncmp(ptr
, "end", 3))
224 s
->alignment
= ALIGN_RIGHT
;
225 else if (!strncmp(ptr
, "middle", 6))
226 s
->alignment
= ALIGN_CENTER
;
228 } else if (!strncmp("stroke-width:", ptr
, 13)) {
230 s
->line_width
= g_ascii_strtod(ptr
, &ptr
);
231 } else if (!strncmp("stroke:", ptr
, 7)) {
233 while ((ptr
[0] != '\0') && g_ascii_isspace(ptr
[0])) ptr
++;
234 if (ptr
[0] == '\0') break;
236 _parse_color (&s
->stroke
, ptr
);
237 } else if (!strncmp("fill:", ptr
, 5)) {
239 while (ptr
[0] != '\0' && g_ascii_isspace(ptr
[0])) ptr
++;
240 if (ptr
[0] == '\0') break;
242 _parse_color (&s
->fill
, ptr
);
243 } else if (!strncmp("stroke-linecap:", ptr
, 15)) {
245 while (ptr
[0] != '\0' && g_ascii_isspace(ptr
[0])) ptr
++;
246 if (ptr
[0] == '\0') break;
248 if (!strncmp(ptr
, "butt", 4))
249 s
->linecap
= LINECAPS_BUTT
;
250 else if (!strncmp(ptr
, "round", 5))
251 s
->linecap
= LINECAPS_ROUND
;
252 else if (!strncmp(ptr
, "square", 6) || !strncmp(ptr
, "projecting", 10))
253 s
->linecap
= LINECAPS_PROJECTING
;
254 else if (!strncmp(ptr
, "default", 7))
255 s
->linecap
= DIA_SVG_LINECAPS_DEFAULT
;
256 } else if (!strncmp("stroke-linejoin:", ptr
, 16)) {
258 while (ptr
[0] != '\0' && g_ascii_isspace(ptr
[0])) ptr
++;
259 if (ptr
[0] == '\0') break;
261 if (!strncmp(ptr
, "miter", 5))
262 s
->linejoin
= LINEJOIN_MITER
;
263 else if (!strncmp(ptr
, "round", 5))
264 s
->linejoin
= LINEJOIN_ROUND
;
265 else if (!strncmp(ptr
, "bevel", 5))
266 s
->linejoin
= LINEJOIN_BEVEL
;
267 else if (!strncmp(ptr
, "default", 7))
268 s
->linejoin
= DIA_SVG_LINEJOIN_DEFAULT
;
269 } else if (!strncmp("stroke-pattern:", ptr
, 15)) {
271 while (ptr
[0] != '\0' && g_ascii_isspace(ptr
[0])) ptr
++;
272 if (ptr
[0] == '\0') break;
274 if (!strncmp(ptr
, "solid", 5))
275 s
->linestyle
= LINESTYLE_SOLID
;
276 else if (!strncmp(ptr
, "dashed", 6))
277 s
->linestyle
= LINESTYLE_DASHED
;
278 else if (!strncmp(ptr
, "dash-dot", 8))
279 s
->linestyle
= LINESTYLE_DASH_DOT
;
280 else if (!strncmp(ptr
, "dash-dot-dot", 12))
281 s
->linestyle
= LINESTYLE_DASH_DOT_DOT
;
282 else if (!strncmp(ptr
, "dotted", 6))
283 s
->linestyle
= LINESTYLE_DOTTED
;
284 else if (!strncmp(ptr
, "default", 7))
285 s
->linestyle
= DIA_SVG_LINESTYLE_DEFAULT
;
286 /* XXX: deal with a real pattern */
287 } else if (!strncmp("stroke-dashlength:", ptr
, 18)) {
289 while (ptr
[0] != '\0' && g_ascii_isspace(ptr
[0])) ptr
++;
290 if (ptr
[0] == '\0') break;
292 if (!strncmp(ptr
, "default", 7))
295 s
->dashlength
= g_ascii_strtod(ptr
, &ptr
);
297 } else if (!strncmp("stroke-dasharray:", ptr
, 17)) {
298 /* FIXME? do we need to read an array here (not clear from
299 * Dia's usage); do we need to set the linestyle depending
300 * on the array's size ? --hb
302 s
->linestyle
= LINESTYLE_DASHED
;
304 while (ptr
[0] != '\0' && g_ascii_isspace(ptr
[0])) ptr
++;
305 if (ptr
[0] == '\0') break;
307 if (!strncmp(ptr
, "default", 7))
310 s
->dashlength
= g_ascii_strtod(ptr
, &ptr
);
314 /* skip up to the next attribute */
315 while (ptr
[0] != '\0' && ptr
[0] != ';' && ptr
[0] != '\n') ptr
++;
316 if (ptr
[0] != '\0') ptr
++;
321 /* ugly svg variations, it is allowed to give style properties without
322 * the style attribute, i.e. direct attributes
324 str
= xmlGetProp(node
, "fill");
326 _parse_color (&s
->fill
, str
);
329 str
= xmlGetProp(node
, "stroke");
331 _parse_color (&s
->stroke
, str
);
334 str
= xmlGetProp(node
, "stroke-width");
336 s
->line_width
= g_ascii_strtod(str
, NULL
);
340 if (family
|| style
|| weight
) {
342 dia_font_unref (s
->font
);
343 s
->font
= dia_font_new_from_style(DIA_FONT_SANS
,s
->font_height
/*bogus*/);
345 dia_font_set_any_family(s
->font
,family
);
349 dia_font_set_slant_from_string(s
->font
,style
);
353 dia_font_set_weight_from_string(s
->font
,weight
);
359 /** Parse a SVG description of an arc segment.
360 * Code stolen from (and adapted)
361 * http://www.inkscape.org/doc/doxygen/html/svg-path_8cpp.php#a7
362 * which may have got it from rsvg, hope it is correct ;)
370 * @param x_axis_rotation
372 * If you want the description of the algorithm read the SVG specs.
375 _path_arc_segment(GArray
* points
,
379 real x_axis_rotation
,
384 real a00
, a01
, a10
, a11
;
385 real x1
, y1
, x2
, y2
, x3
, y3
;
389 sin_th
= sin (x_axis_rotation
* (M_PI
/ 180.0));
390 cos_th
= cos (x_axis_rotation
* (M_PI
/ 180.0));
391 /* inverse transform compared with rsvg_path_arc */
397 th_half
= 0.5 * (th1
- th0
);
398 t
= (8.0 / 3.0) * sin(th_half
* 0.5) * sin(th_half
* 0.5) / sin(th_half
);
399 x1
= xc
+ cos (th0
) - t
* sin (th0
);
400 y1
= yc
+ sin (th0
) + t
* cos (th0
);
403 x2
= x3
+ t
* sin (th1
);
404 y2
= y3
- t
* cos (th1
);
406 bez
.type
= BEZ_CURVE_TO
;
407 bez
.p1
.x
= a00
* x1
+ a01
* y1
;
408 bez
.p1
.y
= a10
* x1
+ a11
* y1
;
409 bez
.p2
.x
= a00
* x2
+ a01
* y2
;
410 bez
.p2
.y
= a10
* x2
+ a11
* y2
;
411 bez
.p3
.x
= a00
* x3
+ a01
* y3
;
412 bez
.p3
.y
= a10
* x3
+ a11
* y3
;
416 g_array_append_val(points
, bez
);
419 /** Parse an SVG description of a full arc.
425 * @param x_axis_rotation
426 * @param large_arc_flag
431 * @bug Also here don't know what the parameters mean.
434 _path_arc(GArray
*points
, double cpx
, double cpy
,
435 double rx
, double ry
, double x_axis_rotation
,
436 int large_arc_flag
, int sweep_flag
,
440 double sin_th
, cos_th
;
441 double a00
, a01
, a10
, a11
;
442 double x0
, y0
, x1
, y1
, xc
, yc
;
443 double d
, sfactor
, sfactor_sq
;
444 double th0
, th1
, th_arc
;
448 sin_th
= sin (x_axis_rotation
* (M_PI
/ 180.0));
449 cos_th
= cos (x_axis_rotation
* (M_PI
/ 180.0));
452 * Correction of out-of-range radii as described in Appendix F.6.6:
454 * 1. Ensure radii are non-zero (Done?).
455 * 2. Ensure that radii are positive.
456 * 3. Ensure that radii are large enough.
458 if(rx
< 0.0) rx
= -rx
;
459 if(ry
< 0.0) ry
= -ry
;
461 px
= cos_th
* (cpx
- x
) * 0.5 + sin_th
* (cpy
- y
) * 0.5;
462 py
= cos_th
* (cpy
- y
) * 0.5 - sin_th
* (cpx
- x
) * 0.5;
463 pl
= (px
* px
) / (rx
* rx
) + (py
* py
) / (ry
* ry
);
472 /* Proceed with computations as described in Appendix F.6.5 */
478 x0
= a00
* cpx
+ a01
* cpy
;
479 y0
= a10
* cpx
+ a11
* cpy
;
480 x1
= a00
* x
+ a01
* y
;
481 y1
= a10
* x
+ a11
* y
;
482 /* (x0, y0) is current point in transformed coordinate space.
483 (x1, y1) is new point in transformed coordinate space.
485 The arc fits a unit-radius circle in this space.
487 d
= (x1
- x0
) * (x1
- x0
) + (y1
- y0
) * (y1
- y0
);
488 sfactor_sq
= 1.0 / d
- 0.25;
489 if (sfactor_sq
< 0) sfactor_sq
= 0;
490 sfactor
= sqrt (sfactor_sq
);
491 if (sweep_flag
== large_arc_flag
) sfactor
= -sfactor
;
492 xc
= 0.5 * (x0
+ x1
) - sfactor
* (y1
- y0
);
493 yc
= 0.5 * (y0
+ y1
) + sfactor
* (x1
- x0
);
494 /* (xc, yc) is center of the circle. */
496 th0
= atan2 (y0
- yc
, x0
- xc
);
497 th1
= atan2 (y1
- yc
, x1
- xc
);
500 if (th_arc
< 0 && sweep_flag
)
502 else if (th_arc
> 0 && !sweep_flag
)
505 n_segs
= (int) ceil (fabs (th_arc
/ (M_PI
* 0.5 + 0.001)));
507 for (i
= 0; i
< n_segs
; i
++) {
508 _path_arc_segment(points
, xc
, yc
,
509 th0
+ i
* th_arc
/ n_segs
,
510 th0
+ (i
+ 1) * th_arc
/ n_segs
,
511 rx
, ry
, x_axis_rotation
,
516 /* routine to chomp off the start of the string */
517 #define path_chomp(path) while (path[0]!='\0'&&strchr(" \t\n\r,", path[0])) path++
519 /** Takes SVG path content and converts it in an array of BezPoint.
521 * SVG pathes can contain multiple MOVE_TO commands while Dia's bezier
522 * object can only contain one so you may need to call this function
525 * @param path_str A string describing an SVG path.
526 * @param unparsed The position in `path_str' where parsing ended, or NULL if
527 * the string was completely parsed. This should be used for
528 * calling the function until it is fully parsed.
529 * @param closed Whether the path was closed.
530 * @returns Array of BezPoint objects, or NULL if an error occurred.
531 * The caller is responsible for freeing the array.
532 * @bug This function is way too long (324 lines). So dont touch it. please!
533 * Shouldn't we try to turn straight lines, simple arc, polylines and
534 * zigzaglines into their appropriate objects? Could either be done by
535 * returning an object or by having functions that try parsing as
536 * specific simple paths.
537 * NOPE: Dia is capable to handle beziers and the file has given us some so WHY should be break it in to pieces ???
540 dia_svg_parse_path(const gchar
*path_str
, gchar
**unparsed
, gboolean
*closed
)
543 PATH_MOVE
, PATH_LINE
, PATH_HLINE
, PATH_VLINE
, PATH_CURVE
,
544 PATH_SMOOTHCURVE
, PATH_ARC
, PATH_CLOSE
} last_type
= PATH_MOVE
;
545 Point last_open
= {0.0, 0.0};
546 Point last_point
= {0.0, 0.0};
547 Point last_control
= {0.0, 0.0};
548 gboolean last_relative
= FALSE
;
551 gchar
*path
= (gchar
*)path_str
;
552 gboolean need_next_element
= FALSE
;
557 points
= g_array_new(FALSE
, FALSE
, sizeof(BezPoint
));
558 g_array_set_size(points
, 0);
561 while (path
[0] != '\0') {
563 g_print("Path: %s\n", path
);
565 /* check for a new command */
568 if (points
->len
> 0) {
569 need_next_element
= TRUE
;
574 last_type
= PATH_MOVE
;
575 last_relative
= FALSE
;
582 last_type
= PATH_MOVE
;
583 last_relative
= TRUE
;
588 last_type
= PATH_LINE
;
589 last_relative
= FALSE
;
594 last_type
= PATH_LINE
;
595 last_relative
= TRUE
;
600 last_type
= PATH_HLINE
;
601 last_relative
= FALSE
;
606 last_type
= PATH_HLINE
;
607 last_relative
= TRUE
;
612 last_type
= PATH_VLINE
;
613 last_relative
= FALSE
;
618 last_type
= PATH_VLINE
;
619 last_relative
= TRUE
;
624 last_type
= PATH_CURVE
;
625 last_relative
= FALSE
;
630 last_type
= PATH_CURVE
;
631 last_relative
= TRUE
;
636 last_type
= PATH_SMOOTHCURVE
;
637 last_relative
= FALSE
;
642 last_type
= PATH_SMOOTHCURVE
;
643 last_relative
= TRUE
;
649 last_type
= PATH_CLOSE
;
650 last_relative
= FALSE
;
655 last_type
= PATH_ARC
;
656 last_relative
= FALSE
;
661 last_type
= PATH_ARC
;
662 last_relative
= TRUE
;
677 if (last_type
== PATH_CLOSE
) {
678 g_warning("parse_path: argument given for implicite close path");
679 /* consume one number so we don't fall into an infinite loop */
680 while (path
!= '\0' && strchr("0123456789.+-", path
[0])) path
++;
683 need_next_element
= TRUE
;
688 g_warning("unsupported path code '%c'", path
[0]);
694 /* actually parse the path component */
697 bez
.type
= BEZ_MOVE_TO
;
698 bez
.p1
.x
= g_ascii_strtod(path
, &path
);
700 bez
.p1
.y
= g_ascii_strtod(path
, &path
);
703 bez
.p1
.x
+= last_point
.x
;
704 bez
.p1
.y
+= last_point
.y
;
707 last_control
= bez
.p1
;
709 g_array_append_val(points
, bez
);
712 bez
.type
= BEZ_LINE_TO
;
713 bez
.p1
.x
= g_ascii_strtod(path
, &path
);
715 bez
.p1
.y
= g_ascii_strtod(path
, &path
);
718 bez
.p1
.x
+= last_point
.x
;
719 bez
.p1
.y
+= last_point
.y
;
722 last_control
= bez
.p1
;
724 g_array_append_val(points
, bez
);
727 bez
.type
= BEZ_LINE_TO
;
728 bez
.p1
.x
= g_ascii_strtod(path
, &path
);
730 bez
.p1
.y
= last_point
.y
;
732 bez
.p1
.x
+= last_point
.x
;
734 last_control
= bez
.p1
;
736 g_array_append_val(points
, bez
);
739 bez
.type
= BEZ_LINE_TO
;
740 bez
.p1
.x
= last_point
.x
;
741 bez
.p1
.y
= g_ascii_strtod(path
, &path
);
744 bez
.p1
.y
+= last_point
.y
;
746 last_control
= bez
.p1
;
748 g_array_append_val(points
, bez
);
751 bez
.type
= BEZ_CURVE_TO
;
752 bez
.p1
.x
= g_ascii_strtod(path
, &path
);
754 bez
.p1
.y
= g_ascii_strtod(path
, &path
);
756 bez
.p2
.x
= g_ascii_strtod(path
, &path
);
758 bez
.p2
.y
= g_ascii_strtod(path
, &path
);
760 bez
.p3
.x
= g_ascii_strtod(path
, &path
);
762 bez
.p3
.y
= g_ascii_strtod(path
, &path
);
765 bez
.p1
.x
+= last_point
.x
;
766 bez
.p1
.y
+= last_point
.y
;
767 bez
.p2
.x
+= last_point
.x
;
768 bez
.p2
.y
+= last_point
.y
;
769 bez
.p3
.x
+= last_point
.x
;
770 bez
.p3
.y
+= last_point
.y
;
773 last_control
= bez
.p2
;
775 g_array_append_val(points
, bez
);
777 case PATH_SMOOTHCURVE
:
778 bez
.type
= BEZ_CURVE_TO
;
779 bez
.p1
.x
= 2 * last_point
.x
- last_control
.x
;
780 bez
.p1
.y
= 2 * last_point
.y
- last_control
.y
;
781 bez
.p2
.x
= g_ascii_strtod(path
, &path
);
783 bez
.p2
.y
= g_ascii_strtod(path
, &path
);
785 bez
.p3
.x
= g_ascii_strtod(path
, &path
);
787 bez
.p3
.y
= g_ascii_strtod(path
, &path
);
790 bez
.p2
.x
+= last_point
.x
;
791 bez
.p2
.y
+= last_point
.y
;
792 bez
.p3
.x
+= last_point
.x
;
793 bez
.p3
.y
+= last_point
.y
;
796 last_control
= bez
.p2
;
798 g_array_append_val(points
, bez
);
807 rx
= g_ascii_strtod(path
, &path
);
809 ry
= g_ascii_strtod(path
, &path
);
811 xrot
= g_ascii_strtod(path
, &path
);
814 largearc
= (int)g_ascii_strtod(path
, &path
);
816 sweep
= (int)g_ascii_strtod(path
, &path
);
819 dest
.x
= g_ascii_strtod(path
, &path
);
821 dest
.y
= g_ascii_strtod(path
, &path
);
825 dest
.x
+= last_point
.x
;
826 dest
.y
+= last_point
.y
;
829 _path_arc (points
, last_point
.x
, last_point
.y
,
830 rx
, ry
, xrot
, largearc
, sweep
, dest
.x
, dest
.y
,
833 last_control
= dest_c
;
837 /* close the path with a line */
838 if (last_open
.x
!= last_point
.x
|| last_open
.y
!= last_point
.y
) {
839 bez
.type
= BEZ_LINE_TO
;
841 g_array_append_val(points
, bez
);
844 need_next_element
= TRUE
;
846 /* get rid of any ignorable characters */
849 if (need_next_element
) {
850 /* check if there really is mor to be parsed */
857 /* avoid returning an array with only one point (I'd say the exporter
858 * producing such is rather broken, but *our* bezier creation code
861 if (points
->len
< 2) {
862 g_array_set_size(points
, 0);