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"
23 /* Source file generated by `gperf`. */
24 #include "autogenerated_colors.c"
27 #define M_PI 3.14159265358979323846
30 #define KAPPA 0.5522847498
32 static svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
33 struct svgtiny_parse_state state
);
34 static svgtiny_code
svgtiny_parse_path(dom_element
*path
,
35 struct svgtiny_parse_state state
);
36 static svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
37 struct svgtiny_parse_state state
);
38 static svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
39 struct svgtiny_parse_state state
);
40 static svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
41 struct svgtiny_parse_state state
);
42 static svgtiny_code
svgtiny_parse_line(dom_element
*line
,
43 struct svgtiny_parse_state state
);
44 static svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
45 struct svgtiny_parse_state state
, bool polygon
);
46 static svgtiny_code
svgtiny_parse_text(dom_element
*text
,
47 struct svgtiny_parse_state state
);
48 static void svgtiny_parse_position_attributes(dom_element
*node
,
49 const struct svgtiny_parse_state state
,
50 float *x
, float *y
, float *width
, float *height
);
51 static void svgtiny_parse_paint_attributes(dom_element
*node
,
52 struct svgtiny_parse_state
*state
);
53 static void svgtiny_parse_font_attributes(dom_element
*node
,
54 struct svgtiny_parse_state
*state
);
55 static void svgtiny_parse_transform_attributes(dom_element
*node
,
56 struct svgtiny_parse_state
*state
);
57 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
58 struct svgtiny_parse_state
*state
);
59 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
60 struct svgtiny_parse_state_gradient
*grad
,
61 struct svgtiny_parse_state
*state
);
64 * Call this to ref the strings in a gradient state.
66 static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient
*grad
)
68 if (grad
->gradient_x1
!= NULL
) {
69 dom_string_ref(grad
->gradient_x1
);
71 if (grad
->gradient_y1
!= NULL
) {
72 dom_string_ref(grad
->gradient_y1
);
74 if (grad
->gradient_x2
!= NULL
) {
75 dom_string_ref(grad
->gradient_x2
);
77 if (grad
->gradient_y2
!= NULL
) {
78 dom_string_ref(grad
->gradient_y2
);
83 * Call this to clean up the strings in a gradient state.
85 static void svgtiny_grad_string_cleanup(
86 struct svgtiny_parse_state_gradient
*grad
)
88 if (grad
->gradient_x1
!= NULL
) {
89 dom_string_unref(grad
->gradient_x1
);
90 grad
->gradient_x1
= NULL
;
92 if (grad
->gradient_y1
!= NULL
) {
93 dom_string_unref(grad
->gradient_y1
);
94 grad
->gradient_y1
= NULL
;
96 if (grad
->gradient_x2
!= NULL
) {
97 dom_string_unref(grad
->gradient_x2
);
98 grad
->gradient_x2
= NULL
;
100 if (grad
->gradient_y2
!= NULL
) {
101 dom_string_unref(grad
->gradient_y2
);
102 grad
->gradient_y2
= NULL
;
107 * Set the local externally-stored parts of a parse state.
108 * Call this in functions that made a new state on the stack.
109 * Doesn't make own copy of global state, such as the interned string list.
111 static void svgtiny_setup_state_local(struct svgtiny_parse_state
*state
)
113 svgtiny_grad_string_ref(&(state
->fill_grad
));
114 svgtiny_grad_string_ref(&(state
->stroke_grad
));
118 * Cleanup the local externally-stored parts of a parse state.
119 * Call this in functions that made a new state on the stack.
120 * Doesn't cleanup global state, such as the interned string list.
122 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state
*state
)
124 svgtiny_grad_string_cleanup(&(state
->fill_grad
));
125 svgtiny_grad_string_cleanup(&(state
->stroke_grad
));
130 * Create a new svgtiny_diagram structure.
133 struct svgtiny_diagram
*svgtiny_create(void)
135 struct svgtiny_diagram
*diagram
;
137 diagram
= calloc(sizeof(*diagram
), 1);
146 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
154 * Parse a block of memory into a svgtiny_diagram.
157 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
158 const char *buffer
, size_t size
, const char *url
,
159 int viewport_width
, int viewport_height
)
161 dom_document
*document
;
163 dom_xml_parser
*parser
;
166 dom_string
*svg_name
;
167 lwc_string
*svg_name_lwc
;
168 struct svgtiny_parse_state state
;
169 float x
, y
, width
, height
;
178 parser
= dom_xml_parser_create(NULL
, NULL
,
179 ignore_msg
, NULL
, &document
);
182 return svgtiny_LIBDOM_ERROR
;
184 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
185 if (err
!= DOM_XML_OK
) {
186 dom_node_unref(document
);
187 dom_xml_parser_destroy(parser
);
188 return svgtiny_LIBDOM_ERROR
;
191 err
= dom_xml_parser_completed(parser
);
192 if (err
!= DOM_XML_OK
) {
193 dom_node_unref(document
);
194 dom_xml_parser_destroy(parser
);
195 return svgtiny_LIBDOM_ERROR
;
198 /* We're done parsing, drop the parser.
199 * We now own the document entirely.
201 dom_xml_parser_destroy(parser
);
203 /* find root <svg> element */
204 exc
= dom_document_get_document_element(document
, &svg
);
205 if (exc
!= DOM_NO_ERR
) {
206 dom_node_unref(document
);
207 return svgtiny_LIBDOM_ERROR
;
210 /* no root svg element */
211 dom_node_unref(document
);
212 return svgtiny_SVG_ERROR
;
215 exc
= dom_node_get_node_name(svg
, &svg_name
);
216 if (exc
!= DOM_NO_ERR
) {
218 dom_node_unref(document
);
219 return svgtiny_LIBDOM_ERROR
;
221 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
222 &svg_name_lwc
) != lwc_error_ok
) {
223 dom_string_unref(svg_name
);
225 dom_node_unref(document
);
226 return svgtiny_LIBDOM_ERROR
;
228 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
229 lwc_string_unref(svg_name_lwc
);
230 dom_string_unref(svg_name
);
232 dom_node_unref(document
);
233 return svgtiny_NOT_SVG
;
236 lwc_string_unref(svg_name_lwc
);
237 dom_string_unref(svg_name
);
239 /* get graphic dimensions */
240 memset(&state
, 0, sizeof(state
));
241 state
.diagram
= diagram
;
242 state
.document
= document
;
243 state
.viewport_width
= viewport_width
;
244 state
.viewport_height
= viewport_height
;
246 #define SVGTINY_STRING_ACTION2(s,n) \
247 if (dom_string_create_interned((const uint8_t *) #n, \
248 strlen(#n), &state.interned_##s) \
250 code = svgtiny_LIBDOM_ERROR; \
253 #include "svgtiny_strings.h"
254 #undef SVGTINY_STRING_ACTION2
256 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
257 diagram
->width
= width
;
258 diagram
->height
= height
;
260 /* set up parsing state */
261 state
.viewport_width
= width
;
262 state
.viewport_height
= height
;
263 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
266 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
267 state
.ctm
.e
= 0; /*x;*/
268 state
.ctm
.f
= 0; /*y;*/
269 /*state.style = css_base_style;
270 state.style.font_size.value.length.value = option_font_size * 0.1;*/
271 state
.fill
= 0x000000;
272 state
.stroke
= svgtiny_TRANSPARENT
;
273 state
.stroke_width
= 1;
276 code
= svgtiny_parse_svg(svg
, state
);
279 dom_node_unref(document
);
282 svgtiny_cleanup_state_local(&state
);
283 #define SVGTINY_STRING_ACTION2(s,n) \
284 if (state.interned_##s != NULL) \
285 dom_string_unref(state.interned_##s);
286 #include "svgtiny_strings.h"
287 #undef SVGTINY_STRING_ACTION2
293 * Parse a <svg> or <g> element node.
296 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
297 struct svgtiny_parse_state state
)
299 float x
, y
, width
, height
;
300 dom_string
*view_box
;
304 svgtiny_setup_state_local(&state
);
306 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
307 svgtiny_parse_paint_attributes(svg
, &state
);
308 svgtiny_parse_font_attributes(svg
, &state
);
310 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
312 if (exc
!= DOM_NO_ERR
) {
313 svgtiny_cleanup_state_local(&state
);
314 return svgtiny_LIBDOM_ERROR
;
318 char *s
= strndup(dom_string_data(view_box
),
319 dom_string_byte_length(view_box
));
320 float min_x
, min_y
, vwidth
, vheight
;
321 if (sscanf(s
, "%f,%f,%f,%f",
322 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
323 sscanf(s
, "%f %f %f %f",
324 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
325 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
326 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
327 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
328 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
331 dom_string_unref(view_box
);
334 svgtiny_parse_transform_attributes(svg
, &state
);
336 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
337 if (exc
!= DOM_NO_ERR
) {
338 svgtiny_cleanup_state_local(&state
);
339 return svgtiny_LIBDOM_ERROR
;
341 while (child
!= NULL
) {
343 dom_node_type nodetype
;
344 svgtiny_code code
= svgtiny_OK
;
346 exc
= dom_node_get_node_type(child
, &nodetype
);
347 if (exc
!= DOM_NO_ERR
) {
348 dom_node_unref(child
);
349 return svgtiny_LIBDOM_ERROR
;
351 if (nodetype
== DOM_ELEMENT_NODE
) {
352 dom_string
*nodename
;
353 exc
= dom_node_get_node_name(child
, &nodename
);
354 if (exc
!= DOM_NO_ERR
) {
355 dom_node_unref(child
);
356 svgtiny_cleanup_state_local(&state
);
357 return svgtiny_LIBDOM_ERROR
;
359 if (dom_string_caseless_isequal(state
.interned_svg
,
361 code
= svgtiny_parse_svg(child
, state
);
362 else if (dom_string_caseless_isequal(state
.interned_g
,
364 code
= svgtiny_parse_svg(child
, state
);
365 else if (dom_string_caseless_isequal(state
.interned_a
,
367 code
= svgtiny_parse_svg(child
, state
);
368 else if (dom_string_caseless_isequal(state
.interned_path
,
370 code
= svgtiny_parse_path(child
, state
);
371 else if (dom_string_caseless_isequal(state
.interned_rect
,
373 code
= svgtiny_parse_rect(child
, state
);
374 else if (dom_string_caseless_isequal(state
.interned_circle
,
376 code
= svgtiny_parse_circle(child
, state
);
377 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
379 code
= svgtiny_parse_ellipse(child
, state
);
380 else if (dom_string_caseless_isequal(state
.interned_line
,
382 code
= svgtiny_parse_line(child
, state
);
383 else if (dom_string_caseless_isequal(state
.interned_polyline
,
385 code
= svgtiny_parse_poly(child
, state
, false);
386 else if (dom_string_caseless_isequal(state
.interned_polygon
,
388 code
= svgtiny_parse_poly(child
, state
, true);
389 else if (dom_string_caseless_isequal(state
.interned_text
,
391 code
= svgtiny_parse_text(child
, state
);
392 dom_string_unref(nodename
);
394 if (code
!= svgtiny_OK
) {
395 dom_node_unref(child
);
396 svgtiny_cleanup_state_local(&state
);
399 exc
= dom_node_get_next_sibling(child
,
400 (dom_node
**) (void *) &next
);
401 dom_node_unref(child
);
402 if (exc
!= DOM_NO_ERR
) {
403 svgtiny_cleanup_state_local(&state
);
404 return svgtiny_LIBDOM_ERROR
;
409 svgtiny_cleanup_state_local(&state
);
416 * Parse a <path> element node.
418 * http://www.w3.org/TR/SVG11/paths#PathElement
421 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
422 struct svgtiny_parse_state state
)
425 dom_string
*path_d_str
;
428 float *p
; /* path elemets */
429 unsigned int palloc
; /* number of path elements allocated */
431 float last_x
= 0, last_y
= 0;
432 float last_cubic_x
= 0, last_cubic_y
= 0;
433 float last_quad_x
= 0, last_quad_y
= 0;
434 float subpath_first_x
= 0, subpath_first_y
= 0;
436 svgtiny_setup_state_local(&state
);
438 svgtiny_parse_paint_attributes(path
, &state
);
439 svgtiny_parse_transform_attributes(path
, &state
);
441 /* read d attribute */
442 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
443 if (exc
!= DOM_NO_ERR
) {
444 state
.diagram
->error_line
= -1; /* path->line; */
445 state
.diagram
->error_message
= "path: error retrieving d attribute";
446 svgtiny_cleanup_state_local(&state
);
447 return svgtiny_SVG_ERROR
;
450 if (path_d_str
== NULL
) {
451 state
.diagram
->error_line
= -1; /* path->line; */
452 state
.diagram
->error_message
= "path: missing d attribute";
453 svgtiny_cleanup_state_local(&state
);
454 return svgtiny_SVG_ERROR
;
457 /* empty path is permitted it just disables the path */
458 palloc
= dom_string_byte_length(path_d_str
);
460 dom_string_unref(path_d_str
);
461 svgtiny_cleanup_state_local(&state
);
465 /* local copy of the path data allowing in-place modification */
466 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
467 dom_string_unref(path_d_str
);
469 svgtiny_cleanup_state_local(&state
);
470 return svgtiny_OUT_OF_MEMORY
;
473 /* ensure path element allocation is sensibly bounded */
476 } else if (palloc
> 64) {
480 /* allocate initial space for path elements */
481 p
= malloc(sizeof p
[0] * palloc
);
484 svgtiny_cleanup_state_local(&state
);
485 return svgtiny_OUT_OF_MEMORY
;
488 /* parse d and build path */
489 for (i
= 0; s
[i
]; i
++)
496 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
499 /* Ensure there is sufficient space for path elements */
500 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
502 if ((palloc - i) < NUM_ELEMENTS) { \
504 palloc = (palloc * 2) + (palloc / 2); \
505 tp = realloc(p, sizeof p[0] * palloc); \
509 svgtiny_cleanup_state_local(&state); \
510 return svgtiny_OUT_OF_MEMORY; \
517 /* moveto (M, m), lineto (L, l) (2 arguments) */
518 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
519 /*LOG(("moveto or lineto"));*/
520 if (*command
== 'M' || *command
== 'm')
521 plot_command
= svgtiny_PATH_MOVE
;
523 plot_command
= svgtiny_PATH_LINE
;
525 ALLOC_PATH_ELEMENTS(3);
526 p
[i
++] = plot_command
;
527 if ('a' <= *command
) {
531 if (plot_command
== svgtiny_PATH_MOVE
) {
535 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
537 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
540 plot_command
= svgtiny_PATH_LINE
;
541 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
543 /* closepath (Z, z) (no arguments) */
544 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
545 /*LOG(("closepath"));*/
546 ALLOC_PATH_ELEMENTS(1);
548 p
[i
++] = svgtiny_PATH_CLOSE
;
550 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
551 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
553 /* horizontal lineto (H, h) (1 argument) */
554 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
555 /*LOG(("horizontal lineto"));*/
557 ALLOC_PATH_ELEMENTS(3);
559 p
[i
++] = svgtiny_PATH_LINE
;
562 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
564 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
566 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
568 /* vertical lineto (V, v) (1 argument) */
569 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
570 /*LOG(("vertical lineto"));*/
572 ALLOC_PATH_ELEMENTS(3);
574 p
[i
++] = svgtiny_PATH_LINE
;
577 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
578 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
581 } while (sscanf(s
, "%f %n", &y
, &n
) == 1);
583 /* curveto (C, c) (6 arguments) */
584 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
585 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
586 /*LOG(("curveto"));*/
588 ALLOC_PATH_ELEMENTS(7);
590 p
[i
++] = svgtiny_PATH_BEZIER
;
591 if (*command
== 'c') {
601 p
[i
++] = last_cubic_x
= x2
;
602 p
[i
++] = last_cubic_y
= y2
;
603 p
[i
++] = last_quad_x
= last_x
= x
;
604 p
[i
++] = last_quad_y
= last_y
= y
;
606 } while (sscanf(s
, "%f %f %f %f %f %f %n",
607 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
609 /* shorthand/smooth curveto (S, s) (4 arguments) */
610 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
611 &x2
, &y2
, &x
, &y
, &n
) == 5) {
612 /*LOG(("shorthand/smooth curveto"));*/
614 ALLOC_PATH_ELEMENTS(7);
616 p
[i
++] = svgtiny_PATH_BEZIER
;
617 x1
= last_x
+ (last_x
- last_cubic_x
);
618 y1
= last_y
+ (last_y
- last_cubic_y
);
619 if (*command
== 's') {
627 p
[i
++] = last_cubic_x
= x2
;
628 p
[i
++] = last_cubic_y
= y2
;
629 p
[i
++] = last_quad_x
= last_x
= x
;
630 p
[i
++] = last_quad_y
= last_y
= y
;
632 } while (sscanf(s
, "%f %f %f %f %n",
633 &x2
, &y2
, &x
, &y
, &n
) == 4);
635 /* quadratic Bezier curveto (Q, q) (4 arguments) */
636 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
637 &x1
, &y1
, &x
, &y
, &n
) == 5) {
638 /*LOG(("quadratic Bezier curveto"));*/
640 ALLOC_PATH_ELEMENTS(7);
642 p
[i
++] = svgtiny_PATH_BEZIER
;
645 if (*command
== 'q') {
651 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
652 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
653 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
654 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
655 p
[i
++] = last_cubic_x
= last_x
= x
;
656 p
[i
++] = last_cubic_y
= last_y
= y
;
658 } while (sscanf(s
, "%f %f %f %f %n",
659 &x1
, &y1
, &x
, &y
, &n
) == 4);
661 /* shorthand/smooth quadratic Bezier curveto (T, t)
663 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
665 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
667 ALLOC_PATH_ELEMENTS(7);
669 p
[i
++] = svgtiny_PATH_BEZIER
;
670 x1
= last_x
+ (last_x
- last_quad_x
);
671 y1
= last_y
+ (last_y
- last_quad_y
);
674 if (*command
== 't') {
680 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
681 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
682 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
683 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
684 p
[i
++] = last_cubic_x
= last_x
= x
;
685 p
[i
++] = last_cubic_y
= last_y
= y
;
687 } while (sscanf(s
, "%f %f %n",
690 /* elliptical arc (A, a) (7 arguments) */
691 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
692 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
695 ALLOC_PATH_ELEMENTS(3);
697 p
[i
++] = svgtiny_PATH_LINE
;
698 if (*command
== 'a') {
702 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
704 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
707 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
708 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
712 /* fprintf(stderr, "parse failed at \"%s\"\n", s); */
720 /* no real segments in path */
722 svgtiny_cleanup_state_local(&state
);
726 /* resize path element array to not be over allocated */
730 /* try the resize, if it fails just continue to use the old
733 tp
= realloc(p
, sizeof p
[0] * i
);
739 err
= svgtiny_add_path(p
, i
, &state
);
741 svgtiny_cleanup_state_local(&state
);
748 * Parse a <rect> element node.
750 * http://www.w3.org/TR/SVG11/shapes#RectElement
753 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
754 struct svgtiny_parse_state state
)
757 float x
, y
, width
, height
;
760 svgtiny_setup_state_local(&state
);
762 svgtiny_parse_position_attributes(rect
, state
,
763 &x
, &y
, &width
, &height
);
764 svgtiny_parse_paint_attributes(rect
, &state
);
765 svgtiny_parse_transform_attributes(rect
, &state
);
767 p
= malloc(13 * sizeof p
[0]);
769 svgtiny_cleanup_state_local(&state
);
770 return svgtiny_OUT_OF_MEMORY
;
773 p
[0] = svgtiny_PATH_MOVE
;
776 p
[3] = svgtiny_PATH_LINE
;
779 p
[6] = svgtiny_PATH_LINE
;
782 p
[9] = svgtiny_PATH_LINE
;
785 p
[12] = svgtiny_PATH_CLOSE
;
787 err
= svgtiny_add_path(p
, 13, &state
);
789 svgtiny_cleanup_state_local(&state
);
796 * Parse a <circle> element node.
799 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
800 struct svgtiny_parse_state state
)
803 float x
= 0, y
= 0, r
= -1;
808 svgtiny_setup_state_local(&state
);
810 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
811 if (exc
!= DOM_NO_ERR
) {
812 svgtiny_cleanup_state_local(&state
);
813 return svgtiny_LIBDOM_ERROR
;
816 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
818 dom_string_unref(attr
);
820 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
821 if (exc
!= DOM_NO_ERR
) {
822 svgtiny_cleanup_state_local(&state
);
823 return svgtiny_LIBDOM_ERROR
;
826 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
828 dom_string_unref(attr
);
830 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
831 if (exc
!= DOM_NO_ERR
) {
832 svgtiny_cleanup_state_local(&state
);
833 return svgtiny_LIBDOM_ERROR
;
836 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
838 dom_string_unref(attr
);
840 svgtiny_parse_paint_attributes(circle
, &state
);
841 svgtiny_parse_transform_attributes(circle
, &state
);
844 state
.diagram
->error_line
= -1; /* circle->line; */
845 state
.diagram
->error_message
= "circle: r missing or negative";
846 svgtiny_cleanup_state_local(&state
);
847 return svgtiny_SVG_ERROR
;
850 svgtiny_cleanup_state_local(&state
);
854 p
= malloc(32 * sizeof p
[0]);
856 svgtiny_cleanup_state_local(&state
);
857 return svgtiny_OUT_OF_MEMORY
;
860 p
[0] = svgtiny_PATH_MOVE
;
863 p
[3] = svgtiny_PATH_BEZIER
;
865 p
[5] = y
+ r
* KAPPA
;
866 p
[6] = x
+ r
* KAPPA
;
870 p
[10] = svgtiny_PATH_BEZIER
;
871 p
[11] = x
- r
* KAPPA
;
874 p
[14] = y
+ r
* KAPPA
;
877 p
[17] = svgtiny_PATH_BEZIER
;
879 p
[19] = y
- r
* KAPPA
;
880 p
[20] = x
- r
* KAPPA
;
884 p
[24] = svgtiny_PATH_BEZIER
;
885 p
[25] = x
+ r
* KAPPA
;
888 p
[28] = y
- r
* KAPPA
;
891 p
[31] = svgtiny_PATH_CLOSE
;
893 err
= svgtiny_add_path(p
, 32, &state
);
895 svgtiny_cleanup_state_local(&state
);
902 * Parse an <ellipse> element node.
905 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
906 struct svgtiny_parse_state state
)
909 float x
= 0, y
= 0, rx
= -1, ry
= -1;
914 svgtiny_setup_state_local(&state
);
916 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
917 if (exc
!= DOM_NO_ERR
) {
918 svgtiny_cleanup_state_local(&state
);
919 return svgtiny_LIBDOM_ERROR
;
922 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
924 dom_string_unref(attr
);
926 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
927 if (exc
!= DOM_NO_ERR
) {
928 svgtiny_cleanup_state_local(&state
);
929 return svgtiny_LIBDOM_ERROR
;
932 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
934 dom_string_unref(attr
);
936 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
937 if (exc
!= DOM_NO_ERR
) {
938 svgtiny_cleanup_state_local(&state
);
939 return svgtiny_LIBDOM_ERROR
;
942 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
944 dom_string_unref(attr
);
946 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
947 if (exc
!= DOM_NO_ERR
) {
948 svgtiny_cleanup_state_local(&state
);
949 return svgtiny_LIBDOM_ERROR
;
952 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
954 dom_string_unref(attr
);
956 svgtiny_parse_paint_attributes(ellipse
, &state
);
957 svgtiny_parse_transform_attributes(ellipse
, &state
);
959 if (rx
< 0 || ry
< 0) {
960 state
.diagram
->error_line
= -1; /* ellipse->line; */
961 state
.diagram
->error_message
= "ellipse: rx or ry missing "
963 svgtiny_cleanup_state_local(&state
);
964 return svgtiny_SVG_ERROR
;
966 if (rx
== 0 || ry
== 0) {
967 svgtiny_cleanup_state_local(&state
);
971 p
= malloc(32 * sizeof p
[0]);
973 svgtiny_cleanup_state_local(&state
);
974 return svgtiny_OUT_OF_MEMORY
;
977 p
[0] = svgtiny_PATH_MOVE
;
980 p
[3] = svgtiny_PATH_BEZIER
;
982 p
[5] = y
+ ry
* KAPPA
;
983 p
[6] = x
+ rx
* KAPPA
;
987 p
[10] = svgtiny_PATH_BEZIER
;
988 p
[11] = x
- rx
* KAPPA
;
991 p
[14] = y
+ ry
* KAPPA
;
994 p
[17] = svgtiny_PATH_BEZIER
;
996 p
[19] = y
- ry
* KAPPA
;
997 p
[20] = x
- rx
* KAPPA
;
1001 p
[24] = svgtiny_PATH_BEZIER
;
1002 p
[25] = x
+ rx
* KAPPA
;
1005 p
[28] = y
- ry
* KAPPA
;
1008 p
[31] = svgtiny_PATH_CLOSE
;
1010 err
= svgtiny_add_path(p
, 32, &state
);
1012 svgtiny_cleanup_state_local(&state
);
1019 * Parse a <line> element node.
1022 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1023 struct svgtiny_parse_state state
)
1026 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1031 svgtiny_setup_state_local(&state
);
1033 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1034 if (exc
!= DOM_NO_ERR
) {
1035 svgtiny_cleanup_state_local(&state
);
1036 return svgtiny_LIBDOM_ERROR
;
1039 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1041 dom_string_unref(attr
);
1043 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1044 if (exc
!= DOM_NO_ERR
) {
1045 svgtiny_cleanup_state_local(&state
);
1046 return svgtiny_LIBDOM_ERROR
;
1049 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1051 dom_string_unref(attr
);
1053 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1054 if (exc
!= DOM_NO_ERR
) {
1055 svgtiny_cleanup_state_local(&state
);
1056 return svgtiny_LIBDOM_ERROR
;
1059 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1061 dom_string_unref(attr
);
1063 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1064 if (exc
!= DOM_NO_ERR
) {
1065 svgtiny_cleanup_state_local(&state
);
1066 return svgtiny_LIBDOM_ERROR
;
1069 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1071 dom_string_unref(attr
);
1073 svgtiny_parse_paint_attributes(line
, &state
);
1074 svgtiny_parse_transform_attributes(line
, &state
);
1076 p
= malloc(7 * sizeof p
[0]);
1078 svgtiny_cleanup_state_local(&state
);
1079 return svgtiny_OUT_OF_MEMORY
;
1082 p
[0] = svgtiny_PATH_MOVE
;
1085 p
[3] = svgtiny_PATH_LINE
;
1088 p
[6] = svgtiny_PATH_CLOSE
;
1090 err
= svgtiny_add_path(p
, 7, &state
);
1092 svgtiny_cleanup_state_local(&state
);
1099 * Parse a <polyline> or <polygon> element node.
1101 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1102 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1105 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1106 struct svgtiny_parse_state state
, bool polygon
)
1109 dom_string
*points_str
;
1115 svgtiny_setup_state_local(&state
);
1117 svgtiny_parse_paint_attributes(poly
, &state
);
1118 svgtiny_parse_transform_attributes(poly
, &state
);
1120 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1122 if (exc
!= DOM_NO_ERR
) {
1123 svgtiny_cleanup_state_local(&state
);
1124 return svgtiny_LIBDOM_ERROR
;
1127 if (points_str
== NULL
) {
1128 state
.diagram
->error_line
= -1; /* poly->line; */
1129 state
.diagram
->error_message
=
1130 "polyline/polygon: missing points attribute";
1131 svgtiny_cleanup_state_local(&state
);
1132 return svgtiny_SVG_ERROR
;
1135 s
= points
= strndup(dom_string_data(points_str
),
1136 dom_string_byte_length(points_str
));
1137 dom_string_unref(points_str
);
1138 /* read points attribute */
1140 svgtiny_cleanup_state_local(&state
);
1141 return svgtiny_OUT_OF_MEMORY
;
1143 /* allocate space for path: it will never have more elements than s */
1144 p
= malloc(sizeof p
[0] * strlen(s
));
1147 svgtiny_cleanup_state_local(&state
);
1148 return svgtiny_OUT_OF_MEMORY
;
1151 /* parse s and build path */
1152 for (i
= 0; s
[i
]; i
++)
1160 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1162 p
[i
++] = svgtiny_PATH_MOVE
;
1164 p
[i
++] = svgtiny_PATH_LINE
;
1173 p
[i
++] = svgtiny_PATH_CLOSE
;
1177 err
= svgtiny_add_path(p
, i
, &state
);
1179 svgtiny_cleanup_state_local(&state
);
1186 * Parse a <text> or <tspan> element node.
1189 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1190 struct svgtiny_parse_state state
)
1192 float x
, y
, width
, height
;
1197 svgtiny_setup_state_local(&state
);
1199 svgtiny_parse_position_attributes(text
, state
,
1200 &x
, &y
, &width
, &height
);
1201 svgtiny_parse_font_attributes(text
, &state
);
1202 svgtiny_parse_transform_attributes(text
, &state
);
1204 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1205 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1206 /* state.ctm.e = px - state.origin_x; */
1207 /* state.ctm.f = py - state.origin_y; */
1209 /*struct css_style style = state.style;
1210 style.font_size.value.length.value *= state.ctm.a;*/
1212 exc
= dom_node_get_first_child(text
, &child
);
1213 if (exc
!= DOM_NO_ERR
) {
1214 return svgtiny_LIBDOM_ERROR
;
1215 svgtiny_cleanup_state_local(&state
);
1217 while (child
!= NULL
) {
1219 dom_node_type nodetype
;
1220 svgtiny_code code
= svgtiny_OK
;
1222 exc
= dom_node_get_node_type(child
, &nodetype
);
1223 if (exc
!= DOM_NO_ERR
) {
1224 dom_node_unref(child
);
1225 svgtiny_cleanup_state_local(&state
);
1226 return svgtiny_LIBDOM_ERROR
;
1228 if (nodetype
== DOM_ELEMENT_NODE
) {
1229 dom_string
*nodename
;
1230 exc
= dom_node_get_node_name(child
, &nodename
);
1231 if (exc
!= DOM_NO_ERR
) {
1232 dom_node_unref(child
);
1233 svgtiny_cleanup_state_local(&state
);
1234 return svgtiny_LIBDOM_ERROR
;
1236 if (dom_string_caseless_isequal(nodename
,
1237 state
.interned_tspan
))
1238 code
= svgtiny_parse_text((dom_element
*)child
,
1240 dom_string_unref(nodename
);
1241 } else if (nodetype
== DOM_TEXT_NODE
) {
1242 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1243 dom_string
*content
;
1244 if (shape
== NULL
) {
1245 dom_node_unref(child
);
1246 svgtiny_cleanup_state_local(&state
);
1247 return svgtiny_OUT_OF_MEMORY
;
1249 exc
= dom_text_get_whole_text(child
, &content
);
1250 if (exc
!= DOM_NO_ERR
) {
1251 dom_node_unref(child
);
1252 svgtiny_cleanup_state_local(&state
);
1253 return svgtiny_LIBDOM_ERROR
;
1255 if (content
!= NULL
) {
1256 shape
->text
= strndup(dom_string_data(content
),
1257 dom_string_byte_length(content
));
1258 dom_string_unref(content
);
1260 shape
->text
= strdup("");
1264 state
.diagram
->shape_count
++;
1267 if (code
!= svgtiny_OK
) {
1268 dom_node_unref(child
);
1269 svgtiny_cleanup_state_local(&state
);
1272 exc
= dom_node_get_next_sibling(child
, &next
);
1273 dom_node_unref(child
);
1274 if (exc
!= DOM_NO_ERR
) {
1275 svgtiny_cleanup_state_local(&state
);
1276 return svgtiny_LIBDOM_ERROR
;
1281 svgtiny_cleanup_state_local(&state
);
1288 * Parse x, y, width, and height attributes, if present.
1291 void svgtiny_parse_position_attributes(dom_element
*node
,
1292 const struct svgtiny_parse_state state
,
1293 float *x
, float *y
, float *width
, float *height
)
1300 *width
= state
.viewport_width
;
1301 *height
= state
.viewport_height
;
1303 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1304 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1305 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1306 dom_string_unref(attr
);
1309 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1310 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1311 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1312 dom_string_unref(attr
);
1315 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
1316 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1317 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
1319 dom_string_unref(attr
);
1322 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
1323 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1324 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
1326 dom_string_unref(attr
);
1332 * Parse a length as a number of pixels.
1335 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1336 const struct svgtiny_parse_state state
)
1338 int num_length
= strspn(s
, "0123456789+-.");
1339 const char *unit
= s
+ num_length
;
1340 float n
= atof((const char *) s
);
1341 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1347 } else if (unit
[0] == '%') {
1348 return n
/ 100.0 * viewport_size
;
1349 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
1350 return n
* font_size
;
1351 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
1352 return n
/ 2.0 * font_size
;
1353 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
1355 } else if (unit
[0] == 'p' && unit
[1] == 't') {
1357 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
1359 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
1360 return n
* 3.543307;
1361 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
1362 return n
* 35.43307;
1363 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
1370 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
1371 const struct svgtiny_parse_state state
)
1373 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1374 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
1380 * Parse paint attributes, if present.
1383 void svgtiny_parse_paint_attributes(dom_element
*node
,
1384 struct svgtiny_parse_state
*state
)
1389 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
1390 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1391 svgtiny_parse_color(attr
, &state
->fill
, &state
->fill_grad
, state
);
1392 dom_string_unref(attr
);
1395 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
1396 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1397 svgtiny_parse_color(attr
, &state
->stroke
, &state
->stroke_grad
, state
);
1398 dom_string_unref(attr
);
1401 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
1402 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1403 state
->stroke_width
= svgtiny_parse_length(attr
,
1404 state
->viewport_width
, *state
);
1405 dom_string_unref(attr
);
1408 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
1409 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1410 char *style
= strndup(dom_string_data(attr
),
1411 dom_string_byte_length(attr
));
1414 if ((s
= strstr(style
, "fill:"))) {
1418 value
= strndup(s
, strcspn(s
, "; "));
1419 _svgtiny_parse_color(value
, &state
->fill
, &state
->fill_grad
, state
);
1422 if ((s
= strstr(style
, "stroke:"))) {
1426 value
= strndup(s
, strcspn(s
, "; "));
1427 _svgtiny_parse_color(value
, &state
->stroke
, &state
->stroke_grad
, state
);
1430 if ((s
= strstr(style
, "stroke-width:"))) {
1434 value
= strndup(s
, strcspn(s
, "; "));
1435 state
->stroke_width
= _svgtiny_parse_length(value
,
1436 state
->viewport_width
, *state
);
1440 dom_string_unref(attr
);
1449 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
1450 struct svgtiny_parse_state_gradient
*grad
,
1451 struct svgtiny_parse_state
*state
)
1453 unsigned int r
, g
, b
;
1455 size_t len
= strlen(s
);
1456 char *id
= 0, *rparen
;
1458 if (len
== 4 && s
[0] == '#') {
1459 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
1460 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1462 } else if (len
== 7 && s
[0] == '#') {
1463 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1464 *c
= svgtiny_RGB(r
, g
, b
);
1466 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1467 s
[3] == '(' && s
[len
- 1] == ')') {
1468 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1469 *c
= svgtiny_RGB(r
, g
, b
);
1470 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1474 *c
= svgtiny_RGB(r
, g
, b
);
1477 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1478 *c
= svgtiny_TRANSPARENT
;
1480 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1483 *c
= svgtiny_RGB(0, 0, 0);
1484 } else if (s
[4] == '#') {
1488 rparen
= strchr(id
, ')');
1491 svgtiny_find_gradient(id
, grad
, state
);
1493 if (grad
->linear_gradient_stop_count
== 0)
1494 *c
= svgtiny_TRANSPARENT
;
1495 else if (grad
->linear_gradient_stop_count
== 1)
1496 *c
= grad
->gradient_stop
[0].color
;
1498 *c
= svgtiny_LINEAR_GRADIENT
;
1502 const struct svgtiny_named_color
*named_color
;
1503 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1505 *c
= named_color
->color
;
1509 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
1510 struct svgtiny_parse_state_gradient
*grad
,
1511 struct svgtiny_parse_state
*state
)
1514 _svgtiny_parse_color(dom_string_data(s
), c
, grad
, state
);
1515 dom_string_unref(s
);
1519 * Parse font attributes, if present.
1522 void svgtiny_parse_font_attributes(dom_element
*node
,
1523 struct svgtiny_parse_state
*state
)
1525 /* TODO: Implement this, it never used to be */
1528 #ifdef WRITTEN_THIS_PROPERLY
1529 const xmlAttr
*attr
;
1533 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1534 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1535 /*if (css_parse_length(
1536 (const char *) attr->children->content,
1537 &state->style.font_size.value.length,
1539 state->style.font_size.size =
1540 CSS_FONT_SIZE_LENGTH;
1549 * Parse transform attributes, if present.
1551 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1554 void svgtiny_parse_transform_attributes(dom_element
*node
,
1555 struct svgtiny_parse_state
*state
)
1561 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
1563 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1564 transform
= strndup(dom_string_data(attr
),
1565 dom_string_byte_length(attr
));
1566 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1567 &state
->ctm
.c
, &state
->ctm
.d
,
1568 &state
->ctm
.e
, &state
->ctm
.f
);
1570 dom_string_unref(attr
);
1576 * Parse a transform string.
1579 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1580 float *mc
, float *md
, float *me
, float *mf
)
1582 float a
, b
, c
, d
, e
, f
;
1583 float za
, zb
, zc
, zd
, ze
, zf
;
1588 for (i
= 0; s
[i
]; i
++)
1597 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
1598 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
1600 else if ((sscanf(s
, " translate (%f %f ) %n",
1601 &e
, &f
, &n
) == 2) && (n
> 0))
1603 else if ((sscanf(s
, " translate (%f ) %n",
1604 &e
, &n
) == 1) && (n
> 0))
1606 else if ((sscanf(s
, " scale (%f %f ) %n",
1607 &a
, &d
, &n
) == 2) && (n
> 0))
1609 else if ((sscanf(s
, " scale (%f ) %n",
1610 &a
, &n
) == 1) && (n
> 0))
1612 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
1613 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
1614 angle
= angle
/ 180 * M_PI
;
1619 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1620 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1621 } else if ((sscanf(s
, " rotate (%f ) %n",
1622 &angle
, &n
) == 1) && (n
> 0)) {
1623 angle
= angle
/ 180 * M_PI
;
1628 } else if ((sscanf(s
, " skewX (%f ) %n",
1629 &angle
, &n
) == 1) && (n
> 0)) {
1630 angle
= angle
/ 180 * M_PI
;
1632 } else if ((sscanf(s
, " skewY (%f ) %n",
1633 &angle
, &n
) == 1) && (n
> 0)) {
1634 angle
= angle
/ 180 * M_PI
;
1638 za
= *ma
* a
+ *mc
* b
;
1639 zb
= *mb
* a
+ *md
* b
;
1640 zc
= *ma
* c
+ *mc
* d
;
1641 zd
= *mb
* c
+ *md
* d
;
1642 ze
= *ma
* e
+ *mc
* f
+ *me
;
1643 zf
= *mb
* e
+ *md
* f
+ *mf
;
1656 * Add a path to the svgtiny_diagram.
1659 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1660 struct svgtiny_parse_state
*state
)
1662 struct svgtiny_shape
*shape
;
1664 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1665 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1667 svgtiny_transform_path(p
, n
, state
);
1669 shape
= svgtiny_add_shape(state
);
1672 return svgtiny_OUT_OF_MEMORY
;
1675 shape
->path_length
= n
;
1676 state
->diagram
->shape_count
++;
1683 * Add a svgtiny_shape to the svgtiny_diagram.
1686 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1688 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1689 (state
->diagram
->shape_count
+ 1) *
1690 sizeof (state
->diagram
->shape
[0]));
1693 state
->diagram
->shape
= shape
;
1695 shape
+= state
->diagram
->shape_count
;
1697 shape
->path_length
= 0;
1699 shape
->fill
= state
->fill
;
1700 shape
->stroke
= state
->stroke
;
1701 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1702 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1703 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1704 shape
->stroke_width
= 1;
1711 * Apply the current transformation matrix to a path.
1714 void svgtiny_transform_path(float *p
, unsigned int n
,
1715 struct svgtiny_parse_state
*state
)
1719 for (j
= 0; j
!= n
; ) {
1720 unsigned int points
= 0;
1722 switch ((int) p
[j
]) {
1723 case svgtiny_PATH_MOVE
:
1724 case svgtiny_PATH_LINE
:
1727 case svgtiny_PATH_CLOSE
:
1730 case svgtiny_PATH_BEZIER
:
1737 for (k
= 0; k
!= points
; k
++) {
1738 float x0
= p
[j
], y0
= p
[j
+ 1];
1739 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1741 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1752 * Free all memory used by a diagram.
1755 void svgtiny_free(struct svgtiny_diagram
*svg
)
1760 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1761 free(svg
->shape
[i
].path
);
1762 free(svg
->shape
[i
].text
);
1770 #ifndef HAVE_STRNDUP
1771 char *svgtiny_strndup(const char *s
, size_t n
)
1776 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1779 s2
= malloc(len
+ 1);