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 parser
= dom_xml_parser_create(NULL
, NULL
,
156 ignore_msg
, NULL
, &document
);
159 return svgtiny_LIBDOM_ERROR
;
161 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
162 if (err
!= DOM_XML_OK
) {
163 dom_node_unref(document
);
164 dom_xml_parser_destroy(parser
);
165 return svgtiny_LIBDOM_ERROR
;
168 err
= dom_xml_parser_completed(parser
);
169 if (err
!= DOM_XML_OK
) {
170 dom_node_unref(document
);
171 dom_xml_parser_destroy(parser
);
172 return svgtiny_LIBDOM_ERROR
;
175 /* We're done parsing, drop the parser.
176 * We now own the document entirely.
178 dom_xml_parser_destroy(parser
);
180 /* find root <svg> element */
181 exc
= dom_document_get_document_element(document
, &svg
);
182 if (exc
!= DOM_NO_ERR
) {
183 dom_node_unref(document
);
184 return svgtiny_LIBDOM_ERROR
;
187 /* no root svg element */
188 dom_node_unref(document
);
189 return svgtiny_SVG_ERROR
;
192 exc
= dom_node_get_node_name(svg
, &svg_name
);
193 if (exc
!= DOM_NO_ERR
) {
195 dom_node_unref(document
);
196 return svgtiny_LIBDOM_ERROR
;
198 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
199 &svg_name_lwc
) != lwc_error_ok
) {
200 dom_string_unref(svg_name
);
202 dom_node_unref(document
);
203 return svgtiny_LIBDOM_ERROR
;
205 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
206 lwc_string_unref(svg_name_lwc
);
207 dom_string_unref(svg_name
);
209 dom_node_unref(document
);
210 return svgtiny_NOT_SVG
;
213 lwc_string_unref(svg_name_lwc
);
214 dom_string_unref(svg_name
);
216 /* get graphic dimensions */
217 memset(&state
, 0, sizeof(state
));
218 state
.diagram
= diagram
;
219 state
.document
= document
;
220 state
.viewport_width
= viewport_width
;
221 state
.viewport_height
= viewport_height
;
223 #define SVGTINY_STRING_ACTION2(s,n) \
224 if (dom_string_create_interned((const uint8_t *) #n, \
225 strlen(#n), &state.interned_##s) \
227 code = svgtiny_LIBDOM_ERROR; \
230 #include "svgtiny_strings.h"
231 #undef SVGTINY_STRING_ACTION2
233 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
234 diagram
->width
= width
;
235 diagram
->height
= height
;
237 /* set up parsing state */
238 state
.viewport_width
= width
;
239 state
.viewport_height
= height
;
240 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
243 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
244 state
.ctm
.e
= 0; /*x;*/
245 state
.ctm
.f
= 0; /*y;*/
246 /*state.style = css_base_style;
247 state.style.font_size.value.length.value = option_font_size * 0.1;*/
248 state
.fill
= 0x000000;
249 state
.stroke
= svgtiny_TRANSPARENT
;
250 state
.stroke_width
= 1;
251 state
.linear_gradient_stop_count
= 0;
254 code
= svgtiny_parse_svg(svg
, state
);
257 dom_node_unref(document
);
260 svgtiny_cleanup_state_local(&state
);
261 #define SVGTINY_STRING_ACTION2(s,n) \
262 if (state.interned_##s != NULL) \
263 dom_string_unref(state.interned_##s);
264 #include "svgtiny_strings.h"
265 #undef SVGTINY_STRING_ACTION2
271 * Parse a <svg> or <g> element node.
274 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
275 struct svgtiny_parse_state state
)
277 float x
, y
, width
, height
;
278 dom_string
*view_box
;
282 svgtiny_setup_state_local(&state
);
284 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
285 svgtiny_parse_paint_attributes(svg
, &state
);
286 svgtiny_parse_font_attributes(svg
, &state
);
288 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
290 if (exc
!= DOM_NO_ERR
) {
291 svgtiny_cleanup_state_local(&state
);
292 return svgtiny_LIBDOM_ERROR
;
296 char *s
= strndup(dom_string_data(view_box
),
297 dom_string_byte_length(view_box
));
298 float min_x
, min_y
, vwidth
, vheight
;
299 if (sscanf(s
, "%f,%f,%f,%f",
300 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
301 sscanf(s
, "%f %f %f %f",
302 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
303 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
304 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
305 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
306 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
309 dom_string_unref(view_box
);
312 svgtiny_parse_transform_attributes(svg
, &state
);
314 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
315 if (exc
!= DOM_NO_ERR
) {
316 svgtiny_cleanup_state_local(&state
);
317 return svgtiny_LIBDOM_ERROR
;
319 while (child
!= NULL
) {
321 dom_node_type nodetype
;
322 svgtiny_code code
= svgtiny_OK
;
324 exc
= dom_node_get_node_type(child
, &nodetype
);
325 if (exc
!= DOM_NO_ERR
) {
326 dom_node_unref(child
);
327 return svgtiny_LIBDOM_ERROR
;
329 if (nodetype
== DOM_ELEMENT_NODE
) {
330 dom_string
*nodename
;
331 exc
= dom_node_get_node_name(child
, &nodename
);
332 if (exc
!= DOM_NO_ERR
) {
333 dom_node_unref(child
);
334 svgtiny_cleanup_state_local(&state
);
335 return svgtiny_LIBDOM_ERROR
;
337 if (dom_string_caseless_isequal(state
.interned_svg
,
339 code
= svgtiny_parse_svg(child
, state
);
340 else if (dom_string_caseless_isequal(state
.interned_g
,
342 code
= svgtiny_parse_svg(child
, state
);
343 else if (dom_string_caseless_isequal(state
.interned_a
,
345 code
= svgtiny_parse_svg(child
, state
);
346 else if (dom_string_caseless_isequal(state
.interned_path
,
348 code
= svgtiny_parse_path(child
, state
);
349 else if (dom_string_caseless_isequal(state
.interned_rect
,
351 code
= svgtiny_parse_rect(child
, state
);
352 else if (dom_string_caseless_isequal(state
.interned_circle
,
354 code
= svgtiny_parse_circle(child
, state
);
355 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
357 code
= svgtiny_parse_ellipse(child
, state
);
358 else if (dom_string_caseless_isequal(state
.interned_line
,
360 code
= svgtiny_parse_line(child
, state
);
361 else if (dom_string_caseless_isequal(state
.interned_polyline
,
363 code
= svgtiny_parse_poly(child
, state
, false);
364 else if (dom_string_caseless_isequal(state
.interned_polygon
,
366 code
= svgtiny_parse_poly(child
, state
, true);
367 else if (dom_string_caseless_isequal(state
.interned_text
,
369 code
= svgtiny_parse_text(child
, state
);
370 dom_string_unref(nodename
);
372 if (code
!= svgtiny_OK
) {
373 dom_node_unref(child
);
374 svgtiny_cleanup_state_local(&state
);
377 exc
= dom_node_get_next_sibling(child
,
378 (dom_node
**) (void *) &next
);
379 dom_node_unref(child
);
380 if (exc
!= DOM_NO_ERR
) {
381 svgtiny_cleanup_state_local(&state
);
382 return svgtiny_LIBDOM_ERROR
;
387 svgtiny_cleanup_state_local(&state
);
394 * Parse a <path> element node.
396 * http://www.w3.org/TR/SVG11/paths#PathElement
399 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
400 struct svgtiny_parse_state state
)
403 dom_string
*path_d_str
;
406 float *p
; /* path elemets */
407 unsigned int palloc
; /* number of path elements allocated */
409 float last_x
= 0, last_y
= 0;
410 float last_cubic_x
= 0, last_cubic_y
= 0;
411 float last_quad_x
= 0, last_quad_y
= 0;
412 float subpath_first_x
= 0, subpath_first_y
= 0;
414 svgtiny_setup_state_local(&state
);
416 svgtiny_parse_paint_attributes(path
, &state
);
417 svgtiny_parse_transform_attributes(path
, &state
);
419 /* read d attribute */
420 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
421 if (exc
!= DOM_NO_ERR
) {
422 state
.diagram
->error_line
= -1; /* path->line; */
423 state
.diagram
->error_message
= "path: error retrieving d attribute";
424 svgtiny_cleanup_state_local(&state
);
425 return svgtiny_SVG_ERROR
;
428 if (path_d_str
== NULL
) {
429 state
.diagram
->error_line
= -1; /* path->line; */
430 state
.diagram
->error_message
= "path: missing d attribute";
431 svgtiny_cleanup_state_local(&state
);
432 return svgtiny_SVG_ERROR
;
435 /* empty path is permitted it just disables the path */
436 palloc
= dom_string_byte_length(path_d_str
);
438 svgtiny_cleanup_state_local(&state
);
442 /* local copy of the path data allowing in-place modification */
443 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
444 dom_string_unref(path_d_str
);
446 svgtiny_cleanup_state_local(&state
);
447 return svgtiny_OUT_OF_MEMORY
;
450 /* ensure path element allocation is sensibly bounded */
453 } else if (palloc
> 64) {
457 /* allocate initial space for path elements */
458 p
= malloc(sizeof p
[0] * palloc
);
461 svgtiny_cleanup_state_local(&state
);
462 return svgtiny_OUT_OF_MEMORY
;
465 /* parse d and build path */
466 for (i
= 0; s
[i
]; i
++)
473 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
476 /* Ensure there is sufficient space for path elements */
477 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
479 if ((palloc - i) < NUM_ELEMENTS) { \
481 palloc = (palloc * 2) + (palloc / 2); \
482 tp = realloc(p, sizeof p[0] * palloc); \
486 svgtiny_cleanup_state_local(&state); \
487 return svgtiny_OUT_OF_MEMORY; \
494 /* moveto (M, m), lineto (L, l) (2 arguments) */
495 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
496 /*LOG(("moveto or lineto"));*/
497 if (*command
== 'M' || *command
== 'm')
498 plot_command
= svgtiny_PATH_MOVE
;
500 plot_command
= svgtiny_PATH_LINE
;
502 ALLOC_PATH_ELEMENTS(3);
503 p
[i
++] = plot_command
;
504 if ('a' <= *command
) {
508 if (plot_command
== svgtiny_PATH_MOVE
) {
512 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
514 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
517 plot_command
= svgtiny_PATH_LINE
;
518 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
520 /* closepath (Z, z) (no arguments) */
521 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
522 /*LOG(("closepath"));*/
523 ALLOC_PATH_ELEMENTS(1);
525 p
[i
++] = svgtiny_PATH_CLOSE
;
527 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
528 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
530 /* horizontal lineto (H, h) (1 argument) */
531 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
532 /*LOG(("horizontal lineto"));*/
534 ALLOC_PATH_ELEMENTS(3);
536 p
[i
++] = svgtiny_PATH_LINE
;
539 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
541 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
543 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
545 /* vertical lineto (V, v) (1 argument) */
546 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
547 /*LOG(("vertical lineto"));*/
549 ALLOC_PATH_ELEMENTS(3);
551 p
[i
++] = svgtiny_PATH_LINE
;
554 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
555 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
558 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
560 /* curveto (C, c) (6 arguments) */
561 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
562 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
563 /*LOG(("curveto"));*/
565 ALLOC_PATH_ELEMENTS(7);
567 p
[i
++] = svgtiny_PATH_BEZIER
;
568 if (*command
== 'c') {
578 p
[i
++] = last_cubic_x
= x2
;
579 p
[i
++] = last_cubic_y
= y2
;
580 p
[i
++] = last_quad_x
= last_x
= x
;
581 p
[i
++] = last_quad_y
= last_y
= y
;
583 } while (sscanf(s
, "%f %f %f %f %f %f %n",
584 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
586 /* shorthand/smooth curveto (S, s) (4 arguments) */
587 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
588 &x2
, &y2
, &x
, &y
, &n
) == 5) {
589 /*LOG(("shorthand/smooth curveto"));*/
591 ALLOC_PATH_ELEMENTS(7);
593 p
[i
++] = svgtiny_PATH_BEZIER
;
594 x1
= last_x
+ (last_x
- last_cubic_x
);
595 y1
= last_y
+ (last_y
- last_cubic_y
);
596 if (*command
== 's') {
604 p
[i
++] = last_cubic_x
= x2
;
605 p
[i
++] = last_cubic_y
= y2
;
606 p
[i
++] = last_quad_x
= last_x
= x
;
607 p
[i
++] = last_quad_y
= last_y
= y
;
609 } while (sscanf(s
, "%f %f %f %f %n",
610 &x2
, &y2
, &x
, &y
, &n
) == 4);
612 /* quadratic Bezier curveto (Q, q) (4 arguments) */
613 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
614 &x1
, &y1
, &x
, &y
, &n
) == 5) {
615 /*LOG(("quadratic Bezier curveto"));*/
617 ALLOC_PATH_ELEMENTS(7);
619 p
[i
++] = svgtiny_PATH_BEZIER
;
622 if (*command
== 'q') {
628 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
629 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
630 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
631 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
632 p
[i
++] = last_cubic_x
= last_x
= x
;
633 p
[i
++] = last_cubic_y
= last_y
= y
;
635 } while (sscanf(s
, "%f %f %f %f %n",
636 &x1
, &y1
, &x
, &y
, &n
) == 4);
638 /* shorthand/smooth quadratic Bezier curveto (T, t)
640 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
642 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
644 ALLOC_PATH_ELEMENTS(7);
646 p
[i
++] = svgtiny_PATH_BEZIER
;
647 x1
= last_x
+ (last_x
- last_quad_x
);
648 y1
= last_y
+ (last_y
- last_quad_y
);
651 if (*command
== 't') {
657 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
658 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
659 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
660 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
661 p
[i
++] = last_cubic_x
= last_x
= x
;
662 p
[i
++] = last_cubic_y
= last_y
= y
;
664 } while (sscanf(s
, "%f %f %n",
667 /* elliptical arc (A, a) (7 arguments) */
668 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
669 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
672 ALLOC_PATH_ELEMENTS(3);
674 p
[i
++] = svgtiny_PATH_LINE
;
675 if (*command
== 'a') {
679 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
681 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
684 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
685 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
689 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
697 /* no real segments in path */
699 svgtiny_cleanup_state_local(&state
);
703 /* resize path element array to not be over allocated */
707 /* try the resize, if it fails just continue to use the old
710 tp
= realloc(p
, sizeof p
[0] * i
);
716 err
= svgtiny_add_path(p
, i
, &state
);
718 svgtiny_cleanup_state_local(&state
);
725 * Parse a <rect> element node.
727 * http://www.w3.org/TR/SVG11/shapes#RectElement
730 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
731 struct svgtiny_parse_state state
)
734 float x
, y
, width
, height
;
737 svgtiny_setup_state_local(&state
);
739 svgtiny_parse_position_attributes(rect
, state
,
740 &x
, &y
, &width
, &height
);
741 svgtiny_parse_paint_attributes(rect
, &state
);
742 svgtiny_parse_transform_attributes(rect
, &state
);
744 p
= malloc(13 * sizeof p
[0]);
746 svgtiny_cleanup_state_local(&state
);
747 return svgtiny_OUT_OF_MEMORY
;
750 p
[0] = svgtiny_PATH_MOVE
;
753 p
[3] = svgtiny_PATH_LINE
;
756 p
[6] = svgtiny_PATH_LINE
;
759 p
[9] = svgtiny_PATH_LINE
;
762 p
[12] = svgtiny_PATH_CLOSE
;
764 err
= svgtiny_add_path(p
, 13, &state
);
766 svgtiny_cleanup_state_local(&state
);
773 * Parse a <circle> element node.
776 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
777 struct svgtiny_parse_state state
)
780 float x
= 0, y
= 0, r
= -1;
785 svgtiny_setup_state_local(&state
);
787 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
788 if (exc
!= DOM_NO_ERR
) {
789 svgtiny_cleanup_state_local(&state
);
790 return svgtiny_LIBDOM_ERROR
;
793 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
795 dom_string_unref(attr
);
797 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
798 if (exc
!= DOM_NO_ERR
) {
799 svgtiny_cleanup_state_local(&state
);
800 return svgtiny_LIBDOM_ERROR
;
803 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
805 dom_string_unref(attr
);
807 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
808 if (exc
!= DOM_NO_ERR
) {
809 svgtiny_cleanup_state_local(&state
);
810 return svgtiny_LIBDOM_ERROR
;
813 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
815 dom_string_unref(attr
);
817 svgtiny_parse_paint_attributes(circle
, &state
);
818 svgtiny_parse_transform_attributes(circle
, &state
);
821 state
.diagram
->error_line
= -1; /* circle->line; */
822 state
.diagram
->error_message
= "circle: r missing or negative";
823 svgtiny_cleanup_state_local(&state
);
824 return svgtiny_SVG_ERROR
;
827 svgtiny_cleanup_state_local(&state
);
831 p
= malloc(32 * sizeof p
[0]);
833 svgtiny_cleanup_state_local(&state
);
834 return svgtiny_OUT_OF_MEMORY
;
837 p
[0] = svgtiny_PATH_MOVE
;
840 p
[3] = svgtiny_PATH_BEZIER
;
842 p
[5] = y
+ r
* KAPPA
;
843 p
[6] = x
+ r
* KAPPA
;
847 p
[10] = svgtiny_PATH_BEZIER
;
848 p
[11] = x
- r
* KAPPA
;
851 p
[14] = y
+ r
* KAPPA
;
854 p
[17] = svgtiny_PATH_BEZIER
;
856 p
[19] = y
- r
* KAPPA
;
857 p
[20] = x
- r
* KAPPA
;
861 p
[24] = svgtiny_PATH_BEZIER
;
862 p
[25] = x
+ r
* KAPPA
;
865 p
[28] = y
- r
* KAPPA
;
868 p
[31] = svgtiny_PATH_CLOSE
;
870 err
= svgtiny_add_path(p
, 32, &state
);
872 svgtiny_cleanup_state_local(&state
);
879 * Parse an <ellipse> element node.
882 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
883 struct svgtiny_parse_state state
)
886 float x
= 0, y
= 0, rx
= -1, ry
= -1;
891 svgtiny_setup_state_local(&state
);
893 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
894 if (exc
!= DOM_NO_ERR
) {
895 svgtiny_cleanup_state_local(&state
);
896 return svgtiny_LIBDOM_ERROR
;
899 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
901 dom_string_unref(attr
);
903 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
904 if (exc
!= DOM_NO_ERR
) {
905 svgtiny_cleanup_state_local(&state
);
906 return svgtiny_LIBDOM_ERROR
;
909 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
911 dom_string_unref(attr
);
913 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
914 if (exc
!= DOM_NO_ERR
) {
915 svgtiny_cleanup_state_local(&state
);
916 return svgtiny_LIBDOM_ERROR
;
919 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
921 dom_string_unref(attr
);
923 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
924 if (exc
!= DOM_NO_ERR
) {
925 svgtiny_cleanup_state_local(&state
);
926 return svgtiny_LIBDOM_ERROR
;
929 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
931 dom_string_unref(attr
);
933 svgtiny_parse_paint_attributes(ellipse
, &state
);
934 svgtiny_parse_transform_attributes(ellipse
, &state
);
936 if (rx
< 0 || ry
< 0) {
937 state
.diagram
->error_line
= -1; /* ellipse->line; */
938 state
.diagram
->error_message
= "ellipse: rx or ry missing "
940 svgtiny_cleanup_state_local(&state
);
941 return svgtiny_SVG_ERROR
;
943 if (rx
== 0 || ry
== 0) {
944 svgtiny_cleanup_state_local(&state
);
948 p
= malloc(32 * sizeof p
[0]);
950 svgtiny_cleanup_state_local(&state
);
951 return svgtiny_OUT_OF_MEMORY
;
954 p
[0] = svgtiny_PATH_MOVE
;
957 p
[3] = svgtiny_PATH_BEZIER
;
959 p
[5] = y
+ ry
* KAPPA
;
960 p
[6] = x
+ rx
* KAPPA
;
964 p
[10] = svgtiny_PATH_BEZIER
;
965 p
[11] = x
- rx
* KAPPA
;
968 p
[14] = y
+ ry
* KAPPA
;
971 p
[17] = svgtiny_PATH_BEZIER
;
973 p
[19] = y
- ry
* KAPPA
;
974 p
[20] = x
- rx
* KAPPA
;
978 p
[24] = svgtiny_PATH_BEZIER
;
979 p
[25] = x
+ rx
* KAPPA
;
982 p
[28] = y
- ry
* KAPPA
;
985 p
[31] = svgtiny_PATH_CLOSE
;
987 err
= svgtiny_add_path(p
, 32, &state
);
989 svgtiny_cleanup_state_local(&state
);
996 * Parse a <line> element node.
999 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1000 struct svgtiny_parse_state state
)
1003 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1008 svgtiny_setup_state_local(&state
);
1010 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1011 if (exc
!= DOM_NO_ERR
) {
1012 svgtiny_cleanup_state_local(&state
);
1013 return svgtiny_LIBDOM_ERROR
;
1016 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1018 dom_string_unref(attr
);
1020 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1021 if (exc
!= DOM_NO_ERR
) {
1022 svgtiny_cleanup_state_local(&state
);
1023 return svgtiny_LIBDOM_ERROR
;
1026 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1028 dom_string_unref(attr
);
1030 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1031 if (exc
!= DOM_NO_ERR
) {
1032 svgtiny_cleanup_state_local(&state
);
1033 return svgtiny_LIBDOM_ERROR
;
1036 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1038 dom_string_unref(attr
);
1040 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1041 if (exc
!= DOM_NO_ERR
) {
1042 svgtiny_cleanup_state_local(&state
);
1043 return svgtiny_LIBDOM_ERROR
;
1046 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1048 dom_string_unref(attr
);
1050 svgtiny_parse_paint_attributes(line
, &state
);
1051 svgtiny_parse_transform_attributes(line
, &state
);
1053 p
= malloc(7 * sizeof p
[0]);
1055 svgtiny_cleanup_state_local(&state
);
1056 return svgtiny_OUT_OF_MEMORY
;
1059 p
[0] = svgtiny_PATH_MOVE
;
1062 p
[3] = svgtiny_PATH_LINE
;
1065 p
[6] = svgtiny_PATH_CLOSE
;
1067 err
= svgtiny_add_path(p
, 7, &state
);
1069 svgtiny_cleanup_state_local(&state
);
1076 * Parse a <polyline> or <polygon> element node.
1078 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1079 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1082 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1083 struct svgtiny_parse_state state
, bool polygon
)
1086 dom_string
*points_str
;
1092 svgtiny_setup_state_local(&state
);
1094 svgtiny_parse_paint_attributes(poly
, &state
);
1095 svgtiny_parse_transform_attributes(poly
, &state
);
1097 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1099 if (exc
!= DOM_NO_ERR
) {
1100 svgtiny_cleanup_state_local(&state
);
1101 return svgtiny_LIBDOM_ERROR
;
1104 if (points_str
== NULL
) {
1105 state
.diagram
->error_line
= -1; /* poly->line; */
1106 state
.diagram
->error_message
=
1107 "polyline/polygon: missing points attribute";
1108 svgtiny_cleanup_state_local(&state
);
1109 return svgtiny_SVG_ERROR
;
1112 s
= points
= strndup(dom_string_data(points_str
),
1113 dom_string_byte_length(points_str
));
1114 dom_string_unref(points_str
);
1115 /* read points attribute */
1117 svgtiny_cleanup_state_local(&state
);
1118 return svgtiny_OUT_OF_MEMORY
;
1120 /* allocate space for path: it will never have more elements than s */
1121 p
= malloc(sizeof p
[0] * strlen(s
));
1124 svgtiny_cleanup_state_local(&state
);
1125 return svgtiny_OUT_OF_MEMORY
;
1128 /* parse s and build path */
1129 for (i
= 0; s
[i
]; i
++)
1137 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1139 p
[i
++] = svgtiny_PATH_MOVE
;
1141 p
[i
++] = svgtiny_PATH_LINE
;
1150 p
[i
++] = svgtiny_PATH_CLOSE
;
1154 err
= svgtiny_add_path(p
, i
, &state
);
1156 svgtiny_cleanup_state_local(&state
);
1163 * Parse a <text> or <tspan> element node.
1166 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1167 struct svgtiny_parse_state state
)
1169 float x
, y
, width
, height
;
1174 svgtiny_setup_state_local(&state
);
1176 svgtiny_parse_position_attributes(text
, state
,
1177 &x
, &y
, &width
, &height
);
1178 svgtiny_parse_font_attributes(text
, &state
);
1179 svgtiny_parse_transform_attributes(text
, &state
);
1181 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1182 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1183 /* state.ctm.e = px - state.origin_x; */
1184 /* state.ctm.f = py - state.origin_y; */
1186 /*struct css_style style = state.style;
1187 style.font_size.value.length.value *= state.ctm.a;*/
1189 exc
= dom_node_get_first_child(text
, &child
);
1190 if (exc
!= DOM_NO_ERR
) {
1191 return svgtiny_LIBDOM_ERROR
;
1192 svgtiny_cleanup_state_local(&state
);
1194 while (child
!= NULL
) {
1196 dom_node_type nodetype
;
1197 svgtiny_code code
= svgtiny_OK
;
1199 exc
= dom_node_get_node_type(child
, &nodetype
);
1200 if (exc
!= DOM_NO_ERR
) {
1201 dom_node_unref(child
);
1202 svgtiny_cleanup_state_local(&state
);
1203 return svgtiny_LIBDOM_ERROR
;
1205 if (nodetype
== DOM_ELEMENT_NODE
) {
1206 dom_string
*nodename
;
1207 exc
= dom_node_get_node_name(child
, &nodename
);
1208 if (exc
!= DOM_NO_ERR
) {
1209 dom_node_unref(child
);
1210 svgtiny_cleanup_state_local(&state
);
1211 return svgtiny_LIBDOM_ERROR
;
1213 if (dom_string_caseless_isequal(nodename
,
1214 state
.interned_tspan
))
1215 code
= svgtiny_parse_text((dom_element
*)child
,
1217 dom_string_unref(nodename
);
1218 } else if (nodetype
== DOM_TEXT_NODE
) {
1219 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1220 dom_string
*content
;
1221 if (shape
== NULL
) {
1222 dom_node_unref(child
);
1223 svgtiny_cleanup_state_local(&state
);
1224 return svgtiny_OUT_OF_MEMORY
;
1226 exc
= dom_text_get_whole_text(child
, &content
);
1227 if (exc
!= DOM_NO_ERR
) {
1228 dom_node_unref(child
);
1229 svgtiny_cleanup_state_local(&state
);
1230 return svgtiny_LIBDOM_ERROR
;
1232 if (content
!= NULL
) {
1233 shape
->text
= strndup(dom_string_data(content
),
1234 dom_string_byte_length(content
));
1235 dom_string_unref(content
);
1237 shape
->text
= strdup("");
1241 state
.diagram
->shape_count
++;
1244 if (code
!= svgtiny_OK
) {
1245 dom_node_unref(child
);
1246 svgtiny_cleanup_state_local(&state
);
1249 exc
= dom_node_get_next_sibling(child
, &next
);
1250 dom_node_unref(child
);
1251 if (exc
!= DOM_NO_ERR
) {
1252 svgtiny_cleanup_state_local(&state
);
1253 return svgtiny_LIBDOM_ERROR
;
1258 svgtiny_cleanup_state_local(&state
);
1265 * Parse x, y, width, and height attributes, if present.
1268 void svgtiny_parse_position_attributes(dom_element
*node
,
1269 const struct svgtiny_parse_state state
,
1270 float *x
, float *y
, float *width
, float *height
)
1277 *width
= state
.viewport_width
;
1278 *height
= state
.viewport_height
;
1280 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1281 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1282 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1283 dom_string_unref(attr
);
1286 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1287 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1288 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1289 dom_string_unref(attr
);
1292 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
1293 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1294 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
1296 dom_string_unref(attr
);
1299 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
1300 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1301 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
1303 dom_string_unref(attr
);
1309 * Parse a length as a number of pixels.
1312 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1313 const struct svgtiny_parse_state state
)
1315 int num_length
= strspn(s
, "0123456789+-.");
1316 const char *unit
= s
+ num_length
;
1317 float n
= atof((const char *) s
);
1318 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1324 } else if (unit
[0] == '%') {
1325 return n
/ 100.0 * viewport_size
;
1326 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
1327 return n
* font_size
;
1328 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
1329 return n
/ 2.0 * font_size
;
1330 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
1332 } else if (unit
[0] == 'p' && unit
[1] == 't') {
1334 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
1336 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
1337 return n
* 3.543307;
1338 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
1339 return n
* 35.43307;
1340 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
1347 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
1348 const struct svgtiny_parse_state state
)
1350 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1351 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
1357 * Parse paint attributes, if present.
1360 void svgtiny_parse_paint_attributes(dom_element
*node
,
1361 struct svgtiny_parse_state
*state
)
1366 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
1367 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1368 svgtiny_parse_color(attr
, &state
->fill
, state
);
1369 dom_string_unref(attr
);
1372 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
1373 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1374 svgtiny_parse_color(attr
, &state
->stroke
, state
);
1375 dom_string_unref(attr
);
1378 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
1379 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1380 state
->stroke_width
= svgtiny_parse_length(attr
,
1381 state
->viewport_width
, *state
);
1382 dom_string_unref(attr
);
1385 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
1386 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1387 char *style
= strndup(dom_string_data(attr
),
1388 dom_string_byte_length(attr
));
1391 if ((s
= strstr(style
, "fill:"))) {
1395 value
= strndup(s
, strcspn(s
, "; "));
1396 _svgtiny_parse_color(value
, &state
->fill
, state
);
1399 if ((s
= strstr(style
, "stroke:"))) {
1403 value
= strndup(s
, strcspn(s
, "; "));
1404 _svgtiny_parse_color(value
, &state
->stroke
, state
);
1407 if ((s
= strstr(style
, "stroke-width:"))) {
1411 value
= strndup(s
, strcspn(s
, "; "));
1412 state
->stroke_width
= _svgtiny_parse_length(value
,
1413 state
->viewport_width
, *state
);
1417 dom_string_unref(attr
);
1426 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
1427 struct svgtiny_parse_state
*state
)
1429 unsigned int r
, g
, b
;
1431 size_t len
= strlen(s
);
1432 char *id
= 0, *rparen
;
1434 if (len
== 4 && s
[0] == '#') {
1435 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
1436 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1438 } else if (len
== 7 && s
[0] == '#') {
1439 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1440 *c
= svgtiny_RGB(r
, g
, b
);
1442 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1443 s
[3] == '(' && s
[len
- 1] == ')') {
1444 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1445 *c
= svgtiny_RGB(r
, g
, b
);
1446 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1450 *c
= svgtiny_RGB(r
, g
, b
);
1453 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1454 *c
= svgtiny_TRANSPARENT
;
1456 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1462 rparen
= strchr(id
, ')');
1465 svgtiny_find_gradient(id
, state
);
1467 if (state
->linear_gradient_stop_count
== 0)
1468 *c
= svgtiny_TRANSPARENT
;
1469 else if (state
->linear_gradient_stop_count
== 1)
1470 *c
= state
->gradient_stop
[0].color
;
1472 *c
= svgtiny_LINEAR_GRADIENT
;
1476 const struct svgtiny_named_color
*named_color
;
1477 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1479 *c
= named_color
->color
;
1483 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
1484 struct svgtiny_parse_state
*state
)
1487 _svgtiny_parse_color(dom_string_data(s
), c
, state
);
1488 dom_string_unref(s
);
1492 * Parse font attributes, if present.
1495 void svgtiny_parse_font_attributes(dom_element
*node
,
1496 struct svgtiny_parse_state
*state
)
1498 /* TODO: Implement this, it never used to be */
1501 #ifdef WRITTEN_THIS_PROPERLY
1502 const xmlAttr
*attr
;
1506 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1507 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1508 /*if (css_parse_length(
1509 (const char *) attr->children->content,
1510 &state->style.font_size.value.length,
1512 state->style.font_size.size =
1513 CSS_FONT_SIZE_LENGTH;
1522 * Parse transform attributes, if present.
1524 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1527 void svgtiny_parse_transform_attributes(dom_element
*node
,
1528 struct svgtiny_parse_state
*state
)
1534 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
1536 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1537 transform
= strndup(dom_string_data(attr
),
1538 dom_string_byte_length(attr
));
1539 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1540 &state
->ctm
.c
, &state
->ctm
.d
,
1541 &state
->ctm
.e
, &state
->ctm
.f
);
1543 dom_string_unref(attr
);
1549 * Parse a transform string.
1552 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1553 float *mc
, float *md
, float *me
, float *mf
)
1555 float a
, b
, c
, d
, e
, f
;
1556 float za
, zb
, zc
, zd
, ze
, zf
;
1561 for (i
= 0; s
[i
]; i
++)
1569 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
1570 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
1572 else if ((sscanf(s
, " translate (%f %f ) %n",
1573 &e
, &f
, &n
) == 2) && (n
> 0))
1575 else if ((sscanf(s
, " translate (%f ) %n",
1576 &e
, &n
) == 1) && (n
> 0))
1578 else if ((sscanf(s
, " scale (%f %f ) %n",
1579 &a
, &d
, &n
) == 2) && (n
> 0))
1581 else if ((sscanf(s
, " scale (%f ) %n",
1582 &a
, &n
) == 1) && (n
> 0))
1584 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
1585 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
1586 angle
= angle
/ 180 * M_PI
;
1591 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1592 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1593 } else if ((sscanf(s
, " rotate (%f ) %n",
1594 &angle
, &n
) == 1) && (n
> 0)) {
1595 angle
= angle
/ 180 * M_PI
;
1600 } else if ((sscanf(s
, " skewX (%f ) %n",
1601 &angle
, &n
) == 1) && (n
> 0)) {
1602 angle
= angle
/ 180 * M_PI
;
1604 } else if ((sscanf(s
, " skewY (%f ) %n",
1605 &angle
, &n
) == 1) && (n
> 0)) {
1606 angle
= angle
/ 180 * M_PI
;
1610 za
= *ma
* a
+ *mc
* b
;
1611 zb
= *mb
* a
+ *md
* b
;
1612 zc
= *ma
* c
+ *mc
* d
;
1613 zd
= *mb
* c
+ *md
* d
;
1614 ze
= *ma
* e
+ *mc
* f
+ *me
;
1615 zf
= *mb
* e
+ *md
* f
+ *mf
;
1628 * Add a path to the svgtiny_diagram.
1631 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1632 struct svgtiny_parse_state
*state
)
1634 struct svgtiny_shape
*shape
;
1636 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1637 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1639 svgtiny_transform_path(p
, n
, state
);
1641 shape
= svgtiny_add_shape(state
);
1644 return svgtiny_OUT_OF_MEMORY
;
1647 shape
->path_length
= n
;
1648 state
->diagram
->shape_count
++;
1655 * Add a svgtiny_shape to the svgtiny_diagram.
1658 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1660 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1661 (state
->diagram
->shape_count
+ 1) *
1662 sizeof (state
->diagram
->shape
[0]));
1665 state
->diagram
->shape
= shape
;
1667 shape
+= state
->diagram
->shape_count
;
1669 shape
->path_length
= 0;
1671 shape
->fill
= state
->fill
;
1672 shape
->stroke
= state
->stroke
;
1673 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1674 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1675 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1676 shape
->stroke_width
= 1;
1683 * Apply the current transformation matrix to a path.
1686 void svgtiny_transform_path(float *p
, unsigned int n
,
1687 struct svgtiny_parse_state
*state
)
1691 for (j
= 0; j
!= n
; ) {
1692 unsigned int points
= 0;
1694 switch ((int) p
[j
]) {
1695 case svgtiny_PATH_MOVE
:
1696 case svgtiny_PATH_LINE
:
1699 case svgtiny_PATH_CLOSE
:
1702 case svgtiny_PATH_BEZIER
:
1709 for (k
= 0; k
!= points
; k
++) {
1710 float x0
= p
[j
], y0
= p
[j
+ 1];
1711 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1713 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1724 * Free all memory used by a diagram.
1727 void svgtiny_free(struct svgtiny_diagram
*svg
)
1732 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1733 free(svg
->shape
[i
].path
);
1734 free(svg
->shape
[i
].text
);
1742 #ifndef HAVE_STRNDUP
1743 char *svgtiny_strndup(const char *s
, size_t n
)
1748 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1751 s2
= malloc(len
+ 1);