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_gradient
*grad
,
58 struct svgtiny_parse_state
*state
);
61 * Call this to ref the strings in a gradient state.
63 static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient
*grad
)
65 if (grad
->gradient_x1
!= NULL
) {
66 dom_string_ref(grad
->gradient_x1
);
68 if (grad
->gradient_y1
!= NULL
) {
69 dom_string_ref(grad
->gradient_y1
);
71 if (grad
->gradient_x2
!= NULL
) {
72 dom_string_ref(grad
->gradient_x2
);
74 if (grad
->gradient_y2
!= NULL
) {
75 dom_string_ref(grad
->gradient_y2
);
80 * Call this to clean up the strings in a gradient state.
82 static void svgtiny_grad_string_cleanup(
83 struct svgtiny_parse_state_gradient
*grad
)
85 if (grad
->gradient_x1
!= NULL
) {
86 dom_string_unref(grad
->gradient_x1
);
87 grad
->gradient_x1
= NULL
;
89 if (grad
->gradient_y1
!= NULL
) {
90 dom_string_unref(grad
->gradient_y1
);
91 grad
->gradient_y1
= NULL
;
93 if (grad
->gradient_x2
!= NULL
) {
94 dom_string_unref(grad
->gradient_x2
);
95 grad
->gradient_x2
= NULL
;
97 if (grad
->gradient_y2
!= NULL
) {
98 dom_string_unref(grad
->gradient_y2
);
99 grad
->gradient_y2
= NULL
;
104 * Set the local externally-stored parts of a parse state.
105 * Call this in functions that made a new state on the stack.
106 * Doesn't make own copy of global state, such as the interned string list.
108 static void svgtiny_setup_state_local(struct svgtiny_parse_state
*state
)
110 svgtiny_grad_string_ref(&(state
->fill_grad
));
111 svgtiny_grad_string_ref(&(state
->stroke_grad
));
115 * Cleanup the local externally-stored parts of a parse state.
116 * Call this in functions that made a new state on the stack.
117 * Doesn't cleanup global state, such as the interned string list.
119 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state
*state
)
121 svgtiny_grad_string_cleanup(&(state
->fill_grad
));
122 svgtiny_grad_string_cleanup(&(state
->stroke_grad
));
127 * Create a new svgtiny_diagram structure.
130 struct svgtiny_diagram
*svgtiny_create(void)
132 struct svgtiny_diagram
*diagram
;
134 diagram
= calloc(sizeof(*diagram
), 1);
143 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
151 * Parse a block of memory into a svgtiny_diagram.
154 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
155 const char *buffer
, size_t size
, const char *url
,
156 int viewport_width
, int viewport_height
)
158 dom_document
*document
;
160 dom_xml_parser
*parser
;
163 dom_string
*svg_name
;
164 lwc_string
*svg_name_lwc
;
165 struct svgtiny_parse_state state
;
166 float x
, y
, width
, height
;
175 parser
= dom_xml_parser_create(NULL
, NULL
,
176 ignore_msg
, NULL
, &document
);
179 return svgtiny_LIBDOM_ERROR
;
181 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
182 if (err
!= DOM_XML_OK
) {
183 dom_node_unref(document
);
184 dom_xml_parser_destroy(parser
);
185 return svgtiny_LIBDOM_ERROR
;
188 err
= dom_xml_parser_completed(parser
);
189 if (err
!= DOM_XML_OK
) {
190 dom_node_unref(document
);
191 dom_xml_parser_destroy(parser
);
192 return svgtiny_LIBDOM_ERROR
;
195 /* We're done parsing, drop the parser.
196 * We now own the document entirely.
198 dom_xml_parser_destroy(parser
);
200 /* find root <svg> element */
201 exc
= dom_document_get_document_element(document
, &svg
);
202 if (exc
!= DOM_NO_ERR
) {
203 dom_node_unref(document
);
204 return svgtiny_LIBDOM_ERROR
;
207 /* no root svg element */
208 dom_node_unref(document
);
209 return svgtiny_SVG_ERROR
;
212 exc
= dom_node_get_node_name(svg
, &svg_name
);
213 if (exc
!= DOM_NO_ERR
) {
215 dom_node_unref(document
);
216 return svgtiny_LIBDOM_ERROR
;
218 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
219 &svg_name_lwc
) != lwc_error_ok
) {
220 dom_string_unref(svg_name
);
222 dom_node_unref(document
);
223 return svgtiny_LIBDOM_ERROR
;
225 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
226 lwc_string_unref(svg_name_lwc
);
227 dom_string_unref(svg_name
);
229 dom_node_unref(document
);
230 return svgtiny_NOT_SVG
;
233 lwc_string_unref(svg_name_lwc
);
234 dom_string_unref(svg_name
);
236 /* get graphic dimensions */
237 memset(&state
, 0, sizeof(state
));
238 state
.diagram
= diagram
;
239 state
.document
= document
;
240 state
.viewport_width
= viewport_width
;
241 state
.viewport_height
= viewport_height
;
243 #define SVGTINY_STRING_ACTION2(s,n) \
244 if (dom_string_create_interned((const uint8_t *) #n, \
245 strlen(#n), &state.interned_##s) \
247 code = svgtiny_LIBDOM_ERROR; \
250 #include "svgtiny_strings.h"
251 #undef SVGTINY_STRING_ACTION2
253 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
254 diagram
->width
= width
;
255 diagram
->height
= height
;
257 /* set up parsing state */
258 state
.viewport_width
= width
;
259 state
.viewport_height
= height
;
260 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
263 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
264 state
.ctm
.e
= 0; /*x;*/
265 state
.ctm
.f
= 0; /*y;*/
266 /*state.style = css_base_style;
267 state.style.font_size.value.length.value = option_font_size * 0.1;*/
268 state
.fill
= 0x000000;
269 state
.stroke
= svgtiny_TRANSPARENT
;
270 state
.stroke_width
= 1;
273 code
= svgtiny_parse_svg(svg
, state
);
276 dom_node_unref(document
);
279 svgtiny_cleanup_state_local(&state
);
280 #define SVGTINY_STRING_ACTION2(s,n) \
281 if (state.interned_##s != NULL) \
282 dom_string_unref(state.interned_##s);
283 #include "svgtiny_strings.h"
284 #undef SVGTINY_STRING_ACTION2
290 * Parse a <svg> or <g> element node.
293 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
294 struct svgtiny_parse_state state
)
296 float x
, y
, width
, height
;
297 dom_string
*view_box
;
301 svgtiny_setup_state_local(&state
);
303 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
304 svgtiny_parse_paint_attributes(svg
, &state
);
305 svgtiny_parse_font_attributes(svg
, &state
);
307 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
309 if (exc
!= DOM_NO_ERR
) {
310 svgtiny_cleanup_state_local(&state
);
311 return svgtiny_LIBDOM_ERROR
;
315 char *s
= strndup(dom_string_data(view_box
),
316 dom_string_byte_length(view_box
));
317 float min_x
, min_y
, vwidth
, vheight
;
318 if (sscanf(s
, "%f,%f,%f,%f",
319 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
320 sscanf(s
, "%f %f %f %f",
321 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
322 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
323 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
324 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
325 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
328 dom_string_unref(view_box
);
331 svgtiny_parse_transform_attributes(svg
, &state
);
333 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
334 if (exc
!= DOM_NO_ERR
) {
335 svgtiny_cleanup_state_local(&state
);
336 return svgtiny_LIBDOM_ERROR
;
338 while (child
!= NULL
) {
340 dom_node_type nodetype
;
341 svgtiny_code code
= svgtiny_OK
;
343 exc
= dom_node_get_node_type(child
, &nodetype
);
344 if (exc
!= DOM_NO_ERR
) {
345 dom_node_unref(child
);
346 return svgtiny_LIBDOM_ERROR
;
348 if (nodetype
== DOM_ELEMENT_NODE
) {
349 dom_string
*nodename
;
350 exc
= dom_node_get_node_name(child
, &nodename
);
351 if (exc
!= DOM_NO_ERR
) {
352 dom_node_unref(child
);
353 svgtiny_cleanup_state_local(&state
);
354 return svgtiny_LIBDOM_ERROR
;
356 if (dom_string_caseless_isequal(state
.interned_svg
,
358 code
= svgtiny_parse_svg(child
, state
);
359 else if (dom_string_caseless_isequal(state
.interned_g
,
361 code
= svgtiny_parse_svg(child
, state
);
362 else if (dom_string_caseless_isequal(state
.interned_a
,
364 code
= svgtiny_parse_svg(child
, state
);
365 else if (dom_string_caseless_isequal(state
.interned_path
,
367 code
= svgtiny_parse_path(child
, state
);
368 else if (dom_string_caseless_isequal(state
.interned_rect
,
370 code
= svgtiny_parse_rect(child
, state
);
371 else if (dom_string_caseless_isequal(state
.interned_circle
,
373 code
= svgtiny_parse_circle(child
, state
);
374 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
376 code
= svgtiny_parse_ellipse(child
, state
);
377 else if (dom_string_caseless_isequal(state
.interned_line
,
379 code
= svgtiny_parse_line(child
, state
);
380 else if (dom_string_caseless_isequal(state
.interned_polyline
,
382 code
= svgtiny_parse_poly(child
, state
, false);
383 else if (dom_string_caseless_isequal(state
.interned_polygon
,
385 code
= svgtiny_parse_poly(child
, state
, true);
386 else if (dom_string_caseless_isequal(state
.interned_text
,
388 code
= svgtiny_parse_text(child
, state
);
389 dom_string_unref(nodename
);
391 if (code
!= svgtiny_OK
) {
392 dom_node_unref(child
);
393 svgtiny_cleanup_state_local(&state
);
396 exc
= dom_node_get_next_sibling(child
,
397 (dom_node
**) (void *) &next
);
398 dom_node_unref(child
);
399 if (exc
!= DOM_NO_ERR
) {
400 svgtiny_cleanup_state_local(&state
);
401 return svgtiny_LIBDOM_ERROR
;
406 svgtiny_cleanup_state_local(&state
);
413 * Parse a <path> element node.
415 * http://www.w3.org/TR/SVG11/paths#PathElement
418 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
419 struct svgtiny_parse_state state
)
422 dom_string
*path_d_str
;
425 float *p
; /* path elemets */
426 unsigned int palloc
; /* number of path elements allocated */
428 float last_x
= 0, last_y
= 0;
429 float last_cubic_x
= 0, last_cubic_y
= 0;
430 float last_quad_x
= 0, last_quad_y
= 0;
431 float subpath_first_x
= 0, subpath_first_y
= 0;
433 svgtiny_setup_state_local(&state
);
435 svgtiny_parse_paint_attributes(path
, &state
);
436 svgtiny_parse_transform_attributes(path
, &state
);
438 /* read d attribute */
439 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
440 if (exc
!= DOM_NO_ERR
) {
441 state
.diagram
->error_line
= -1; /* path->line; */
442 state
.diagram
->error_message
= "path: error retrieving d attribute";
443 svgtiny_cleanup_state_local(&state
);
444 return svgtiny_SVG_ERROR
;
447 if (path_d_str
== NULL
) {
448 state
.diagram
->error_line
= -1; /* path->line; */
449 state
.diagram
->error_message
= "path: missing d attribute";
450 svgtiny_cleanup_state_local(&state
);
451 return svgtiny_SVG_ERROR
;
454 /* empty path is permitted it just disables the path */
455 palloc
= dom_string_byte_length(path_d_str
);
457 svgtiny_cleanup_state_local(&state
);
461 /* local copy of the path data allowing in-place modification */
462 s
= path_d
= strndup(dom_string_data(path_d_str
), palloc
);
463 dom_string_unref(path_d_str
);
465 svgtiny_cleanup_state_local(&state
);
466 return svgtiny_OUT_OF_MEMORY
;
469 /* ensure path element allocation is sensibly bounded */
472 } else if (palloc
> 64) {
476 /* allocate initial space for path elements */
477 p
= malloc(sizeof p
[0] * palloc
);
480 svgtiny_cleanup_state_local(&state
);
481 return svgtiny_OUT_OF_MEMORY
;
484 /* parse d and build path */
485 for (i
= 0; s
[i
]; i
++)
492 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
495 /* Ensure there is sufficient space for path elements */
496 #define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS) \
498 if ((palloc - i) < NUM_ELEMENTS) { \
500 palloc = (palloc * 2) + (palloc / 2); \
501 tp = realloc(p, sizeof p[0] * palloc); \
505 svgtiny_cleanup_state_local(&state); \
506 return svgtiny_OUT_OF_MEMORY; \
513 /* moveto (M, m), lineto (L, l) (2 arguments) */
514 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
515 /*LOG(("moveto or lineto"));*/
516 if (*command
== 'M' || *command
== 'm')
517 plot_command
= svgtiny_PATH_MOVE
;
519 plot_command
= svgtiny_PATH_LINE
;
521 ALLOC_PATH_ELEMENTS(3);
522 p
[i
++] = plot_command
;
523 if ('a' <= *command
) {
527 if (plot_command
== svgtiny_PATH_MOVE
) {
531 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
533 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
536 plot_command
= svgtiny_PATH_LINE
;
537 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
539 /* closepath (Z, z) (no arguments) */
540 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
541 /*LOG(("closepath"));*/
542 ALLOC_PATH_ELEMENTS(1);
544 p
[i
++] = svgtiny_PATH_CLOSE
;
546 last_cubic_x
= last_quad_x
= last_x
= subpath_first_x
;
547 last_cubic_y
= last_quad_y
= last_y
= subpath_first_y
;
549 /* horizontal lineto (H, h) (1 argument) */
550 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
551 /*LOG(("horizontal lineto"));*/
553 ALLOC_PATH_ELEMENTS(3);
555 p
[i
++] = svgtiny_PATH_LINE
;
558 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
560 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
562 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
564 /* vertical lineto (V, v) (1 argument) */
565 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
566 /*LOG(("vertical lineto"));*/
568 ALLOC_PATH_ELEMENTS(3);
570 p
[i
++] = svgtiny_PATH_LINE
;
573 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
574 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
577 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
579 /* curveto (C, c) (6 arguments) */
580 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
581 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
582 /*LOG(("curveto"));*/
584 ALLOC_PATH_ELEMENTS(7);
586 p
[i
++] = svgtiny_PATH_BEZIER
;
587 if (*command
== 'c') {
597 p
[i
++] = last_cubic_x
= x2
;
598 p
[i
++] = last_cubic_y
= y2
;
599 p
[i
++] = last_quad_x
= last_x
= x
;
600 p
[i
++] = last_quad_y
= last_y
= y
;
602 } while (sscanf(s
, "%f %f %f %f %f %f %n",
603 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
605 /* shorthand/smooth curveto (S, s) (4 arguments) */
606 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
607 &x2
, &y2
, &x
, &y
, &n
) == 5) {
608 /*LOG(("shorthand/smooth curveto"));*/
610 ALLOC_PATH_ELEMENTS(7);
612 p
[i
++] = svgtiny_PATH_BEZIER
;
613 x1
= last_x
+ (last_x
- last_cubic_x
);
614 y1
= last_y
+ (last_y
- last_cubic_y
);
615 if (*command
== 's') {
623 p
[i
++] = last_cubic_x
= x2
;
624 p
[i
++] = last_cubic_y
= y2
;
625 p
[i
++] = last_quad_x
= last_x
= x
;
626 p
[i
++] = last_quad_y
= last_y
= y
;
628 } while (sscanf(s
, "%f %f %f %f %n",
629 &x2
, &y2
, &x
, &y
, &n
) == 4);
631 /* quadratic Bezier curveto (Q, q) (4 arguments) */
632 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
633 &x1
, &y1
, &x
, &y
, &n
) == 5) {
634 /*LOG(("quadratic Bezier curveto"));*/
636 ALLOC_PATH_ELEMENTS(7);
638 p
[i
++] = svgtiny_PATH_BEZIER
;
641 if (*command
== 'q') {
647 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
648 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
649 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
650 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
651 p
[i
++] = last_cubic_x
= last_x
= x
;
652 p
[i
++] = last_cubic_y
= last_y
= y
;
654 } while (sscanf(s
, "%f %f %f %f %n",
655 &x1
, &y1
, &x
, &y
, &n
) == 4);
657 /* shorthand/smooth quadratic Bezier curveto (T, t)
659 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
661 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
663 ALLOC_PATH_ELEMENTS(7);
665 p
[i
++] = svgtiny_PATH_BEZIER
;
666 x1
= last_x
+ (last_x
- last_quad_x
);
667 y1
= last_y
+ (last_y
- last_quad_y
);
670 if (*command
== 't') {
676 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
677 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
678 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
679 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
680 p
[i
++] = last_cubic_x
= last_x
= x
;
681 p
[i
++] = last_cubic_y
= last_y
= y
;
683 } while (sscanf(s
, "%f %f %n",
686 /* elliptical arc (A, a) (7 arguments) */
687 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
688 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
691 ALLOC_PATH_ELEMENTS(3);
693 p
[i
++] = svgtiny_PATH_LINE
;
694 if (*command
== 'a') {
698 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
700 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
703 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
704 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
708 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
716 /* no real segments in path */
718 svgtiny_cleanup_state_local(&state
);
722 /* resize path element array to not be over allocated */
726 /* try the resize, if it fails just continue to use the old
729 tp
= realloc(p
, sizeof p
[0] * i
);
735 err
= svgtiny_add_path(p
, i
, &state
);
737 svgtiny_cleanup_state_local(&state
);
744 * Parse a <rect> element node.
746 * http://www.w3.org/TR/SVG11/shapes#RectElement
749 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
750 struct svgtiny_parse_state state
)
753 float x
, y
, width
, height
;
756 svgtiny_setup_state_local(&state
);
758 svgtiny_parse_position_attributes(rect
, state
,
759 &x
, &y
, &width
, &height
);
760 svgtiny_parse_paint_attributes(rect
, &state
);
761 svgtiny_parse_transform_attributes(rect
, &state
);
763 p
= malloc(13 * sizeof p
[0]);
765 svgtiny_cleanup_state_local(&state
);
766 return svgtiny_OUT_OF_MEMORY
;
769 p
[0] = svgtiny_PATH_MOVE
;
772 p
[3] = svgtiny_PATH_LINE
;
775 p
[6] = svgtiny_PATH_LINE
;
778 p
[9] = svgtiny_PATH_LINE
;
781 p
[12] = svgtiny_PATH_CLOSE
;
783 err
= svgtiny_add_path(p
, 13, &state
);
785 svgtiny_cleanup_state_local(&state
);
792 * Parse a <circle> element node.
795 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
796 struct svgtiny_parse_state state
)
799 float x
= 0, y
= 0, r
= -1;
804 svgtiny_setup_state_local(&state
);
806 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
807 if (exc
!= DOM_NO_ERR
) {
808 svgtiny_cleanup_state_local(&state
);
809 return svgtiny_LIBDOM_ERROR
;
812 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
814 dom_string_unref(attr
);
816 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
817 if (exc
!= DOM_NO_ERR
) {
818 svgtiny_cleanup_state_local(&state
);
819 return svgtiny_LIBDOM_ERROR
;
822 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
824 dom_string_unref(attr
);
826 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
827 if (exc
!= DOM_NO_ERR
) {
828 svgtiny_cleanup_state_local(&state
);
829 return svgtiny_LIBDOM_ERROR
;
832 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
834 dom_string_unref(attr
);
836 svgtiny_parse_paint_attributes(circle
, &state
);
837 svgtiny_parse_transform_attributes(circle
, &state
);
840 state
.diagram
->error_line
= -1; /* circle->line; */
841 state
.diagram
->error_message
= "circle: r missing or negative";
842 svgtiny_cleanup_state_local(&state
);
843 return svgtiny_SVG_ERROR
;
846 svgtiny_cleanup_state_local(&state
);
850 p
= malloc(32 * sizeof p
[0]);
852 svgtiny_cleanup_state_local(&state
);
853 return svgtiny_OUT_OF_MEMORY
;
856 p
[0] = svgtiny_PATH_MOVE
;
859 p
[3] = svgtiny_PATH_BEZIER
;
861 p
[5] = y
+ r
* KAPPA
;
862 p
[6] = x
+ r
* KAPPA
;
866 p
[10] = svgtiny_PATH_BEZIER
;
867 p
[11] = x
- r
* KAPPA
;
870 p
[14] = y
+ r
* KAPPA
;
873 p
[17] = svgtiny_PATH_BEZIER
;
875 p
[19] = y
- r
* KAPPA
;
876 p
[20] = x
- r
* KAPPA
;
880 p
[24] = svgtiny_PATH_BEZIER
;
881 p
[25] = x
+ r
* KAPPA
;
884 p
[28] = y
- r
* KAPPA
;
887 p
[31] = svgtiny_PATH_CLOSE
;
889 err
= svgtiny_add_path(p
, 32, &state
);
891 svgtiny_cleanup_state_local(&state
);
898 * Parse an <ellipse> element node.
901 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
902 struct svgtiny_parse_state state
)
905 float x
= 0, y
= 0, rx
= -1, ry
= -1;
910 svgtiny_setup_state_local(&state
);
912 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
913 if (exc
!= DOM_NO_ERR
) {
914 svgtiny_cleanup_state_local(&state
);
915 return svgtiny_LIBDOM_ERROR
;
918 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
920 dom_string_unref(attr
);
922 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
923 if (exc
!= DOM_NO_ERR
) {
924 svgtiny_cleanup_state_local(&state
);
925 return svgtiny_LIBDOM_ERROR
;
928 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
930 dom_string_unref(attr
);
932 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
933 if (exc
!= DOM_NO_ERR
) {
934 svgtiny_cleanup_state_local(&state
);
935 return svgtiny_LIBDOM_ERROR
;
938 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
940 dom_string_unref(attr
);
942 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
943 if (exc
!= DOM_NO_ERR
) {
944 svgtiny_cleanup_state_local(&state
);
945 return svgtiny_LIBDOM_ERROR
;
948 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
950 dom_string_unref(attr
);
952 svgtiny_parse_paint_attributes(ellipse
, &state
);
953 svgtiny_parse_transform_attributes(ellipse
, &state
);
955 if (rx
< 0 || ry
< 0) {
956 state
.diagram
->error_line
= -1; /* ellipse->line; */
957 state
.diagram
->error_message
= "ellipse: rx or ry missing "
959 svgtiny_cleanup_state_local(&state
);
960 return svgtiny_SVG_ERROR
;
962 if (rx
== 0 || ry
== 0) {
963 svgtiny_cleanup_state_local(&state
);
967 p
= malloc(32 * sizeof p
[0]);
969 svgtiny_cleanup_state_local(&state
);
970 return svgtiny_OUT_OF_MEMORY
;
973 p
[0] = svgtiny_PATH_MOVE
;
976 p
[3] = svgtiny_PATH_BEZIER
;
978 p
[5] = y
+ ry
* KAPPA
;
979 p
[6] = x
+ rx
* KAPPA
;
983 p
[10] = svgtiny_PATH_BEZIER
;
984 p
[11] = x
- rx
* KAPPA
;
987 p
[14] = y
+ ry
* KAPPA
;
990 p
[17] = svgtiny_PATH_BEZIER
;
992 p
[19] = y
- ry
* KAPPA
;
993 p
[20] = x
- rx
* KAPPA
;
997 p
[24] = svgtiny_PATH_BEZIER
;
998 p
[25] = x
+ rx
* KAPPA
;
1001 p
[28] = y
- ry
* KAPPA
;
1004 p
[31] = svgtiny_PATH_CLOSE
;
1006 err
= svgtiny_add_path(p
, 32, &state
);
1008 svgtiny_cleanup_state_local(&state
);
1015 * Parse a <line> element node.
1018 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
1019 struct svgtiny_parse_state state
)
1022 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
1027 svgtiny_setup_state_local(&state
);
1029 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
1030 if (exc
!= DOM_NO_ERR
) {
1031 svgtiny_cleanup_state_local(&state
);
1032 return svgtiny_LIBDOM_ERROR
;
1035 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1037 dom_string_unref(attr
);
1039 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
1040 if (exc
!= DOM_NO_ERR
) {
1041 svgtiny_cleanup_state_local(&state
);
1042 return svgtiny_LIBDOM_ERROR
;
1045 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1047 dom_string_unref(attr
);
1049 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
1050 if (exc
!= DOM_NO_ERR
) {
1051 svgtiny_cleanup_state_local(&state
);
1052 return svgtiny_LIBDOM_ERROR
;
1055 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1057 dom_string_unref(attr
);
1059 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
1060 if (exc
!= DOM_NO_ERR
) {
1061 svgtiny_cleanup_state_local(&state
);
1062 return svgtiny_LIBDOM_ERROR
;
1065 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1067 dom_string_unref(attr
);
1069 svgtiny_parse_paint_attributes(line
, &state
);
1070 svgtiny_parse_transform_attributes(line
, &state
);
1072 p
= malloc(7 * sizeof p
[0]);
1074 svgtiny_cleanup_state_local(&state
);
1075 return svgtiny_OUT_OF_MEMORY
;
1078 p
[0] = svgtiny_PATH_MOVE
;
1081 p
[3] = svgtiny_PATH_LINE
;
1084 p
[6] = svgtiny_PATH_CLOSE
;
1086 err
= svgtiny_add_path(p
, 7, &state
);
1088 svgtiny_cleanup_state_local(&state
);
1095 * Parse a <polyline> or <polygon> element node.
1097 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1098 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1101 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
1102 struct svgtiny_parse_state state
, bool polygon
)
1105 dom_string
*points_str
;
1111 svgtiny_setup_state_local(&state
);
1113 svgtiny_parse_paint_attributes(poly
, &state
);
1114 svgtiny_parse_transform_attributes(poly
, &state
);
1116 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
1118 if (exc
!= DOM_NO_ERR
) {
1119 svgtiny_cleanup_state_local(&state
);
1120 return svgtiny_LIBDOM_ERROR
;
1123 if (points_str
== NULL
) {
1124 state
.diagram
->error_line
= -1; /* poly->line; */
1125 state
.diagram
->error_message
=
1126 "polyline/polygon: missing points attribute";
1127 svgtiny_cleanup_state_local(&state
);
1128 return svgtiny_SVG_ERROR
;
1131 s
= points
= strndup(dom_string_data(points_str
),
1132 dom_string_byte_length(points_str
));
1133 dom_string_unref(points_str
);
1134 /* read points attribute */
1136 svgtiny_cleanup_state_local(&state
);
1137 return svgtiny_OUT_OF_MEMORY
;
1139 /* allocate space for path: it will never have more elements than s */
1140 p
= malloc(sizeof p
[0] * strlen(s
));
1143 svgtiny_cleanup_state_local(&state
);
1144 return svgtiny_OUT_OF_MEMORY
;
1147 /* parse s and build path */
1148 for (i
= 0; s
[i
]; i
++)
1156 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
1158 p
[i
++] = svgtiny_PATH_MOVE
;
1160 p
[i
++] = svgtiny_PATH_LINE
;
1169 p
[i
++] = svgtiny_PATH_CLOSE
;
1173 err
= svgtiny_add_path(p
, i
, &state
);
1175 svgtiny_cleanup_state_local(&state
);
1182 * Parse a <text> or <tspan> element node.
1185 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
1186 struct svgtiny_parse_state state
)
1188 float x
, y
, width
, height
;
1193 svgtiny_setup_state_local(&state
);
1195 svgtiny_parse_position_attributes(text
, state
,
1196 &x
, &y
, &width
, &height
);
1197 svgtiny_parse_font_attributes(text
, &state
);
1198 svgtiny_parse_transform_attributes(text
, &state
);
1200 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
1201 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
1202 /* state.ctm.e = px - state.origin_x; */
1203 /* state.ctm.f = py - state.origin_y; */
1205 /*struct css_style style = state.style;
1206 style.font_size.value.length.value *= state.ctm.a;*/
1208 exc
= dom_node_get_first_child(text
, &child
);
1209 if (exc
!= DOM_NO_ERR
) {
1210 return svgtiny_LIBDOM_ERROR
;
1211 svgtiny_cleanup_state_local(&state
);
1213 while (child
!= NULL
) {
1215 dom_node_type nodetype
;
1216 svgtiny_code code
= svgtiny_OK
;
1218 exc
= dom_node_get_node_type(child
, &nodetype
);
1219 if (exc
!= DOM_NO_ERR
) {
1220 dom_node_unref(child
);
1221 svgtiny_cleanup_state_local(&state
);
1222 return svgtiny_LIBDOM_ERROR
;
1224 if (nodetype
== DOM_ELEMENT_NODE
) {
1225 dom_string
*nodename
;
1226 exc
= dom_node_get_node_name(child
, &nodename
);
1227 if (exc
!= DOM_NO_ERR
) {
1228 dom_node_unref(child
);
1229 svgtiny_cleanup_state_local(&state
);
1230 return svgtiny_LIBDOM_ERROR
;
1232 if (dom_string_caseless_isequal(nodename
,
1233 state
.interned_tspan
))
1234 code
= svgtiny_parse_text((dom_element
*)child
,
1236 dom_string_unref(nodename
);
1237 } else if (nodetype
== DOM_TEXT_NODE
) {
1238 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1239 dom_string
*content
;
1240 if (shape
== NULL
) {
1241 dom_node_unref(child
);
1242 svgtiny_cleanup_state_local(&state
);
1243 return svgtiny_OUT_OF_MEMORY
;
1245 exc
= dom_text_get_whole_text(child
, &content
);
1246 if (exc
!= DOM_NO_ERR
) {
1247 dom_node_unref(child
);
1248 svgtiny_cleanup_state_local(&state
);
1249 return svgtiny_LIBDOM_ERROR
;
1251 if (content
!= NULL
) {
1252 shape
->text
= strndup(dom_string_data(content
),
1253 dom_string_byte_length(content
));
1254 dom_string_unref(content
);
1256 shape
->text
= strdup("");
1260 state
.diagram
->shape_count
++;
1263 if (code
!= svgtiny_OK
) {
1264 dom_node_unref(child
);
1265 svgtiny_cleanup_state_local(&state
);
1268 exc
= dom_node_get_next_sibling(child
, &next
);
1269 dom_node_unref(child
);
1270 if (exc
!= DOM_NO_ERR
) {
1271 svgtiny_cleanup_state_local(&state
);
1272 return svgtiny_LIBDOM_ERROR
;
1277 svgtiny_cleanup_state_local(&state
);
1284 * Parse x, y, width, and height attributes, if present.
1287 void svgtiny_parse_position_attributes(dom_element
*node
,
1288 const struct svgtiny_parse_state state
,
1289 float *x
, float *y
, float *width
, float *height
)
1296 *width
= state
.viewport_width
;
1297 *height
= state
.viewport_height
;
1299 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1300 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1301 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1302 dom_string_unref(attr
);
1305 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1306 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1307 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1308 dom_string_unref(attr
);
1311 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
1312 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1313 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
1315 dom_string_unref(attr
);
1318 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
1319 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1320 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
1322 dom_string_unref(attr
);
1328 * Parse a length as a number of pixels.
1331 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1332 const struct svgtiny_parse_state state
)
1334 int num_length
= strspn(s
, "0123456789+-.");
1335 const char *unit
= s
+ num_length
;
1336 float n
= atof((const char *) s
);
1337 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1343 } else if (unit
[0] == '%') {
1344 return n
/ 100.0 * viewport_size
;
1345 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
1346 return n
* font_size
;
1347 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
1348 return n
/ 2.0 * font_size
;
1349 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
1351 } else if (unit
[0] == 'p' && unit
[1] == 't') {
1353 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
1355 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
1356 return n
* 3.543307;
1357 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
1358 return n
* 35.43307;
1359 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
1366 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
1367 const struct svgtiny_parse_state state
)
1369 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1370 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
1376 * Parse paint attributes, if present.
1379 void svgtiny_parse_paint_attributes(dom_element
*node
,
1380 struct svgtiny_parse_state
*state
)
1385 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
1386 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1387 svgtiny_parse_color(attr
, &state
->fill
, &state
->fill_grad
, state
);
1388 dom_string_unref(attr
);
1391 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
1392 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1393 svgtiny_parse_color(attr
, &state
->stroke
, &state
->stroke_grad
, state
);
1394 dom_string_unref(attr
);
1397 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
1398 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1399 state
->stroke_width
= svgtiny_parse_length(attr
,
1400 state
->viewport_width
, *state
);
1401 dom_string_unref(attr
);
1404 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
1405 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1406 char *style
= strndup(dom_string_data(attr
),
1407 dom_string_byte_length(attr
));
1410 if ((s
= strstr(style
, "fill:"))) {
1414 value
= strndup(s
, strcspn(s
, "; "));
1415 _svgtiny_parse_color(value
, &state
->fill
, &state
->fill_grad
, state
);
1418 if ((s
= strstr(style
, "stroke:"))) {
1422 value
= strndup(s
, strcspn(s
, "; "));
1423 _svgtiny_parse_color(value
, &state
->stroke
, &state
->stroke_grad
, state
);
1426 if ((s
= strstr(style
, "stroke-width:"))) {
1430 value
= strndup(s
, strcspn(s
, "; "));
1431 state
->stroke_width
= _svgtiny_parse_length(value
,
1432 state
->viewport_width
, *state
);
1436 dom_string_unref(attr
);
1445 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
1446 struct svgtiny_parse_state_gradient
*grad
,
1447 struct svgtiny_parse_state
*state
)
1449 unsigned int r
, g
, b
;
1451 size_t len
= strlen(s
);
1452 char *id
= 0, *rparen
;
1454 if (len
== 4 && s
[0] == '#') {
1455 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
1456 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1458 } else if (len
== 7 && s
[0] == '#') {
1459 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1460 *c
= svgtiny_RGB(r
, g
, b
);
1462 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1463 s
[3] == '(' && s
[len
- 1] == ')') {
1464 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1465 *c
= svgtiny_RGB(r
, g
, b
);
1466 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1470 *c
= svgtiny_RGB(r
, g
, b
);
1473 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1474 *c
= svgtiny_TRANSPARENT
;
1476 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1479 *c
= svgtiny_RGB(0, 0, 0);
1480 } else if (s
[4] == '#') {
1484 rparen
= strchr(id
, ')');
1487 svgtiny_find_gradient(id
, grad
, state
);
1489 if (grad
->linear_gradient_stop_count
== 0)
1490 *c
= svgtiny_TRANSPARENT
;
1491 else if (grad
->linear_gradient_stop_count
== 1)
1492 *c
= grad
->gradient_stop
[0].color
;
1494 *c
= svgtiny_LINEAR_GRADIENT
;
1498 const struct svgtiny_named_color
*named_color
;
1499 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1501 *c
= named_color
->color
;
1505 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
1506 struct svgtiny_parse_state_gradient
*grad
,
1507 struct svgtiny_parse_state
*state
)
1510 _svgtiny_parse_color(dom_string_data(s
), c
, grad
, state
);
1511 dom_string_unref(s
);
1515 * Parse font attributes, if present.
1518 void svgtiny_parse_font_attributes(dom_element
*node
,
1519 struct svgtiny_parse_state
*state
)
1521 /* TODO: Implement this, it never used to be */
1524 #ifdef WRITTEN_THIS_PROPERLY
1525 const xmlAttr
*attr
;
1529 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1530 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1531 /*if (css_parse_length(
1532 (const char *) attr->children->content,
1533 &state->style.font_size.value.length,
1535 state->style.font_size.size =
1536 CSS_FONT_SIZE_LENGTH;
1545 * Parse transform attributes, if present.
1547 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1550 void svgtiny_parse_transform_attributes(dom_element
*node
,
1551 struct svgtiny_parse_state
*state
)
1557 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
1559 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1560 transform
= strndup(dom_string_data(attr
),
1561 dom_string_byte_length(attr
));
1562 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1563 &state
->ctm
.c
, &state
->ctm
.d
,
1564 &state
->ctm
.e
, &state
->ctm
.f
);
1566 dom_string_unref(attr
);
1572 * Parse a transform string.
1575 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1576 float *mc
, float *md
, float *me
, float *mf
)
1578 float a
, b
, c
, d
, e
, f
;
1579 float za
, zb
, zc
, zd
, ze
, zf
;
1584 for (i
= 0; s
[i
]; i
++)
1593 if ((sscanf(s
, " matrix (%f %f %f %f %f %f ) %n",
1594 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6) && (n
> 0))
1596 else if ((sscanf(s
, " translate (%f %f ) %n",
1597 &e
, &f
, &n
) == 2) && (n
> 0))
1599 else if ((sscanf(s
, " translate (%f ) %n",
1600 &e
, &n
) == 1) && (n
> 0))
1602 else if ((sscanf(s
, " scale (%f %f ) %n",
1603 &a
, &d
, &n
) == 2) && (n
> 0))
1605 else if ((sscanf(s
, " scale (%f ) %n",
1606 &a
, &n
) == 1) && (n
> 0))
1608 else if ((sscanf(s
, " rotate (%f %f %f ) %n",
1609 &angle
, &x
, &y
, &n
) == 3) && (n
> 0)) {
1610 angle
= angle
/ 180 * M_PI
;
1615 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1616 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1617 } else if ((sscanf(s
, " rotate (%f ) %n",
1618 &angle
, &n
) == 1) && (n
> 0)) {
1619 angle
= angle
/ 180 * M_PI
;
1624 } else if ((sscanf(s
, " skewX (%f ) %n",
1625 &angle
, &n
) == 1) && (n
> 0)) {
1626 angle
= angle
/ 180 * M_PI
;
1628 } else if ((sscanf(s
, " skewY (%f ) %n",
1629 &angle
, &n
) == 1) && (n
> 0)) {
1630 angle
= angle
/ 180 * M_PI
;
1634 za
= *ma
* a
+ *mc
* b
;
1635 zb
= *mb
* a
+ *md
* b
;
1636 zc
= *ma
* c
+ *mc
* d
;
1637 zd
= *mb
* c
+ *md
* d
;
1638 ze
= *ma
* e
+ *mc
* f
+ *me
;
1639 zf
= *mb
* e
+ *md
* f
+ *mf
;
1652 * Add a path to the svgtiny_diagram.
1655 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1656 struct svgtiny_parse_state
*state
)
1658 struct svgtiny_shape
*shape
;
1660 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1661 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1663 svgtiny_transform_path(p
, n
, state
);
1665 shape
= svgtiny_add_shape(state
);
1668 return svgtiny_OUT_OF_MEMORY
;
1671 shape
->path_length
= n
;
1672 state
->diagram
->shape_count
++;
1679 * Add a svgtiny_shape to the svgtiny_diagram.
1682 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1684 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1685 (state
->diagram
->shape_count
+ 1) *
1686 sizeof (state
->diagram
->shape
[0]));
1689 state
->diagram
->shape
= shape
;
1691 shape
+= state
->diagram
->shape_count
;
1693 shape
->path_length
= 0;
1695 shape
->fill
= state
->fill
;
1696 shape
->stroke
= state
->stroke
;
1697 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1698 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1699 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1700 shape
->stroke_width
= 1;
1707 * Apply the current transformation matrix to a path.
1710 void svgtiny_transform_path(float *p
, unsigned int n
,
1711 struct svgtiny_parse_state
*state
)
1715 for (j
= 0; j
!= n
; ) {
1716 unsigned int points
= 0;
1718 switch ((int) p
[j
]) {
1719 case svgtiny_PATH_MOVE
:
1720 case svgtiny_PATH_LINE
:
1723 case svgtiny_PATH_CLOSE
:
1726 case svgtiny_PATH_BEZIER
:
1733 for (k
= 0; k
!= points
; k
++) {
1734 float x0
= p
[j
], y0
= p
[j
+ 1];
1735 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1737 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1748 * Free all memory used by a diagram.
1751 void svgtiny_free(struct svgtiny_diagram
*svg
)
1756 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1757 free(svg
->shape
[i
].path
);
1758 free(svg
->shape
[i
].text
);
1766 #ifndef HAVE_STRNDUP
1767 char *svgtiny_strndup(const char *s
, size_t n
)
1772 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1775 s2
= malloc(len
+ 1);