2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2008-2009 James Bursa <james@semichrome.net>
6 * Copyright 2012 Daniel Silverstone <dsilvers@netsurf-browser.org>
18 #include <dom/bindings/xml/xmlparser.h>
21 #include "svgtiny_internal.h"
24 #define M_PI 3.14159265358979323846
27 #define KAPPA 0.5522847498
29 static svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
30 struct svgtiny_parse_state state
);
31 static svgtiny_code
svgtiny_parse_path(dom_element
*path
,
32 struct svgtiny_parse_state state
);
33 static svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
34 struct svgtiny_parse_state state
);
35 static svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
36 struct svgtiny_parse_state state
);
37 static svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
38 struct svgtiny_parse_state state
);
39 static svgtiny_code
svgtiny_parse_line(dom_element
*line
,
40 struct svgtiny_parse_state state
);
41 static svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
42 struct svgtiny_parse_state state
, bool polygon
);
43 static svgtiny_code
svgtiny_parse_text(dom_element
*text
,
44 struct svgtiny_parse_state state
);
45 static void svgtiny_parse_position_attributes(dom_element
*node
,
46 const struct svgtiny_parse_state state
,
47 float *x
, float *y
, float *width
, float *height
);
48 static void svgtiny_parse_paint_attributes(dom_element
*node
,
49 struct svgtiny_parse_state
*state
);
50 static void svgtiny_parse_font_attributes(dom_element
*node
,
51 struct svgtiny_parse_state
*state
);
52 static void svgtiny_parse_transform_attributes(dom_element
*node
,
53 struct svgtiny_parse_state
*state
);
54 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
55 struct svgtiny_parse_state
*state
);
56 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
57 struct svgtiny_parse_state
*state
);
60 * Set the local externally-stored parts of a parse state.
61 * Call this in functions that made a new state on the stack.
62 * Doesn't make own copy of global state, such as the interned string list.
64 static void svgtiny_setup_state_local(struct svgtiny_parse_state
*state
)
66 if (state
->gradient_x1
!= NULL
) {
67 dom_string_ref(state
->gradient_x1
);
69 if (state
->gradient_y1
!= NULL
) {
70 dom_string_ref(state
->gradient_y1
);
72 if (state
->gradient_x2
!= NULL
) {
73 dom_string_ref(state
->gradient_x2
);
75 if (state
->gradient_y2
!= NULL
) {
76 dom_string_ref(state
->gradient_y2
);
81 * Cleanup the local externally-stored parts of a parse state.
82 * Call this in functions that made a new state on the stack.
83 * Doesn't cleanup global state, such as the interned string list.
85 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state
*state
)
87 if (state
->gradient_x1
!= NULL
) {
88 dom_string_unref(state
->gradient_x1
);
89 state
->gradient_x1
= NULL
;
91 if (state
->gradient_y1
!= NULL
) {
92 dom_string_unref(state
->gradient_y1
);
93 state
->gradient_y1
= NULL
;
95 if (state
->gradient_x2
!= NULL
) {
96 dom_string_unref(state
->gradient_x2
);
97 state
->gradient_x2
= NULL
;
99 if (state
->gradient_y2
!= NULL
) {
100 dom_string_unref(state
->gradient_y2
);
101 state
->gradient_y2
= NULL
;
107 * Create a new svgtiny_diagram structure.
110 struct svgtiny_diagram
*svgtiny_create(void)
112 struct svgtiny_diagram
*diagram
;
114 diagram
= calloc(sizeof(*diagram
), 1);
123 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
131 * Parse a block of memory into a svgtiny_diagram.
134 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
135 const char *buffer
, size_t size
, const char *url
,
136 int viewport_width
, int viewport_height
)
138 dom_document
*document
;
140 dom_xml_parser
*parser
;
143 dom_string
*svg_name
;
144 lwc_string
*svg_name_lwc
;
145 struct svgtiny_parse_state state
;
146 float x
, y
, width
, height
;
155 state
.gradient_x1
= NULL
;
156 state
.gradient_y1
= NULL
;
157 state
.gradient_x2
= NULL
;
158 state
.gradient_y2
= NULL
;
160 parser
= dom_xml_parser_create(NULL
, NULL
,
161 ignore_msg
, NULL
, &document
);
164 return svgtiny_LIBDOM_ERROR
;
166 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
167 if (err
!= DOM_XML_OK
) {
168 dom_node_unref(document
);
169 dom_xml_parser_destroy(parser
);
170 return svgtiny_LIBDOM_ERROR
;
173 err
= dom_xml_parser_completed(parser
);
174 if (err
!= DOM_XML_OK
) {
175 dom_node_unref(document
);
176 dom_xml_parser_destroy(parser
);
177 return svgtiny_LIBDOM_ERROR
;
180 /* We're done parsing, drop the parser.
181 * We now own the document entirely.
183 dom_xml_parser_destroy(parser
);
185 /* find root <svg> element */
186 exc
= dom_document_get_document_element(document
, &svg
);
187 if (exc
!= DOM_NO_ERR
) {
188 dom_node_unref(document
);
189 return svgtiny_LIBDOM_ERROR
;
192 /* no root svg element */
193 dom_node_unref(document
);
194 return svgtiny_SVG_ERROR
;
197 exc
= dom_node_get_node_name(svg
, &svg_name
);
198 if (exc
!= DOM_NO_ERR
) {
200 dom_node_unref(document
);
201 return svgtiny_LIBDOM_ERROR
;
203 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
204 &svg_name_lwc
) != lwc_error_ok
) {
205 dom_string_unref(svg_name
);
207 dom_node_unref(document
);
208 return svgtiny_LIBDOM_ERROR
;
210 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
211 lwc_string_unref(svg_name_lwc
);
212 dom_string_unref(svg_name
);
214 dom_node_unref(document
);
215 return svgtiny_NOT_SVG
;
218 lwc_string_unref(svg_name_lwc
);
219 dom_string_unref(svg_name
);
221 /* get graphic dimensions */
222 memset(&state
, 0, sizeof(state
));
223 state
.diagram
= diagram
;
224 state
.document
= document
;
225 state
.viewport_width
= viewport_width
;
226 state
.viewport_height
= viewport_height
;
228 #define SVGTINY_STRING_ACTION2(s,n) \
229 if (dom_string_create_interned((const uint8_t *) #n, \
230 strlen(#n), &state.interned_##s) \
232 code = svgtiny_LIBDOM_ERROR; \
235 #include "svgtiny_strings.h"
236 #undef SVGTINY_STRING_ACTION2
238 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
239 diagram
->width
= width
;
240 diagram
->height
= height
;
242 /* set up parsing state */
243 state
.viewport_width
= width
;
244 state
.viewport_height
= height
;
245 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
248 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
249 state
.ctm
.e
= 0; /*x;*/
250 state
.ctm
.f
= 0; /*y;*/
251 /*state.style = css_base_style;
252 state.style.font_size.value.length.value = option_font_size * 0.1;*/
253 state
.fill
= 0x000000;
254 state
.stroke
= svgtiny_TRANSPARENT
;
255 state
.stroke_width
= 1;
256 state
.linear_gradient_stop_count
= 0;
259 code
= svgtiny_parse_svg(svg
, state
);
262 dom_node_unref(document
);
265 svgtiny_cleanup_state_local(&state
);
266 #define SVGTINY_STRING_ACTION2(s,n) \
267 if (state.interned_##s != NULL) \
268 dom_string_unref(state.interned_##s);
269 #include "svgtiny_strings.h"
270 #undef SVGTINY_STRING_ACTION2
276 * Parse a <svg> or <g> element node.
279 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
280 struct svgtiny_parse_state state
)
282 float x
, y
, width
, height
;
283 dom_string
*view_box
;
287 svgtiny_setup_state_local(&state
);
289 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
290 svgtiny_parse_paint_attributes(svg
, &state
);
291 svgtiny_parse_font_attributes(svg
, &state
);
293 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
295 if (exc
!= DOM_NO_ERR
) {
296 svgtiny_cleanup_state_local(&state
);
297 return svgtiny_LIBDOM_ERROR
;
301 char *s
= strndup(dom_string_data(view_box
),
302 dom_string_byte_length(view_box
));
303 float min_x
, min_y
, vwidth
, vheight
;
304 if (sscanf(s
, "%f,%f,%f,%f",
305 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
306 sscanf(s
, "%f %f %f %f",
307 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
308 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
309 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
310 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
311 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
314 dom_string_unref(view_box
);
317 svgtiny_parse_transform_attributes(svg
, &state
);
319 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
320 if (exc
!= DOM_NO_ERR
) {
321 svgtiny_cleanup_state_local(&state
);
322 return svgtiny_LIBDOM_ERROR
;
324 while (child
!= NULL
) {
326 dom_node_type nodetype
;
327 svgtiny_code code
= svgtiny_OK
;
329 exc
= dom_node_get_node_type(child
, &nodetype
);
330 if (exc
!= DOM_NO_ERR
) {
331 dom_node_unref(child
);
332 return svgtiny_LIBDOM_ERROR
;
334 if (nodetype
== DOM_ELEMENT_NODE
) {
335 dom_string
*nodename
;
336 exc
= dom_node_get_node_name(child
, &nodename
);
337 if (exc
!= DOM_NO_ERR
) {
338 dom_node_unref(child
);
339 svgtiny_cleanup_state_local(&state
);
340 return svgtiny_LIBDOM_ERROR
;
342 if (dom_string_caseless_isequal(state
.interned_svg
,
344 code
= svgtiny_parse_svg(child
, state
);
345 else if (dom_string_caseless_isequal(state
.interned_g
,
347 code
= svgtiny_parse_svg(child
, state
);
348 else if (dom_string_caseless_isequal(state
.interned_a
,
350 code
= svgtiny_parse_svg(child
, state
);
351 else if (dom_string_caseless_isequal(state
.interned_path
,
353 code
= svgtiny_parse_path(child
, state
);
354 else if (dom_string_caseless_isequal(state
.interned_rect
,
356 code
= svgtiny_parse_rect(child
, state
);
357 else if (dom_string_caseless_isequal(state
.interned_circle
,
359 code
= svgtiny_parse_circle(child
, state
);
360 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
362 code
= svgtiny_parse_ellipse(child
, state
);
363 else if (dom_string_caseless_isequal(state
.interned_line
,
365 code
= svgtiny_parse_line(child
, state
);
366 else if (dom_string_caseless_isequal(state
.interned_polyline
,
368 code
= svgtiny_parse_poly(child
, state
, false);
369 else if (dom_string_caseless_isequal(state
.interned_polygon
,
371 code
= svgtiny_parse_poly(child
, state
, true);
372 else if (dom_string_caseless_isequal(state
.interned_text
,
374 code
= svgtiny_parse_text(child
, state
);
375 dom_string_unref(nodename
);
377 if (code
!= svgtiny_OK
) {
378 dom_node_unref(child
);
379 svgtiny_cleanup_state_local(&state
);
382 exc
= dom_node_get_next_sibling(child
,
383 (dom_node
**) (void *) &next
);
384 dom_node_unref(child
);
385 if (exc
!= DOM_NO_ERR
) {
386 svgtiny_cleanup_state_local(&state
);
387 return svgtiny_LIBDOM_ERROR
;
392 svgtiny_cleanup_state_local(&state
);
399 * Parse a <path> element node.
401 * http://www.w3.org/TR/SVG11/paths#PathElement
404 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
405 struct svgtiny_parse_state state
)
408 dom_string
*path_d_str
;
411 float *p
; /* path elemets */
412 unsigned int palloc
; /* number of path elements allocated */
414 float last_x
= 0, last_y
= 0;
415 float last_cubic_x
= 0, last_cubic_y
= 0;
416 float last_quad_x
= 0, last_quad_y
= 0;
417 float subpath_first_x
= 0, subpath_first_y
= 0;
419 svgtiny_setup_state_local(&state
);
421 svgtiny_parse_paint_attributes(path
, &state
);
422 svgtiny_parse_transform_attributes(path
, &state
);
424 /* read d attribute */
425 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
426 if (exc
!= DOM_NO_ERR
) {
427 state
.diagram
->error_line
= -1; /* path->line; */
428 state
.diagram
->error_message
= "path: error retrieving d attribute";
429 svgtiny_cleanup_state_local(&state
);
430 return svgtiny_SVG_ERROR
;
433 if (path_d_str
== NULL
) {
434 state
.diagram
->error_line
= -1; /* path->line; */
435 state
.diagram
->error_message
= "path: missing d attribute";
436 svgtiny_cleanup_state_local(&state
);
437 return svgtiny_SVG_ERROR
;
440 /* empty path is permitted it just disables the path */
441 palloc
= dom_string_byte_length(path_d_str
);
443 svgtiny_cleanup_state_local(&state
);
447 /* local copy of the path data allowing in-place modification */
448 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
449 dom_string_unref(path_d_str
);
451 svgtiny_cleanup_state_local(&state
);
452 return svgtiny_OUT_OF_MEMORY
;
455 /* ensure path element allocation is sensibly bounded */
458 } else if (palloc
> 64) {
462 /* allocate initial space for path elements */
463 p
= malloc(sizeof p
[0] * palloc
);
466 svgtiny_cleanup_state_local(&state
);
467 return svgtiny_OUT_OF_MEMORY
;
470 /* parse d and build path */
471 for (i
= 0; s
[i
]; i
++)
478 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
481 /* Ensure there is sufficient space for path elements */
482 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
484 if ((palloc - i) < NUM_ELEMENTS) { \
486 palloc = (palloc * 2) + (palloc / 2); \
487 tp = realloc(p, sizeof p[0] * palloc); \
491 svgtiny_cleanup_state_local(&state); \
492 return svgtiny_OUT_OF_MEMORY; \
499 /* moveto (M, m), lineto (L, l) (2 arguments) */
500 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
501 /*LOG(("moveto or lineto"));*/
502 if (*command
== 'M' || *command
== 'm')
503 plot_command
= svgtiny_PATH_MOVE
;
505 plot_command
= svgtiny_PATH_LINE
;
507 ALLOC_PATH_ELEMENTS(3);
508 p
[i
++] = plot_command
;
509 if ('a' <= *command
) {
513 if (plot_command
== svgtiny_PATH_MOVE
) {
517 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
519 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
522 plot_command
= svgtiny_PATH_LINE
;
523 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
525 /* closepath (Z, z) (no arguments) */
526 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
527 /*LOG(("closepath"));*/
528 ALLOC_PATH_ELEMENTS(1);
530 p
[i
++] = svgtiny_PATH_CLOSE
;
532 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
533 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
535 /* horizontal lineto (H, h) (1 argument) */
536 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
537 /*LOG(("horizontal lineto"));*/
539 ALLOC_PATH_ELEMENTS(3);
541 p
[i
++] = svgtiny_PATH_LINE
;
544 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
546 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
548 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
550 /* vertical lineto (V, v) (1 argument) */
551 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
552 /*LOG(("vertical lineto"));*/
554 ALLOC_PATH_ELEMENTS(3);
556 p
[i
++] = svgtiny_PATH_LINE
;
559 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
560 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
563 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
565 /* curveto (C, c) (6 arguments) */
566 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
567 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
568 /*LOG(("curveto"));*/
570 ALLOC_PATH_ELEMENTS(7);
572 p
[i
++] = svgtiny_PATH_BEZIER
;
573 if (*command
== 'c') {
583 p
[i
++] = last_cubic_x
= x2
;
584 p
[i
++] = last_cubic_y
= y2
;
585 p
[i
++] = last_quad_x
= last_x
= x
;
586 p
[i
++] = last_quad_y
= last_y
= y
;
588 } while (sscanf(s
, "%f %f %f %f %f %f %n",
589 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
591 /* shorthand/smooth curveto (S, s) (4 arguments) */
592 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
593 &x2
, &y2
, &x
, &y
, &n
) == 5) {
594 /*LOG(("shorthand/smooth curveto"));*/
596 ALLOC_PATH_ELEMENTS(7);
598 p
[i
++] = svgtiny_PATH_BEZIER
;
599 x1
= last_x
+ (last_x
- last_cubic_x
);
600 y1
= last_y
+ (last_y
- last_cubic_y
);
601 if (*command
== 's') {
609 p
[i
++] = last_cubic_x
= x2
;
610 p
[i
++] = last_cubic_y
= y2
;
611 p
[i
++] = last_quad_x
= last_x
= x
;
612 p
[i
++] = last_quad_y
= last_y
= y
;
614 } while (sscanf(s
, "%f %f %f %f %n",
615 &x2
, &y2
, &x
, &y
, &n
) == 4);
617 /* quadratic Bezier curveto (Q, q) (4 arguments) */
618 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
619 &x1
, &y1
, &x
, &y
, &n
) == 5) {
620 /*LOG(("quadratic Bezier curveto"));*/
622 ALLOC_PATH_ELEMENTS(7);
624 p
[i
++] = svgtiny_PATH_BEZIER
;
627 if (*command
== 'q') {
633 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
634 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
635 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
636 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
637 p
[i
++] = last_cubic_x
= last_x
= x
;
638 p
[i
++] = last_cubic_y
= last_y
= y
;
640 } while (sscanf(s
, "%f %f %f %f %n",
641 &x1
, &y1
, &x
, &y
, &n
) == 4);
643 /* shorthand/smooth quadratic Bezier curveto (T, t)
645 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
647 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
649 ALLOC_PATH_ELEMENTS(7);
651 p
[i
++] = svgtiny_PATH_BEZIER
;
652 x1
= last_x
+ (last_x
- last_quad_x
);
653 y1
= last_y
+ (last_y
- last_quad_y
);
656 if (*command
== 't') {
662 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
663 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
664 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
665 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
666 p
[i
++] = last_cubic_x
= last_x
= x
;
667 p
[i
++] = last_cubic_y
= last_y
= y
;
669 } while (sscanf(s
, "%f %f %n",
672 /* elliptical arc (A, a) (7 arguments) */
673 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
674 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
677 ALLOC_PATH_ELEMENTS(3);
679 p
[i
++] = svgtiny_PATH_LINE
;
680 if (*command
== 'a') {
684 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
686 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
689 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
690 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
694 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
702 /* no real segments in path */
704 svgtiny_cleanup_state_local(&state
);
708 /* resize path element array to not be over allocated */
712 /* try the resize, if it fails just continue to use the old
715 tp
= realloc(p
, sizeof p
[0] * i
);
721 err
= svgtiny_add_path(p
, i
, &state
);
723 svgtiny_cleanup_state_local(&state
);
730 * Parse a <rect> element node.
732 * http://www.w3.org/TR/SVG11/shapes#RectElement
735 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
736 struct svgtiny_parse_state state
)
739 float x
, y
, width
, height
;
742 svgtiny_setup_state_local(&state
);
744 svgtiny_parse_position_attributes(rect
, state
,
745 &x
, &y
, &width
, &height
);
746 svgtiny_parse_paint_attributes(rect
, &state
);
747 svgtiny_parse_transform_attributes(rect
, &state
);
749 p
= malloc(13 * sizeof p
[0]);
751 svgtiny_cleanup_state_local(&state
);
752 return svgtiny_OUT_OF_MEMORY
;
755 p
[0] = svgtiny_PATH_MOVE
;
758 p
[3] = svgtiny_PATH_LINE
;
761 p
[6] = svgtiny_PATH_LINE
;
764 p
[9] = svgtiny_PATH_LINE
;
767 p
[12] = svgtiny_PATH_CLOSE
;
769 err
= svgtiny_add_path(p
, 13, &state
);
771 svgtiny_cleanup_state_local(&state
);
778 * Parse a <circle> element node.
781 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
782 struct svgtiny_parse_state state
)
785 float x
= 0, y
= 0, r
= -1;
790 svgtiny_setup_state_local(&state
);
792 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
793 if (exc
!= DOM_NO_ERR
) {
794 svgtiny_cleanup_state_local(&state
);
795 return svgtiny_LIBDOM_ERROR
;
798 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
800 dom_string_unref(attr
);
802 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
803 if (exc
!= DOM_NO_ERR
) {
804 svgtiny_cleanup_state_local(&state
);
805 return svgtiny_LIBDOM_ERROR
;
808 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
810 dom_string_unref(attr
);
812 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
813 if (exc
!= DOM_NO_ERR
) {
814 svgtiny_cleanup_state_local(&state
);
815 return svgtiny_LIBDOM_ERROR
;
818 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
820 dom_string_unref(attr
);
822 svgtiny_parse_paint_attributes(circle
, &state
);
823 svgtiny_parse_transform_attributes(circle
, &state
);
826 state
.diagram
->error_line
= -1; /* circle->line; */
827 state
.diagram
->error_message
= "circle: r missing or negative";
828 svgtiny_cleanup_state_local(&state
);
829 return svgtiny_SVG_ERROR
;
832 svgtiny_cleanup_state_local(&state
);
836 p
= malloc(32 * sizeof p
[0]);
838 svgtiny_cleanup_state_local(&state
);
839 return svgtiny_OUT_OF_MEMORY
;
842 p
[0] = svgtiny_PATH_MOVE
;
845 p
[3] = svgtiny_PATH_BEZIER
;
847 p
[5] = y
+ r
* KAPPA
;
848 p
[6] = x
+ r
* KAPPA
;
852 p
[10] = svgtiny_PATH_BEZIER
;
853 p
[11] = x
- r
* KAPPA
;
856 p
[14] = y
+ r
* KAPPA
;
859 p
[17] = svgtiny_PATH_BEZIER
;
861 p
[19] = y
- r
* KAPPA
;
862 p
[20] = x
- r
* KAPPA
;
866 p
[24] = svgtiny_PATH_BEZIER
;
867 p
[25] = x
+ r
* KAPPA
;
870 p
[28] = y
- r
* KAPPA
;
873 p
[31] = svgtiny_PATH_CLOSE
;
875 err
= svgtiny_add_path(p
, 32, &state
);
877 svgtiny_cleanup_state_local(&state
);
884 * Parse an <ellipse> element node.
887 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
888 struct svgtiny_parse_state state
)
891 float x
= 0, y
= 0, rx
= -1, ry
= -1;
896 svgtiny_setup_state_local(&state
);
898 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
899 if (exc
!= DOM_NO_ERR
) {
900 svgtiny_cleanup_state_local(&state
);
901 return svgtiny_LIBDOM_ERROR
;
904 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
906 dom_string_unref(attr
);
908 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
909 if (exc
!= DOM_NO_ERR
) {
910 svgtiny_cleanup_state_local(&state
);
911 return svgtiny_LIBDOM_ERROR
;
914 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
916 dom_string_unref(attr
);
918 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
919 if (exc
!= DOM_NO_ERR
) {
920 svgtiny_cleanup_state_local(&state
);
921 return svgtiny_LIBDOM_ERROR
;
924 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
926 dom_string_unref(attr
);
928 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
929 if (exc
!= DOM_NO_ERR
) {
930 svgtiny_cleanup_state_local(&state
);
931 return svgtiny_LIBDOM_ERROR
;
934 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
936 dom_string_unref(attr
);
938 svgtiny_parse_paint_attributes(ellipse
, &state
);
939 svgtiny_parse_transform_attributes(ellipse
, &state
);
941 if (rx
< 0 || ry
< 0) {
942 state
.diagram
->error_line
= -1; /* ellipse->line; */
943 state
.diagram
->error_message
= "ellipse: rx or ry missing "
945 svgtiny_cleanup_state_local(&state
);
946 return svgtiny_SVG_ERROR
;
948 if (rx
== 0 || ry
== 0) {
949 svgtiny_cleanup_state_local(&state
);
953 p
= malloc(32 * sizeof p
[0]);
955 svgtiny_cleanup_state_local(&state
);
956 return svgtiny_OUT_OF_MEMORY
;
959 p
[0] = svgtiny_PATH_MOVE
;
962 p
[3] = svgtiny_PATH_BEZIER
;
964 p
[5] = y
+ ry
* KAPPA
;
965 p
[6] = x
+ rx
* KAPPA
;
969 p
[10] = svgtiny_PATH_BEZIER
;
970 p
[11] = x
- rx
* KAPPA
;
973 p
[14] = y
+ ry
* KAPPA
;
976 p
[17] = svgtiny_PATH_BEZIER
;
978 p
[19] = y
- ry
* KAPPA
;
979 p
[20] = x
- rx
* KAPPA
;
983 p
[24] = svgtiny_PATH_BEZIER
;
984 p
[25] = x
+ rx
* KAPPA
;
987 p
[28] = y
- ry
* KAPPA
;
990 p
[31] = svgtiny_PATH_CLOSE
;
992 err
= svgtiny_add_path(p
, 32, &state
);
994 svgtiny_cleanup_state_local(&state
);
1001 * Parse a <line> element node.
1004 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1005 struct svgtiny_parse_state state
)
1008 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1013 svgtiny_setup_state_local(&state
);
1015 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1016 if (exc
!= DOM_NO_ERR
) {
1017 svgtiny_cleanup_state_local(&state
);
1018 return svgtiny_LIBDOM_ERROR
;
1021 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1023 dom_string_unref(attr
);
1025 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1026 if (exc
!= DOM_NO_ERR
) {
1027 svgtiny_cleanup_state_local(&state
);
1028 return svgtiny_LIBDOM_ERROR
;
1031 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1033 dom_string_unref(attr
);
1035 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1036 if (exc
!= DOM_NO_ERR
) {
1037 svgtiny_cleanup_state_local(&state
);
1038 return svgtiny_LIBDOM_ERROR
;
1041 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1043 dom_string_unref(attr
);
1045 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1046 if (exc
!= DOM_NO_ERR
) {
1047 svgtiny_cleanup_state_local(&state
);
1048 return svgtiny_LIBDOM_ERROR
;
1051 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1053 dom_string_unref(attr
);
1055 svgtiny_parse_paint_attributes(line
, &state
);
1056 svgtiny_parse_transform_attributes(line
, &state
);
1058 p
= malloc(7 * sizeof p
[0]);
1060 svgtiny_cleanup_state_local(&state
);
1061 return svgtiny_OUT_OF_MEMORY
;
1064 p
[0] = svgtiny_PATH_MOVE
;
1067 p
[3] = svgtiny_PATH_LINE
;
1070 p
[6] = svgtiny_PATH_CLOSE
;
1072 err
= svgtiny_add_path(p
, 7, &state
);
1074 svgtiny_cleanup_state_local(&state
);
1081 * Parse a <polyline> or <polygon> element node.
1083 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1084 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1087 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1088 struct svgtiny_parse_state state
, bool polygon
)
1091 dom_string
*points_str
;
1097 svgtiny_setup_state_local(&state
);
1099 svgtiny_parse_paint_attributes(poly
, &state
);
1100 svgtiny_parse_transform_attributes(poly
, &state
);
1102 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1104 if (exc
!= DOM_NO_ERR
) {
1105 svgtiny_cleanup_state_local(&state
);
1106 return svgtiny_LIBDOM_ERROR
;
1109 if (points_str
== NULL
) {
1110 state
.diagram
->error_line
= -1; /* poly->line; */
1111 state
.diagram
->error_message
=
1112 "polyline/polygon: missing points attribute";
1113 svgtiny_cleanup_state_local(&state
);
1114 return svgtiny_SVG_ERROR
;
1117 s
= points
= strndup(dom_string_data(points_str
),
1118 dom_string_byte_length(points_str
));
1119 dom_string_unref(points_str
);
1120 /* read points attribute */
1122 svgtiny_cleanup_state_local(&state
);
1123 return svgtiny_OUT_OF_MEMORY
;
1125 /* allocate space for path: it will never have more elements than s */
1126 p
= malloc(sizeof p
[0] * strlen(s
));
1129 svgtiny_cleanup_state_local(&state
);
1130 return svgtiny_OUT_OF_MEMORY
;
1133 /* parse s and build path */
1134 for (i
= 0; s
[i
]; i
++)
1142 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1144 p
[i
++] = svgtiny_PATH_MOVE
;
1146 p
[i
++] = svgtiny_PATH_LINE
;
1155 p
[i
++] = svgtiny_PATH_CLOSE
;
1159 err
= svgtiny_add_path(p
, i
, &state
);
1161 svgtiny_cleanup_state_local(&state
);
1168 * Parse a <text> or <tspan> element node.
1171 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1172 struct svgtiny_parse_state state
)
1174 float x
, y
, width
, height
;
1179 svgtiny_setup_state_local(&state
);
1181 svgtiny_parse_position_attributes(text
, state
,
1182 &x
, &y
, &width
, &height
);
1183 svgtiny_parse_font_attributes(text
, &state
);
1184 svgtiny_parse_transform_attributes(text
, &state
);
1186 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1187 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1188 /* state.ctm.e = px - state.origin_x; */
1189 /* state.ctm.f = py - state.origin_y; */
1191 /*struct css_style style = state.style;
1192 style.font_size.value.length.value *= state.ctm.a;*/
1194 exc
= dom_node_get_first_child(text
, &child
);
1195 if (exc
!= DOM_NO_ERR
) {
1196 return svgtiny_LIBDOM_ERROR
;
1197 svgtiny_cleanup_state_local(&state
);
1199 while (child
!= NULL
) {
1201 dom_node_type nodetype
;
1202 svgtiny_code code
= svgtiny_OK
;
1204 exc
= dom_node_get_node_type(child
, &nodetype
);
1205 if (exc
!= DOM_NO_ERR
) {
1206 dom_node_unref(child
);
1207 svgtiny_cleanup_state_local(&state
);
1208 return svgtiny_LIBDOM_ERROR
;
1210 if (nodetype
== DOM_ELEMENT_NODE
) {
1211 dom_string
*nodename
;
1212 exc
= dom_node_get_node_name(child
, &nodename
);
1213 if (exc
!= DOM_NO_ERR
) {
1214 dom_node_unref(child
);
1215 svgtiny_cleanup_state_local(&state
);
1216 return svgtiny_LIBDOM_ERROR
;
1218 if (dom_string_caseless_isequal(nodename
,
1219 state
.interned_tspan
))
1220 code
= svgtiny_parse_text((dom_element
*)child
,
1222 dom_string_unref(nodename
);
1223 } else if (nodetype
== DOM_TEXT_NODE
) {
1224 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1225 dom_string
*content
;
1226 if (shape
== NULL
) {
1227 dom_node_unref(child
);
1228 svgtiny_cleanup_state_local(&state
);
1229 return svgtiny_OUT_OF_MEMORY
;
1231 exc
= dom_text_get_whole_text(child
, &content
);
1232 if (exc
!= DOM_NO_ERR
) {
1233 dom_node_unref(child
);
1234 svgtiny_cleanup_state_local(&state
);
1235 return svgtiny_LIBDOM_ERROR
;
1237 if (content
!= NULL
) {
1238 shape
->text
= strndup(dom_string_data(content
),
1239 dom_string_byte_length(content
));
1240 dom_string_unref(content
);
1242 shape
->text
= strdup("");
1246 state
.diagram
->shape_count
++;
1249 if (code
!= svgtiny_OK
) {
1250 dom_node_unref(child
);
1251 svgtiny_cleanup_state_local(&state
);
1254 exc
= dom_node_get_next_sibling(child
, &next
);
1255 dom_node_unref(child
);
1256 if (exc
!= DOM_NO_ERR
) {
1257 svgtiny_cleanup_state_local(&state
);
1258 return svgtiny_LIBDOM_ERROR
;
1263 svgtiny_cleanup_state_local(&state
);
1270 * Parse x, y, width, and height attributes, if present.
1273 void svgtiny_parse_position_attributes(dom_element
*node
,
1274 const struct svgtiny_parse_state state
,
1275 float *x
, float *y
, float *width
, float *height
)
1282 *width
= state
.viewport_width
;
1283 *height
= state
.viewport_height
;
1285 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1286 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1287 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1288 dom_string_unref(attr
);
1291 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1292 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1293 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1294 dom_string_unref(attr
);
1297 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
1298 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1299 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
1301 dom_string_unref(attr
);
1304 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
1305 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1306 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
1308 dom_string_unref(attr
);
1314 * Parse a length as a number of pixels.
1317 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1318 const struct svgtiny_parse_state state
)
1320 int num_length
= strspn(s
, "0123456789+-.");
1321 const char *unit
= s
+ num_length
;
1322 float n
= atof((const char *) s
);
1323 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1329 } else if (unit
[0] == '%') {
1330 return n
/ 100.0 * viewport_size
;
1331 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
1332 return n
* font_size
;
1333 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
1334 return n
/ 2.0 * font_size
;
1335 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
1337 } else if (unit
[0] == 'p' && unit
[1] == 't') {
1339 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
1341 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
1342 return n
* 3.543307;
1343 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
1344 return n
* 35.43307;
1345 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
1352 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
1353 const struct svgtiny_parse_state state
)
1355 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1356 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
1362 * Parse paint attributes, if present.
1365 void svgtiny_parse_paint_attributes(dom_element
*node
,
1366 struct svgtiny_parse_state
*state
)
1371 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
1372 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1373 svgtiny_parse_color(attr
, &state
->fill
, state
);
1374 dom_string_unref(attr
);
1377 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
1378 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1379 svgtiny_parse_color(attr
, &state
->stroke
, state
);
1380 dom_string_unref(attr
);
1383 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
1384 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1385 state
->stroke_width
= svgtiny_parse_length(attr
,
1386 state
->viewport_width
, *state
);
1387 dom_string_unref(attr
);
1390 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
1391 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1392 char *style
= strndup(dom_string_data(attr
),
1393 dom_string_byte_length(attr
));
1396 if ((s
= strstr(style
, "fill:"))) {
1400 value
= strndup(s
, strcspn(s
, "; "));
1401 _svgtiny_parse_color(value
, &state
->fill
, state
);
1404 if ((s
= strstr(style
, "stroke:"))) {
1408 value
= strndup(s
, strcspn(s
, "; "));
1409 _svgtiny_parse_color(value
, &state
->stroke
, state
);
1412 if ((s
= strstr(style
, "stroke-width:"))) {
1416 value
= strndup(s
, strcspn(s
, "; "));
1417 state
->stroke_width
= _svgtiny_parse_length(value
,
1418 state
->viewport_width
, *state
);
1422 dom_string_unref(attr
);
1431 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
1432 struct svgtiny_parse_state
*state
)
1434 unsigned int r
, g
, b
;
1436 size_t len
= strlen(s
);
1437 char *id
= 0, *rparen
;
1439 if (len
== 4 && s
[0] == '#') {
1440 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
1441 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1443 } else if (len
== 7 && s
[0] == '#') {
1444 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1445 *c
= svgtiny_RGB(r
, g
, b
);
1447 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1448 s
[3] == '(' && s
[len
- 1] == ')') {
1449 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1450 *c
= svgtiny_RGB(r
, g
, b
);
1451 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1455 *c
= svgtiny_RGB(r
, g
, b
);
1458 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1459 *c
= svgtiny_TRANSPARENT
;
1461 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1467 rparen
= strchr(id
, ')');
1470 svgtiny_find_gradient(id
, state
);
1472 if (state
->linear_gradient_stop_count
== 0)
1473 *c
= svgtiny_TRANSPARENT
;
1474 else if (state
->linear_gradient_stop_count
== 1)
1475 *c
= state
->gradient_stop
[0].color
;
1477 *c
= svgtiny_LINEAR_GRADIENT
;
1481 const struct svgtiny_named_color
*named_color
;
1482 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1484 *c
= named_color
->color
;
1488 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
1489 struct svgtiny_parse_state
*state
)
1491 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1492 _svgtiny_parse_color(ss
, c
, state
);
1497 * Parse font attributes, if present.
1500 void svgtiny_parse_font_attributes(dom_element
*node
,
1501 struct svgtiny_parse_state
*state
)
1503 /* TODO: Implement this, it never used to be */
1506 #ifdef WRITTEN_THIS_PROPERLY
1507 const xmlAttr
*attr
;
1511 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1512 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1513 /*if (css_parse_length(
1514 (const char *) attr->children->content,
1515 &state->style.font_size.value.length,
1517 state->style.font_size.size =
1518 CSS_FONT_SIZE_LENGTH;
1527 * Parse transform attributes, if present.
1529 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1532 void svgtiny_parse_transform_attributes(dom_element
*node
,
1533 struct svgtiny_parse_state
*state
)
1539 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
1541 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1542 transform
= strndup(dom_string_data(attr
),
1543 dom_string_byte_length(attr
));
1544 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1545 &state
->ctm
.c
, &state
->ctm
.d
,
1546 &state
->ctm
.e
, &state
->ctm
.f
);
1548 dom_string_unref(attr
);
1554 * Parse a transform string.
1557 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1558 float *mc
, float *md
, float *me
, float *mf
)
1560 float a
, b
, c
, d
, e
, f
;
1561 float za
, zb
, zc
, zd
, ze
, zf
;
1566 for (i
= 0; s
[i
]; i
++)
1574 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
1575 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
1577 else if ((sscanf(s
, " translate (%f %f ) %n",
1578 &e
, &f
, &n
) == 2) && (n
> 0))
1580 else if ((sscanf(s
, " translate (%f ) %n",
1581 &e
, &n
) == 1) && (n
> 0))
1583 else if ((sscanf(s
, " scale (%f %f ) %n",
1584 &a
, &d
, &n
) == 2) && (n
> 0))
1586 else if ((sscanf(s
, " scale (%f ) %n",
1587 &a
, &n
) == 1) && (n
> 0))
1589 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
1590 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
1591 angle
= angle
/ 180 * M_PI
;
1596 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1597 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1598 } else if ((sscanf(s
, " rotate (%f ) %n",
1599 &angle
, &n
) == 1) && (n
> 0)) {
1600 angle
= angle
/ 180 * M_PI
;
1605 } else if ((sscanf(s
, " skewX (%f ) %n",
1606 &angle
, &n
) == 1) && (n
> 0)) {
1607 angle
= angle
/ 180 * M_PI
;
1609 } else if ((sscanf(s
, " skewY (%f ) %n",
1610 &angle
, &n
) == 1) && (n
> 0)) {
1611 angle
= angle
/ 180 * M_PI
;
1615 za
= *ma
* a
+ *mc
* b
;
1616 zb
= *mb
* a
+ *md
* b
;
1617 zc
= *ma
* c
+ *mc
* d
;
1618 zd
= *mb
* c
+ *md
* d
;
1619 ze
= *ma
* e
+ *mc
* f
+ *me
;
1620 zf
= *mb
* e
+ *md
* f
+ *mf
;
1633 * Add a path to the svgtiny_diagram.
1636 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1637 struct svgtiny_parse_state
*state
)
1639 struct svgtiny_shape
*shape
;
1641 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1642 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1644 svgtiny_transform_path(p
, n
, state
);
1646 shape
= svgtiny_add_shape(state
);
1649 return svgtiny_OUT_OF_MEMORY
;
1652 shape
->path_length
= n
;
1653 state
->diagram
->shape_count
++;
1660 * Add a svgtiny_shape to the svgtiny_diagram.
1663 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1665 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1666 (state
->diagram
->shape_count
+ 1) *
1667 sizeof (state
->diagram
->shape
[0]));
1670 state
->diagram
->shape
= shape
;
1672 shape
+= state
->diagram
->shape_count
;
1674 shape
->path_length
= 0;
1676 shape
->fill
= state
->fill
;
1677 shape
->stroke
= state
->stroke
;
1678 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1679 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1680 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1681 shape
->stroke_width
= 1;
1688 * Apply the current transformation matrix to a path.
1691 void svgtiny_transform_path(float *p
, unsigned int n
,
1692 struct svgtiny_parse_state
*state
)
1696 for (j
= 0; j
!= n
; ) {
1697 unsigned int points
= 0;
1699 switch ((int) p
[j
]) {
1700 case svgtiny_PATH_MOVE
:
1701 case svgtiny_PATH_LINE
:
1704 case svgtiny_PATH_CLOSE
:
1707 case svgtiny_PATH_BEZIER
:
1714 for (k
= 0; k
!= points
; k
++) {
1715 float x0
= p
[j
], y0
= p
[j
+ 1];
1716 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1718 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1729 * Free all memory used by a diagram.
1732 void svgtiny_free(struct svgtiny_diagram
*svg
)
1737 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1738 free(svg
->shape
[i
].path
);
1739 free(svg
->shape
[i
].text
);
1747 #ifndef HAVE_STRNDUP
1748 char *svgtiny_strndup(const char *s
, size_t n
)
1753 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1756 s2
= malloc(len
+ 1);