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(const 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(const dom_element
*node
,
49 struct svgtiny_parse_state
*state
);
50 static void svgtiny_parse_font_attributes(const 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
);
59 * Create a new svgtiny_diagram structure.
62 struct svgtiny_diagram
*svgtiny_create(void)
64 struct svgtiny_diagram
*diagram
;
66 diagram
= calloc(sizeof(*diagram
), 1);
75 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
83 * Parse a block of memory into a svgtiny_diagram.
86 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
87 const char *buffer
, size_t size
, const char *url
,
88 int viewport_width
, int viewport_height
)
90 dom_document
*document
;
92 dom_xml_parser
*parser
;
96 lwc_string
*svg_name_lwc
;
97 struct svgtiny_parse_state state
;
98 float x
, y
, width
, height
;
107 parser
= dom_xml_parser_create(NULL
, NULL
,
108 ignore_msg
, NULL
, &document
);
111 return svgtiny_LIBDOM_ERROR
;
113 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
114 if (err
!= DOM_XML_OK
) {
115 dom_node_unref(document
);
116 dom_xml_parser_destroy(parser
);
117 return svgtiny_LIBDOM_ERROR
;
120 err
= dom_xml_parser_completed(parser
);
121 if (err
!= DOM_XML_OK
) {
122 dom_node_unref(document
);
123 dom_xml_parser_destroy(parser
);
124 return svgtiny_LIBDOM_ERROR
;
127 /* We're done parsing, drop the parser.
128 * We now own the document entirely.
130 dom_xml_parser_destroy(parser
);
132 /* find root <svg> element */
133 exc
= dom_document_get_document_element(document
, &svg
);
134 if (exc
!= DOM_NO_ERR
) {
135 dom_node_unref(document
);
136 return svgtiny_LIBDOM_ERROR
;
138 exc
= dom_node_get_node_name(svg
, &svg_name
);
139 if (exc
!= DOM_NO_ERR
) {
141 dom_node_unref(document
);
142 return svgtiny_LIBDOM_ERROR
;
144 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
145 &svg_name_lwc
) != lwc_error_ok
) {
146 dom_string_unref(svg_name
);
148 dom_node_unref(document
);
149 return svgtiny_LIBDOM_ERROR
;
151 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
152 lwc_string_unref(svg_name_lwc
);
153 dom_string_unref(svg_name
);
155 dom_node_unref(document
);
156 return svgtiny_NOT_SVG
;
159 lwc_string_unref(svg_name_lwc
);
160 dom_string_unref(svg_name
);
162 /* get graphic dimensions */
163 state
.diagram
= diagram
;
164 state
.document
= document
;
165 state
.viewport_width
= viewport_width
;
166 state
.viewport_height
= viewport_height
;
168 #define SVGTINY_STRING_ACTION(s) \
169 if (dom_string_create_interned((const uint8_t *) #s, \
170 strlen(#s), &state.interned_##s) \
172 code = svgtiny_LIBDOM_ERROR; \
175 #include "svgtiny_strings.h"
176 #undef SVGTINY_STRING_ACTION
178 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
179 diagram
->width
= width
;
180 diagram
->height
= height
;
182 /* set up parsing state */
183 state
.viewport_width
= width
;
184 state
.viewport_height
= height
;
185 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
188 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
189 state
.ctm
.e
= 0; /*x;*/
190 state
.ctm
.f
= 0; /*y;*/
191 /*state.style = css_base_style;
192 state.style.font_size.value.length.value = option_font_size * 0.1;*/
193 state
.fill
= 0x000000;
194 state
.stroke
= svgtiny_TRANSPARENT
;
195 state
.stroke_width
= 1;
196 state
.linear_gradient_stop_count
= 0;
199 code
= svgtiny_parse_svg(svg
, state
);
202 dom_node_unref(document
);
205 #define SVGTINY_STRING_ACTION(s) \
206 if (state.interned_##s != NULL) \
207 dom_string_unref(state.interned_##s);
208 //#include "svgtiny_strings.h"
209 #undef SVGTINY_STRING_ACTION
215 * Parse a <svg> or <g> element node.
218 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
219 struct svgtiny_parse_state state
)
221 float x
, y
, width
, height
;
222 dom_string
*view_box
;
226 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
227 svgtiny_parse_paint_attributes(svg
, &state
);
228 svgtiny_parse_font_attributes(svg
, &state
);
230 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
232 if (exc
!= DOM_NO_ERR
) {
233 return svgtiny_LIBDOM_ERROR
;
237 char *s
= strndup(dom_string_data(view_box
),
238 dom_string_length(view_box
));
239 float min_x
, min_y
, vwidth
, vheight
;
240 if (sscanf(s
, "%f,%f,%f,%f",
241 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
242 sscanf(s
, "%f %f %f %f",
243 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
244 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
245 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
246 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
247 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
250 dom_string_unref(view_box
);
253 svgtiny_parse_transform_attributes(svg
, &state
);
255 exc
= dom_node_get_first_child(svg
, &child
);
256 if (exc
!= DOM_NO_ERR
) {
257 return svgtiny_LIBDOM_ERROR
;
259 while (child
!= NULL
) {
261 dom_node_type nodetype
;
262 svgtiny_code code
= svgtiny_OK
;
264 exc
= dom_node_get_node_type(child
, &nodetype
);
265 if (exc
!= DOM_NO_ERR
) {
266 dom_node_unref(child
);
267 return svgtiny_LIBDOM_ERROR
;
269 if (nodetype
== DOM_ELEMENT_NODE
) {
270 dom_string
*nodename
;
271 exc
= dom_node_get_node_name(child
, &nodename
);
272 if (exc
!= DOM_NO_ERR
) {
273 dom_node_unref(child
);
274 return svgtiny_LIBDOM_ERROR
;
276 if (dom_string_caseless_isequal(state
.interned_svg
,
278 code
= svgtiny_parse_svg(child
, state
);
279 else if (dom_string_caseless_isequal(state
.interned_g
,
281 code
= svgtiny_parse_svg(child
, state
);
282 else if (dom_string_caseless_isequal(state
.interned_a
,
284 code
= svgtiny_parse_svg(child
, state
);
285 else if (dom_string_caseless_isequal(state
.interned_path
,
287 code
= svgtiny_parse_path(child
, state
);
288 else if (dom_string_caseless_isequal(state
.interned_rect
,
290 code
= svgtiny_parse_rect(child
, state
);
291 else if (dom_string_caseless_isequal(state
.interned_circle
,
293 code
= svgtiny_parse_circle(child
, state
);
294 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
296 code
= svgtiny_parse_ellipse(child
, state
);
297 else if (dom_string_caseless_isequal(state
.interned_line
,
299 code
= svgtiny_parse_line(child
, state
);
300 else if (dom_string_caseless_isequal(state
.interned_polyline
,
302 code
= svgtiny_parse_poly(child
, state
, false);
303 else if (dom_string_caseless_isequal(state
.interned_polygon
,
305 code
= svgtiny_parse_poly(child
, state
, true);
306 else if (dom_string_caseless_isequal(state
.interned_text
,
308 code
= svgtiny_parse_text(child
, state
);
309 dom_string_unref(nodename
);
311 if (code
!= svgtiny_OK
) {
312 dom_node_unref(child
);
315 exc
= dom_node_get_next_sibling(child
, &next
);
316 dom_node_unref(child
);
317 if (exc
!= DOM_NO_ERR
) {
318 return svgtiny_LIBDOM_ERROR
;
329 * Parse a <path> element node.
331 * http://www.w3.org/TR/SVG11/paths#PathElement
334 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
335 struct svgtiny_parse_state state
)
337 dom_string
*path_d_str
;
342 float last_x
= 0, last_y
= 0;
343 float last_cubic_x
= 0, last_cubic_y
= 0;
344 float last_quad_x
= 0, last_quad_y
= 0;
346 svgtiny_parse_paint_attributes(path
, &state
);
347 svgtiny_parse_transform_attributes(path
, &state
);
349 /* read d attribute */
350 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
351 if (exc
!= DOM_NO_ERR
) {
352 state
.diagram
->error_line
= -1; /* path->line; */
353 state
.diagram
->error_message
= "path: error retrieving d attribute";
354 return svgtiny_SVG_ERROR
;
357 if (path_d_str
== NULL
) {
358 state
.diagram
->error_line
= -1; /* path->line; */
359 state
.diagram
->error_message
= "path: missing d attribute";
360 return svgtiny_SVG_ERROR
;
363 s
= path_d
= strndup(dom_string_data(path_d_str
),
364 dom_string_length(path_d_str
));
365 dom_string_unref(path_d_str
);
367 return svgtiny_OUT_OF_MEMORY
;
369 /* allocate space for path: it will never have more elements than d */
370 p
= malloc(sizeof p
[0] * strlen(s
));
373 return svgtiny_OUT_OF_MEMORY
;
376 /* parse d and build path */
377 for (i
= 0; s
[i
]; i
++)
384 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
387 /* moveto (M, m), lineto (L, l) (2 arguments) */
388 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
389 /*LOG(("moveto or lineto"));*/
390 if (*command
== 'M' || *command
== 'm')
391 plot_command
= svgtiny_PATH_MOVE
;
393 plot_command
= svgtiny_PATH_LINE
;
395 p
[i
++] = plot_command
;
396 if ('a' <= *command
) {
400 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
402 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
405 plot_command
= svgtiny_PATH_LINE
;
406 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
408 /* closepath (Z, z) (no arguments) */
409 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
410 /*LOG(("closepath"));*/
411 p
[i
++] = svgtiny_PATH_CLOSE
;
414 /* horizontal lineto (H, h) (1 argument) */
415 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
416 /*LOG(("horizontal lineto"));*/
418 p
[i
++] = svgtiny_PATH_LINE
;
421 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
423 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
425 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
427 /* vertical lineto (V, v) (1 argument) */
428 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
429 /*LOG(("vertical lineto"));*/
431 p
[i
++] = svgtiny_PATH_LINE
;
434 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
435 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
438 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
440 /* curveto (C, c) (6 arguments) */
441 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
442 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
443 /*LOG(("curveto"));*/
445 p
[i
++] = svgtiny_PATH_BEZIER
;
446 if (*command
== 'c') {
456 p
[i
++] = last_cubic_x
= x2
;
457 p
[i
++] = last_cubic_y
= y2
;
458 p
[i
++] = last_quad_x
= last_x
= x
;
459 p
[i
++] = last_quad_y
= last_y
= y
;
461 } while (sscanf(s
, "%f %f %f %f %f %f %n",
462 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
464 /* shorthand/smooth curveto (S, s) (4 arguments) */
465 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
466 &x2
, &y2
, &x
, &y
, &n
) == 5) {
467 /*LOG(("shorthand/smooth curveto"));*/
469 p
[i
++] = svgtiny_PATH_BEZIER
;
470 x1
= last_x
+ (last_x
- last_cubic_x
);
471 y1
= last_y
+ (last_y
- last_cubic_y
);
472 if (*command
== 's') {
480 p
[i
++] = last_cubic_x
= x2
;
481 p
[i
++] = last_cubic_y
= y2
;
482 p
[i
++] = last_quad_x
= last_x
= x
;
483 p
[i
++] = last_quad_y
= last_y
= y
;
485 } while (sscanf(s
, "%f %f %f %f %n",
486 &x2
, &y2
, &x
, &y
, &n
) == 4);
488 /* quadratic Bezier curveto (Q, q) (4 arguments) */
489 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
490 &x1
, &y1
, &x
, &y
, &n
) == 5) {
491 /*LOG(("quadratic Bezier curveto"));*/
493 p
[i
++] = svgtiny_PATH_BEZIER
;
496 if (*command
== 'q') {
502 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
503 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
504 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
505 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
506 p
[i
++] = last_cubic_x
= last_x
= x
;
507 p
[i
++] = last_cubic_y
= last_y
= y
;
509 } while (sscanf(s
, "%f %f %f %f %n",
510 &x1
, &y1
, &x
, &y
, &n
) == 4);
512 /* shorthand/smooth quadratic Bezier curveto (T, t)
514 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
516 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
518 p
[i
++] = svgtiny_PATH_BEZIER
;
519 x1
= last_x
+ (last_x
- last_quad_x
);
520 y1
= last_y
+ (last_y
- last_quad_y
);
523 if (*command
== 't') {
529 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
530 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
531 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
532 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
533 p
[i
++] = last_cubic_x
= last_x
= x
;
534 p
[i
++] = last_cubic_y
= last_y
= y
;
536 } while (sscanf(s
, "%f %f %n",
539 /* elliptical arc (A, a) (7 arguments) */
540 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
541 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
544 p
[i
++] = svgtiny_PATH_LINE
;
545 if (*command
== 'a') {
549 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
551 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
554 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
555 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
559 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
567 /* no real segments in path */
572 return svgtiny_add_path(p
, i
, &state
);
577 * Parse a <rect> element node.
579 * http://www.w3.org/TR/SVG11/shapes#RectElement
582 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
583 struct svgtiny_parse_state state
)
585 float x
, y
, width
, height
;
588 svgtiny_parse_position_attributes(rect
, state
,
589 &x
, &y
, &width
, &height
);
590 svgtiny_parse_paint_attributes(rect
, &state
);
591 svgtiny_parse_transform_attributes(rect
, &state
);
593 p
= malloc(13 * sizeof p
[0]);
595 return svgtiny_OUT_OF_MEMORY
;
597 p
[0] = svgtiny_PATH_MOVE
;
600 p
[3] = svgtiny_PATH_LINE
;
603 p
[6] = svgtiny_PATH_LINE
;
606 p
[9] = svgtiny_PATH_LINE
;
609 p
[12] = svgtiny_PATH_CLOSE
;
611 return svgtiny_add_path(p
, 13, &state
);
616 * Parse a <circle> element node.
619 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
620 struct svgtiny_parse_state state
)
622 float x
= 0, y
= 0, r
= -1;
627 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
628 if (exc
!= DOM_NO_ERR
)
629 return svgtiny_LIBDOM_ERROR
;
631 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
633 dom_string_unref(attr
);
635 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
636 if (exc
!= DOM_NO_ERR
)
637 return svgtiny_LIBDOM_ERROR
;
639 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
641 dom_string_unref(attr
);
643 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
644 if (exc
!= DOM_NO_ERR
)
645 return svgtiny_LIBDOM_ERROR
;
647 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
649 dom_string_unref(attr
);
651 svgtiny_parse_paint_attributes(circle
, &state
);
652 svgtiny_parse_transform_attributes(circle
, &state
);
655 state
.diagram
->error_line
= -1; /* circle->line; */
656 state
.diagram
->error_message
= "circle: r missing or negative";
657 return svgtiny_SVG_ERROR
;
662 p
= malloc(32 * sizeof p
[0]);
664 return svgtiny_OUT_OF_MEMORY
;
666 p
[0] = svgtiny_PATH_MOVE
;
669 p
[3] = svgtiny_PATH_BEZIER
;
671 p
[5] = y
+ r
* KAPPA
;
672 p
[6] = x
+ r
* KAPPA
;
676 p
[10] = svgtiny_PATH_BEZIER
;
677 p
[11] = x
- r
* KAPPA
;
680 p
[14] = y
+ r
* KAPPA
;
683 p
[17] = svgtiny_PATH_BEZIER
;
685 p
[19] = y
- r
* KAPPA
;
686 p
[20] = x
- r
* KAPPA
;
690 p
[24] = svgtiny_PATH_BEZIER
;
691 p
[25] = x
+ r
* KAPPA
;
694 p
[28] = y
- r
* KAPPA
;
697 p
[31] = svgtiny_PATH_CLOSE
;
699 return svgtiny_add_path(p
, 32, &state
);
704 * Parse an <ellipse> element node.
707 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
708 struct svgtiny_parse_state state
)
710 float x
= 0, y
= 0, rx
= -1, ry
= -1;
715 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
716 if (exc
!= DOM_NO_ERR
)
717 return svgtiny_LIBDOM_ERROR
;
719 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
721 dom_string_unref(attr
);
723 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
724 if (exc
!= DOM_NO_ERR
)
725 return svgtiny_LIBDOM_ERROR
;
727 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
729 dom_string_unref(attr
);
731 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
732 if (exc
!= DOM_NO_ERR
)
733 return svgtiny_LIBDOM_ERROR
;
735 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
737 dom_string_unref(attr
);
739 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
740 if (exc
!= DOM_NO_ERR
)
741 return svgtiny_LIBDOM_ERROR
;
743 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
745 dom_string_unref(attr
);
747 svgtiny_parse_paint_attributes(ellipse
, &state
);
748 svgtiny_parse_transform_attributes(ellipse
, &state
);
750 if (rx
< 0 || ry
< 0) {
751 state
.diagram
->error_line
= -1; /* ellipse->line; */
752 state
.diagram
->error_message
= "ellipse: rx or ry missing "
754 return svgtiny_SVG_ERROR
;
756 if (rx
== 0 || ry
== 0)
759 p
= malloc(32 * sizeof p
[0]);
761 return svgtiny_OUT_OF_MEMORY
;
763 p
[0] = svgtiny_PATH_MOVE
;
766 p
[3] = svgtiny_PATH_BEZIER
;
768 p
[5] = y
+ ry
* KAPPA
;
769 p
[6] = x
+ rx
* KAPPA
;
773 p
[10] = svgtiny_PATH_BEZIER
;
774 p
[11] = x
- rx
* KAPPA
;
777 p
[14] = y
+ ry
* KAPPA
;
780 p
[17] = svgtiny_PATH_BEZIER
;
782 p
[19] = y
- ry
* KAPPA
;
783 p
[20] = x
- rx
* KAPPA
;
787 p
[24] = svgtiny_PATH_BEZIER
;
788 p
[25] = x
+ rx
* KAPPA
;
791 p
[28] = y
- ry
* KAPPA
;
794 p
[31] = svgtiny_PATH_CLOSE
;
796 return svgtiny_add_path(p
, 32, &state
);
801 * Parse a <line> element node.
804 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
805 struct svgtiny_parse_state state
)
807 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
812 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
813 if (exc
!= DOM_NO_ERR
)
814 return svgtiny_LIBDOM_ERROR
;
816 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
818 dom_string_unref(attr
);
820 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
821 if (exc
!= DOM_NO_ERR
)
822 return svgtiny_LIBDOM_ERROR
;
824 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
826 dom_string_unref(attr
);
828 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
829 if (exc
!= DOM_NO_ERR
)
830 return svgtiny_LIBDOM_ERROR
;
832 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
834 dom_string_unref(attr
);
836 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
837 if (exc
!= DOM_NO_ERR
)
838 return svgtiny_LIBDOM_ERROR
;
840 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
842 dom_string_unref(attr
);
844 svgtiny_parse_paint_attributes(line
, &state
);
845 svgtiny_parse_transform_attributes(line
, &state
);
847 p
= malloc(7 * sizeof p
[0]);
849 return svgtiny_OUT_OF_MEMORY
;
851 p
[0] = svgtiny_PATH_MOVE
;
854 p
[3] = svgtiny_PATH_LINE
;
857 p
[6] = svgtiny_PATH_CLOSE
;
859 return svgtiny_add_path(p
, 7, &state
);
864 * Parse a <polyline> or <polygon> element node.
866 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
867 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
870 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
871 struct svgtiny_parse_state state
, bool polygon
)
877 svgtiny_parse_paint_attributes(poly
, &state
);
878 svgtiny_parse_transform_attributes(poly
, &state
);
880 /* read points attribute */
881 s
= points
= (char *) xmlGetProp(poly
, (const xmlChar
*) "points");
883 state
.diagram
->error_line
= poly
->line
;
884 state
.diagram
->error_message
=
885 "polyline/polygon: missing points attribute";
886 return svgtiny_SVG_ERROR
;
889 /* allocate space for path: it will never have more elements than s */
890 p
= malloc(sizeof p
[0] * strlen(s
));
893 return svgtiny_OUT_OF_MEMORY
;
896 /* parse s and build path */
897 for (i
= 0; s
[i
]; i
++)
905 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
907 p
[i
++] = svgtiny_PATH_MOVE
;
909 p
[i
++] = svgtiny_PATH_LINE
;
918 p
[i
++] = svgtiny_PATH_CLOSE
;
922 return svgtiny_add_path(p
, i
, &state
);
927 * Parse a <text> or <tspan> element node.
930 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
931 struct svgtiny_parse_state state
)
933 float x
, y
, width
, height
;
937 svgtiny_parse_position_attributes(text
, state
,
938 &x
, &y
, &width
, &height
);
939 svgtiny_parse_font_attributes(text
, &state
);
940 svgtiny_parse_transform_attributes(text
, &state
);
942 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
943 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
944 /* state.ctm.e = px - state.origin_x; */
945 /* state.ctm.f = py - state.origin_y; */
947 /*struct css_style style = state.style;
948 style.font_size.value.length.value *= state.ctm.a;*/
950 for (child
= text
->children
; child
; child
= child
->next
) {
951 svgtiny_code code
= svgtiny_OK
;
953 if (child
->type
== XML_TEXT_NODE
) {
954 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
956 return svgtiny_OUT_OF_MEMORY
;
957 shape
->text
= strdup((const char *) child
->content
);
960 state
.diagram
->shape_count
++;
962 } else if (child
->type
== XML_ELEMENT_NODE
&&
963 strcmp((const char *) child
->name
,
965 code
= svgtiny_parse_text(child
, state
);
968 if (!code
!= svgtiny_OK
)
977 * Parse x, y, width, and height attributes, if present.
980 void svgtiny_parse_position_attributes(const dom_element
*node
,
981 const struct svgtiny_parse_state state
,
982 float *x
, float *y
, float *width
, float *height
)
988 *width
= state
.viewport_width
;
989 *height
= state
.viewport_height
;
991 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
992 const char *name
= (const char *) attr
->name
;
993 const char *content
= (const char *) attr
->children
->content
;
994 if (strcmp(name
, "x") == 0)
995 *x
= svgtiny_parse_length(content
,
996 state
.viewport_width
, state
);
997 else if (strcmp(name
, "y") == 0)
998 *y
= svgtiny_parse_length(content
,
999 state
.viewport_height
, state
);
1000 else if (strcmp(name
, "width") == 0)
1001 *width
= svgtiny_parse_length(content
,
1002 state
.viewport_width
, state
);
1003 else if (strcmp(name
, "height") == 0)
1004 *height
= svgtiny_parse_length(content
,
1005 state
.viewport_height
, state
);
1011 * Parse a length as a number of pixels.
1014 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1015 const struct svgtiny_parse_state state
)
1017 int num_length
= strspn(s
, "0123456789+-.");
1018 const char *unit
= s
+ num_length
;
1019 float n
= atof((const char *) s
);
1020 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1026 } else if (unit
[0] == '%') {
1027 return n
/ 100.0 * viewport_size
;
1028 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
1029 return n
* font_size
;
1030 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
1031 return n
/ 2.0 * font_size
;
1032 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
1034 } else if (unit
[0] == 'p' && unit
[1] == 't') {
1036 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
1038 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
1039 return n
* 3.543307;
1040 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
1041 return n
* 35.43307;
1042 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
1049 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
1050 const struct svgtiny_parse_state state
)
1052 const char *ss
= strndup(dom_string_data(s
), dom_string_length(s
));
1053 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
1059 * Parse paint attributes, if present.
1062 void svgtiny_parse_paint_attributes(const dom_element
*node
,
1063 struct svgtiny_parse_state
*state
)
1065 const xmlAttr
*attr
;
1067 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1068 const char *name
= (const char *) attr
->name
;
1069 const char *content
= (const char *) attr
->children
->content
;
1070 if (strcmp(name
, "fill") == 0)
1071 svgtiny_parse_color(content
, &state
->fill
, state
);
1072 else if (strcmp(name
, "stroke") == 0)
1073 svgtiny_parse_color(content
, &state
->stroke
, state
);
1074 else if (strcmp(name
, "stroke-width") == 0)
1075 state
->stroke_width
= svgtiny_parse_length(content
,
1076 state
->viewport_width
, *state
);
1077 else if (strcmp(name
, "style") == 0) {
1078 const char *style
= (const char *)
1079 attr
->children
->content
;
1082 if ((s
= strstr(style
, "fill:"))) {
1086 value
= strndup(s
, strcspn(s
, "; "));
1087 svgtiny_parse_color(value
, &state
->fill
, state
);
1090 if ((s
= strstr(style
, "stroke:"))) {
1094 value
= strndup(s
, strcspn(s
, "; "));
1095 svgtiny_parse_color(value
, &state
->stroke
, state
);
1098 if ((s
= strstr(style
, "stroke-width:"))) {
1102 value
= strndup(s
, strcspn(s
, "; "));
1103 state
->stroke_width
= svgtiny_parse_length(value
,
1104 state
->viewport_width
, *state
);
1116 void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
1117 struct svgtiny_parse_state
*state
)
1119 unsigned int r
, g
, b
;
1121 size_t len
= strlen(s
);
1122 char *id
= 0, *rparen
;
1124 if (len
== 4 && s
[0] == '#') {
1125 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
1126 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1128 } else if (len
== 7 && s
[0] == '#') {
1129 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1130 *c
= svgtiny_RGB(r
, g
, b
);
1132 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1133 s
[3] == '(' && s
[len
- 1] == ')') {
1134 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1135 *c
= svgtiny_RGB(r
, g
, b
);
1136 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1140 *c
= svgtiny_RGB(r
, g
, b
);
1143 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1144 *c
= svgtiny_TRANSPARENT
;
1146 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1152 rparen
= strchr(id
, ')');
1155 svgtiny_find_gradient(id
, state
);
1157 fprintf(stderr
, "linear_gradient_stop_count %i\n",
1158 state
->linear_gradient_stop_count
);
1159 if (state
->linear_gradient_stop_count
== 0)
1160 *c
= svgtiny_TRANSPARENT
;
1161 else if (state
->linear_gradient_stop_count
== 1)
1162 *c
= state
->gradient_stop
[0].color
;
1164 *c
= svgtiny_LINEAR_GRADIENT
;
1168 const struct svgtiny_named_color
*named_color
;
1169 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1171 *c
= named_color
->color
;
1177 * Parse font attributes, if present.
1180 void svgtiny_parse_font_attributes(const dom_element
*node
,
1181 struct svgtiny_parse_state
*state
)
1183 const xmlAttr
*attr
;
1187 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1188 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1189 /*if (css_parse_length(
1190 (const char *) attr->children->content,
1191 &state->style.font_size.value.length,
1193 state->style.font_size.size =
1194 CSS_FONT_SIZE_LENGTH;
1202 * Parse transform attributes, if present.
1204 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1207 void svgtiny_parse_transform_attributes(dom_element
*node
,
1208 struct svgtiny_parse_state
*state
)
1212 /* parse transform */
1213 transform
= (char *) xmlGetProp(node
, (const xmlChar
*) "transform");
1215 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1216 &state
->ctm
.c
, &state
->ctm
.d
,
1217 &state
->ctm
.e
, &state
->ctm
.f
);
1224 * Parse a transform string.
1227 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1228 float *mc
, float *md
, float *me
, float *mf
)
1230 float a
, b
, c
, d
, e
, f
;
1231 float za
, zb
, zc
, zd
, ze
, zf
;
1236 for (i
= 0; s
[i
]; i
++)
1244 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
1245 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
1247 else if (sscanf(s
, "translate (%f %f) %n",
1250 else if (sscanf(s
, "translate (%f) %n",
1253 else if (sscanf(s
, "scale (%f %f) %n",
1256 else if (sscanf(s
, "scale (%f) %n",
1259 else if (sscanf(s
, "rotate (%f %f %f) %n",
1260 &angle
, &x
, &y
, &n
) == 3) {
1261 angle
= angle
/ 180 * M_PI
;
1266 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1267 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1268 } else if (sscanf(s
, "rotate (%f) %n",
1270 angle
= angle
/ 180 * M_PI
;
1275 } else if (sscanf(s
, "skewX (%f) %n",
1277 angle
= angle
/ 180 * M_PI
;
1279 } else if (sscanf(s
, "skewY (%f) %n",
1281 angle
= angle
/ 180 * M_PI
;
1285 za
= *ma
* a
+ *mc
* b
;
1286 zb
= *mb
* a
+ *md
* b
;
1287 zc
= *ma
* c
+ *mc
* d
;
1288 zd
= *mb
* c
+ *md
* d
;
1289 ze
= *ma
* e
+ *mc
* f
+ *me
;
1290 zf
= *mb
* e
+ *md
* f
+ *mf
;
1303 * Add a path to the svgtiny_diagram.
1306 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1307 struct svgtiny_parse_state
*state
)
1309 struct svgtiny_shape
*shape
;
1311 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1312 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1314 svgtiny_transform_path(p
, n
, state
);
1316 shape
= svgtiny_add_shape(state
);
1319 return svgtiny_OUT_OF_MEMORY
;
1322 shape
->path_length
= n
;
1323 state
->diagram
->shape_count
++;
1330 * Add a svgtiny_shape to the svgtiny_diagram.
1333 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1335 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1336 (state
->diagram
->shape_count
+ 1) *
1337 sizeof (state
->diagram
->shape
[0]));
1340 state
->diagram
->shape
= shape
;
1342 shape
+= state
->diagram
->shape_count
;
1344 shape
->path_length
= 0;
1346 shape
->fill
= state
->fill
;
1347 shape
->stroke
= state
->stroke
;
1348 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1349 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1350 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1351 shape
->stroke_width
= 1;
1358 * Apply the current transformation matrix to a path.
1361 void svgtiny_transform_path(float *p
, unsigned int n
,
1362 struct svgtiny_parse_state
*state
)
1366 for (j
= 0; j
!= n
; ) {
1367 unsigned int points
= 0;
1369 switch ((int) p
[j
]) {
1370 case svgtiny_PATH_MOVE
:
1371 case svgtiny_PATH_LINE
:
1374 case svgtiny_PATH_CLOSE
:
1377 case svgtiny_PATH_BEZIER
:
1384 for (k
= 0; k
!= points
; k
++) {
1385 float x0
= p
[j
], y0
= p
[j
+ 1];
1386 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1388 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1399 * Free all memory used by a diagram.
1402 void svgtiny_free(struct svgtiny_diagram
*svg
)
1407 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1408 free(svg
->shape
[i
].path
);
1409 free(svg
->shape
[i
].text
);
1417 #ifndef HAVE_STRNDUP
1418 char *svgtiny_strndup(const char *s
, size_t n
)
1423 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1426 s2
= malloc(len
+ 1);