2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2008-2009 James Bursa <james@semichrome.net>
6 * Copyright 2012 Daniel Silverstone <dsilvers@netsurf-browser.org>
18 #include <dom/bindings/xml/xmlparser.h>
21 #include "svgtiny_internal.h"
24 #define M_PI 3.14159265358979323846
27 #define KAPPA 0.5522847498
29 static svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
30 struct svgtiny_parse_state state
);
31 static svgtiny_code
svgtiny_parse_path(dom_element
*path
,
32 struct svgtiny_parse_state state
);
33 static svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
34 struct svgtiny_parse_state state
);
35 static svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
36 struct svgtiny_parse_state state
);
37 static svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
38 struct svgtiny_parse_state state
);
39 static svgtiny_code
svgtiny_parse_line(dom_element
*line
,
40 struct svgtiny_parse_state state
);
41 static svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
42 struct svgtiny_parse_state state
, bool polygon
);
43 static svgtiny_code
svgtiny_parse_text(dom_element
*text
,
44 struct svgtiny_parse_state state
);
45 static void svgtiny_parse_position_attributes(dom_element
*node
,
46 const struct svgtiny_parse_state state
,
47 float *x
, float *y
, float *width
, float *height
);
48 static void svgtiny_parse_paint_attributes(dom_element
*node
,
49 struct svgtiny_parse_state
*state
);
50 static void svgtiny_parse_font_attributes(dom_element
*node
,
51 struct svgtiny_parse_state
*state
);
52 static void svgtiny_parse_transform_attributes(dom_element
*node
,
53 struct svgtiny_parse_state
*state
);
54 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
55 struct svgtiny_parse_state
*state
);
56 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
57 struct svgtiny_parse_state
*state
);
61 * Create a new svgtiny_diagram structure.
64 struct svgtiny_diagram
*svgtiny_create(void)
66 struct svgtiny_diagram
*diagram
;
68 diagram
= calloc(sizeof(*diagram
), 1);
77 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
85 * Parse a block of memory into a svgtiny_diagram.
88 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
89 const char *buffer
, size_t size
, const char *url
,
90 int viewport_width
, int viewport_height
)
92 dom_document
*document
;
94 dom_xml_parser
*parser
;
98 lwc_string
*svg_name_lwc
;
99 struct svgtiny_parse_state state
;
100 float x
, y
, width
, height
;
109 parser
= dom_xml_parser_create(NULL
, NULL
,
110 ignore_msg
, NULL
, &document
);
113 return svgtiny_LIBDOM_ERROR
;
115 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
116 if (err
!= DOM_XML_OK
) {
117 dom_node_unref(document
);
118 dom_xml_parser_destroy(parser
);
119 return svgtiny_LIBDOM_ERROR
;
122 err
= dom_xml_parser_completed(parser
);
123 if (err
!= DOM_XML_OK
) {
124 dom_node_unref(document
);
125 dom_xml_parser_destroy(parser
);
126 return svgtiny_LIBDOM_ERROR
;
129 /* We're done parsing, drop the parser.
130 * We now own the document entirely.
132 dom_xml_parser_destroy(parser
);
134 /* find root <svg> element */
135 exc
= dom_document_get_document_element(document
, &svg
);
136 if (exc
!= DOM_NO_ERR
) {
137 dom_node_unref(document
);
138 return svgtiny_LIBDOM_ERROR
;
140 exc
= dom_node_get_node_name(svg
, &svg_name
);
141 if (exc
!= DOM_NO_ERR
) {
143 dom_node_unref(document
);
144 return svgtiny_LIBDOM_ERROR
;
146 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
147 &svg_name_lwc
) != lwc_error_ok
) {
148 dom_string_unref(svg_name
);
150 dom_node_unref(document
);
151 return svgtiny_LIBDOM_ERROR
;
153 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
154 lwc_string_unref(svg_name_lwc
);
155 dom_string_unref(svg_name
);
157 dom_node_unref(document
);
158 return svgtiny_NOT_SVG
;
161 lwc_string_unref(svg_name_lwc
);
162 dom_string_unref(svg_name
);
164 /* get graphic dimensions */
165 memset(&state
, 0, sizeof(state
));
166 state
.diagram
= diagram
;
167 state
.document
= document
;
168 state
.viewport_width
= viewport_width
;
169 state
.viewport_height
= viewport_height
;
171 #define SVGTINY_STRING_ACTION2(s,n) \
172 if (dom_string_create_interned((const uint8_t *) #n, \
173 strlen(#n), &state.interned_##s) \
175 code = svgtiny_LIBDOM_ERROR; \
178 #include "svgtiny_strings.h"
179 #undef SVGTINY_STRING_ACTION2
181 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
182 diagram
->width
= width
;
183 diagram
->height
= height
;
185 /* set up parsing state */
186 state
.viewport_width
= width
;
187 state
.viewport_height
= height
;
188 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
191 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
192 state
.ctm
.e
= 0; /*x;*/
193 state
.ctm
.f
= 0; /*y;*/
194 /*state.style = css_base_style;
195 state.style.font_size.value.length.value = option_font_size * 0.1;*/
196 state
.fill
= 0x000000;
197 state
.stroke
= svgtiny_TRANSPARENT
;
198 state
.stroke_width
= 1;
199 state
.linear_gradient_stop_count
= 0;
202 code
= svgtiny_parse_svg(svg
, state
);
205 dom_node_unref(document
);
208 if (state
.gradient_x1
!= NULL
)
209 dom_string_unref(state
.gradient_x1
);
210 if (state
.gradient_x2
!= NULL
)
211 dom_string_unref(state
.gradient_x2
);
212 if (state
.gradient_y1
!= NULL
)
213 dom_string_unref(state
.gradient_y1
);
214 if (state
.gradient_y2
!= NULL
)
215 dom_string_unref(state
.gradient_y2
);
216 #define SVGTINY_STRING_ACTION2(s,n) \
217 if (state.interned_##s != NULL) \
218 dom_string_unref(state.interned_##s);
219 #include "svgtiny_strings.h"
220 #undef SVGTINY_STRING_ACTION2
226 * Parse a <svg> or <g> element node.
229 svgtiny_code
svgtiny_parse_svg(dom_element
*svg
,
230 struct svgtiny_parse_state state
)
232 float x
, y
, width
, height
;
233 dom_string
*view_box
;
237 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
238 svgtiny_parse_paint_attributes(svg
, &state
);
239 svgtiny_parse_font_attributes(svg
, &state
);
241 exc
= dom_element_get_attribute(svg
, state
.interned_viewBox
,
243 if (exc
!= DOM_NO_ERR
) {
244 return svgtiny_LIBDOM_ERROR
;
248 char *s
= strndup(dom_string_data(view_box
),
249 dom_string_byte_length(view_box
));
250 float min_x
, min_y
, vwidth
, vheight
;
251 if (sscanf(s
, "%f,%f,%f,%f",
252 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
253 sscanf(s
, "%f %f %f %f",
254 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
255 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
256 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
257 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
258 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
261 dom_string_unref(view_box
);
264 svgtiny_parse_transform_attributes(svg
, &state
);
266 exc
= dom_node_get_first_child(svg
, (dom_node
**) (void *) &child
);
267 if (exc
!= DOM_NO_ERR
) {
268 return svgtiny_LIBDOM_ERROR
;
270 while (child
!= NULL
) {
272 dom_node_type nodetype
;
273 svgtiny_code code
= svgtiny_OK
;
275 exc
= dom_node_get_node_type(child
, &nodetype
);
276 if (exc
!= DOM_NO_ERR
) {
277 dom_node_unref(child
);
278 return svgtiny_LIBDOM_ERROR
;
280 if (nodetype
== DOM_ELEMENT_NODE
) {
281 dom_string
*nodename
;
282 exc
= dom_node_get_node_name(child
, &nodename
);
283 if (exc
!= DOM_NO_ERR
) {
284 dom_node_unref(child
);
285 return svgtiny_LIBDOM_ERROR
;
287 if (dom_string_caseless_isequal(state
.interned_svg
,
289 code
= svgtiny_parse_svg(child
, state
);
290 else if (dom_string_caseless_isequal(state
.interned_g
,
292 code
= svgtiny_parse_svg(child
, state
);
293 else if (dom_string_caseless_isequal(state
.interned_a
,
295 code
= svgtiny_parse_svg(child
, state
);
296 else if (dom_string_caseless_isequal(state
.interned_path
,
298 code
= svgtiny_parse_path(child
, state
);
299 else if (dom_string_caseless_isequal(state
.interned_rect
,
301 code
= svgtiny_parse_rect(child
, state
);
302 else if (dom_string_caseless_isequal(state
.interned_circle
,
304 code
= svgtiny_parse_circle(child
, state
);
305 else if (dom_string_caseless_isequal(state
.interned_ellipse
,
307 code
= svgtiny_parse_ellipse(child
, state
);
308 else if (dom_string_caseless_isequal(state
.interned_line
,
310 code
= svgtiny_parse_line(child
, state
);
311 else if (dom_string_caseless_isequal(state
.interned_polyline
,
313 code
= svgtiny_parse_poly(child
, state
, false);
314 else if (dom_string_caseless_isequal(state
.interned_polygon
,
316 code
= svgtiny_parse_poly(child
, state
, true);
317 else if (dom_string_caseless_isequal(state
.interned_text
,
319 code
= svgtiny_parse_text(child
, state
);
320 dom_string_unref(nodename
);
322 if (code
!= svgtiny_OK
) {
323 dom_node_unref(child
);
326 exc
= dom_node_get_next_sibling(child
,
327 (dom_node
**) (void *) &next
);
328 dom_node_unref(child
);
329 if (exc
!= DOM_NO_ERR
) {
330 return svgtiny_LIBDOM_ERROR
;
341 * Parse a <path> element node.
343 * http://www.w3.org/TR/SVG11/paths#PathElement
346 svgtiny_code
svgtiny_parse_path(dom_element
*path
,
347 struct svgtiny_parse_state state
)
349 dom_string
*path_d_str
;
354 float last_x
= 0, last_y
= 0;
355 float last_cubic_x
= 0, last_cubic_y
= 0;
356 float last_quad_x
= 0, last_quad_y
= 0;
358 svgtiny_parse_paint_attributes(path
, &state
);
359 svgtiny_parse_transform_attributes(path
, &state
);
361 /* read d attribute */
362 exc
= dom_element_get_attribute(path
, state
.interned_d
, &path_d_str
);
363 if (exc
!= DOM_NO_ERR
) {
364 state
.diagram
->error_line
= -1; /* path->line; */
365 state
.diagram
->error_message
= "path: error retrieving d attribute";
366 return svgtiny_SVG_ERROR
;
369 if (path_d_str
== NULL
) {
370 state
.diagram
->error_line
= -1; /* path->line; */
371 state
.diagram
->error_message
= "path: missing d attribute";
372 return svgtiny_SVG_ERROR
;
375 s
= path_d
= strndup(dom_string_data(path_d_str
),
376 dom_string_byte_length(path_d_str
));
377 dom_string_unref(path_d_str
);
379 return svgtiny_OUT_OF_MEMORY
;
381 /* allocate space for path: it will never have more elements than d */
382 p
= malloc(sizeof p
[0] * strlen(s
));
385 return svgtiny_OUT_OF_MEMORY
;
388 /* parse d and build path */
389 for (i
= 0; s
[i
]; i
++)
396 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
399 /* moveto (M, m), lineto (L, l) (2 arguments) */
400 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
401 /*LOG(("moveto or lineto"));*/
402 if (*command
== 'M' || *command
== 'm')
403 plot_command
= svgtiny_PATH_MOVE
;
405 plot_command
= svgtiny_PATH_LINE
;
407 p
[i
++] = plot_command
;
408 if ('a' <= *command
) {
412 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
414 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
417 plot_command
= svgtiny_PATH_LINE
;
418 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
420 /* closepath (Z, z) (no arguments) */
421 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
422 /*LOG(("closepath"));*/
423 p
[i
++] = svgtiny_PATH_CLOSE
;
426 /* horizontal lineto (H, h) (1 argument) */
427 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
428 /*LOG(("horizontal lineto"));*/
430 p
[i
++] = svgtiny_PATH_LINE
;
433 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
435 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
437 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
439 /* vertical lineto (V, v) (1 argument) */
440 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
441 /*LOG(("vertical lineto"));*/
443 p
[i
++] = svgtiny_PATH_LINE
;
446 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
447 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
450 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
452 /* curveto (C, c) (6 arguments) */
453 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
454 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
455 /*LOG(("curveto"));*/
457 p
[i
++] = svgtiny_PATH_BEZIER
;
458 if (*command
== 'c') {
468 p
[i
++] = last_cubic_x
= x2
;
469 p
[i
++] = last_cubic_y
= y2
;
470 p
[i
++] = last_quad_x
= last_x
= x
;
471 p
[i
++] = last_quad_y
= last_y
= y
;
473 } while (sscanf(s
, "%f %f %f %f %f %f %n",
474 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
476 /* shorthand/smooth curveto (S, s) (4 arguments) */
477 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
478 &x2
, &y2
, &x
, &y
, &n
) == 5) {
479 /*LOG(("shorthand/smooth curveto"));*/
481 p
[i
++] = svgtiny_PATH_BEZIER
;
482 x1
= last_x
+ (last_x
- last_cubic_x
);
483 y1
= last_y
+ (last_y
- last_cubic_y
);
484 if (*command
== 's') {
492 p
[i
++] = last_cubic_x
= x2
;
493 p
[i
++] = last_cubic_y
= y2
;
494 p
[i
++] = last_quad_x
= last_x
= x
;
495 p
[i
++] = last_quad_y
= last_y
= y
;
497 } while (sscanf(s
, "%f %f %f %f %n",
498 &x2
, &y2
, &x
, &y
, &n
) == 4);
500 /* quadratic Bezier curveto (Q, q) (4 arguments) */
501 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
502 &x1
, &y1
, &x
, &y
, &n
) == 5) {
503 /*LOG(("quadratic Bezier curveto"));*/
505 p
[i
++] = svgtiny_PATH_BEZIER
;
508 if (*command
== 'q') {
514 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
515 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
516 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
517 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
518 p
[i
++] = last_cubic_x
= last_x
= x
;
519 p
[i
++] = last_cubic_y
= last_y
= y
;
521 } while (sscanf(s
, "%f %f %f %f %n",
522 &x1
, &y1
, &x
, &y
, &n
) == 4);
524 /* shorthand/smooth quadratic Bezier curveto (T, t)
526 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
528 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
530 p
[i
++] = svgtiny_PATH_BEZIER
;
531 x1
= last_x
+ (last_x
- last_quad_x
);
532 y1
= last_y
+ (last_y
- last_quad_y
);
535 if (*command
== 't') {
541 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
542 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
543 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
544 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
545 p
[i
++] = last_cubic_x
= last_x
= x
;
546 p
[i
++] = last_cubic_y
= last_y
= y
;
548 } while (sscanf(s
, "%f %f %n",
551 /* elliptical arc (A, a) (7 arguments) */
552 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
553 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
556 p
[i
++] = svgtiny_PATH_LINE
;
557 if (*command
== 'a') {
561 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
563 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
566 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
567 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
571 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
579 /* no real segments in path */
584 return svgtiny_add_path(p
, i
, &state
);
589 * Parse a <rect> element node.
591 * http://www.w3.org/TR/SVG11/shapes#RectElement
594 svgtiny_code
svgtiny_parse_rect(dom_element
*rect
,
595 struct svgtiny_parse_state state
)
597 float x
, y
, width
, height
;
600 svgtiny_parse_position_attributes(rect
, state
,
601 &x
, &y
, &width
, &height
);
602 svgtiny_parse_paint_attributes(rect
, &state
);
603 svgtiny_parse_transform_attributes(rect
, &state
);
605 p
= malloc(13 * sizeof p
[0]);
607 return svgtiny_OUT_OF_MEMORY
;
609 p
[0] = svgtiny_PATH_MOVE
;
612 p
[3] = svgtiny_PATH_LINE
;
615 p
[6] = svgtiny_PATH_LINE
;
618 p
[9] = svgtiny_PATH_LINE
;
621 p
[12] = svgtiny_PATH_CLOSE
;
623 return svgtiny_add_path(p
, 13, &state
);
628 * Parse a <circle> element node.
631 svgtiny_code
svgtiny_parse_circle(dom_element
*circle
,
632 struct svgtiny_parse_state state
)
634 float x
= 0, y
= 0, r
= -1;
639 exc
= dom_element_get_attribute(circle
, state
.interned_cx
, &attr
);
640 if (exc
!= DOM_NO_ERR
)
641 return svgtiny_LIBDOM_ERROR
;
643 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
645 dom_string_unref(attr
);
647 exc
= dom_element_get_attribute(circle
, state
.interned_cy
, &attr
);
648 if (exc
!= DOM_NO_ERR
)
649 return svgtiny_LIBDOM_ERROR
;
651 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
653 dom_string_unref(attr
);
655 exc
= dom_element_get_attribute(circle
, state
.interned_r
, &attr
);
656 if (exc
!= DOM_NO_ERR
)
657 return svgtiny_LIBDOM_ERROR
;
659 r
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
661 dom_string_unref(attr
);
663 svgtiny_parse_paint_attributes(circle
, &state
);
664 svgtiny_parse_transform_attributes(circle
, &state
);
667 state
.diagram
->error_line
= -1; /* circle->line; */
668 state
.diagram
->error_message
= "circle: r missing or negative";
669 return svgtiny_SVG_ERROR
;
674 p
= malloc(32 * sizeof p
[0]);
676 return svgtiny_OUT_OF_MEMORY
;
678 p
[0] = svgtiny_PATH_MOVE
;
681 p
[3] = svgtiny_PATH_BEZIER
;
683 p
[5] = y
+ r
* KAPPA
;
684 p
[6] = x
+ r
* KAPPA
;
688 p
[10] = svgtiny_PATH_BEZIER
;
689 p
[11] = x
- r
* KAPPA
;
692 p
[14] = y
+ r
* KAPPA
;
695 p
[17] = svgtiny_PATH_BEZIER
;
697 p
[19] = y
- r
* KAPPA
;
698 p
[20] = x
- r
* KAPPA
;
702 p
[24] = svgtiny_PATH_BEZIER
;
703 p
[25] = x
+ r
* KAPPA
;
706 p
[28] = y
- r
* KAPPA
;
709 p
[31] = svgtiny_PATH_CLOSE
;
711 return svgtiny_add_path(p
, 32, &state
);
716 * Parse an <ellipse> element node.
719 svgtiny_code
svgtiny_parse_ellipse(dom_element
*ellipse
,
720 struct svgtiny_parse_state state
)
722 float x
= 0, y
= 0, rx
= -1, ry
= -1;
727 exc
= dom_element_get_attribute(ellipse
, state
.interned_cx
, &attr
);
728 if (exc
!= DOM_NO_ERR
)
729 return svgtiny_LIBDOM_ERROR
;
731 x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
733 dom_string_unref(attr
);
735 exc
= dom_element_get_attribute(ellipse
, state
.interned_cy
, &attr
);
736 if (exc
!= DOM_NO_ERR
)
737 return svgtiny_LIBDOM_ERROR
;
739 y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
741 dom_string_unref(attr
);
743 exc
= dom_element_get_attribute(ellipse
, state
.interned_rx
, &attr
);
744 if (exc
!= DOM_NO_ERR
)
745 return svgtiny_LIBDOM_ERROR
;
747 rx
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
749 dom_string_unref(attr
);
751 exc
= dom_element_get_attribute(ellipse
, state
.interned_ry
, &attr
);
752 if (exc
!= DOM_NO_ERR
)
753 return svgtiny_LIBDOM_ERROR
;
755 ry
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
757 dom_string_unref(attr
);
759 svgtiny_parse_paint_attributes(ellipse
, &state
);
760 svgtiny_parse_transform_attributes(ellipse
, &state
);
762 if (rx
< 0 || ry
< 0) {
763 state
.diagram
->error_line
= -1; /* ellipse->line; */
764 state
.diagram
->error_message
= "ellipse: rx or ry missing "
766 return svgtiny_SVG_ERROR
;
768 if (rx
== 0 || ry
== 0)
771 p
= malloc(32 * sizeof p
[0]);
773 return svgtiny_OUT_OF_MEMORY
;
775 p
[0] = svgtiny_PATH_MOVE
;
778 p
[3] = svgtiny_PATH_BEZIER
;
780 p
[5] = y
+ ry
* KAPPA
;
781 p
[6] = x
+ rx
* KAPPA
;
785 p
[10] = svgtiny_PATH_BEZIER
;
786 p
[11] = x
- rx
* KAPPA
;
789 p
[14] = y
+ ry
* KAPPA
;
792 p
[17] = svgtiny_PATH_BEZIER
;
794 p
[19] = y
- ry
* KAPPA
;
795 p
[20] = x
- rx
* KAPPA
;
799 p
[24] = svgtiny_PATH_BEZIER
;
800 p
[25] = x
+ rx
* KAPPA
;
803 p
[28] = y
- ry
* KAPPA
;
806 p
[31] = svgtiny_PATH_CLOSE
;
808 return svgtiny_add_path(p
, 32, &state
);
813 * Parse a <line> element node.
816 svgtiny_code
svgtiny_parse_line(dom_element
*line
,
817 struct svgtiny_parse_state state
)
819 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
824 exc
= dom_element_get_attribute(line
, state
.interned_x1
, &attr
);
825 if (exc
!= DOM_NO_ERR
)
826 return svgtiny_LIBDOM_ERROR
;
828 x1
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
830 dom_string_unref(attr
);
832 exc
= dom_element_get_attribute(line
, state
.interned_y1
, &attr
);
833 if (exc
!= DOM_NO_ERR
)
834 return svgtiny_LIBDOM_ERROR
;
836 y1
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
838 dom_string_unref(attr
);
840 exc
= dom_element_get_attribute(line
, state
.interned_x2
, &attr
);
841 if (exc
!= DOM_NO_ERR
)
842 return svgtiny_LIBDOM_ERROR
;
844 x2
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
846 dom_string_unref(attr
);
848 exc
= dom_element_get_attribute(line
, state
.interned_y2
, &attr
);
849 if (exc
!= DOM_NO_ERR
)
850 return svgtiny_LIBDOM_ERROR
;
852 y2
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
854 dom_string_unref(attr
);
856 svgtiny_parse_paint_attributes(line
, &state
);
857 svgtiny_parse_transform_attributes(line
, &state
);
859 p
= malloc(7 * sizeof p
[0]);
861 return svgtiny_OUT_OF_MEMORY
;
863 p
[0] = svgtiny_PATH_MOVE
;
866 p
[3] = svgtiny_PATH_LINE
;
869 p
[6] = svgtiny_PATH_CLOSE
;
871 return svgtiny_add_path(p
, 7, &state
);
876 * Parse a <polyline> or <polygon> element node.
878 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
879 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
882 svgtiny_code
svgtiny_parse_poly(dom_element
*poly
,
883 struct svgtiny_parse_state state
, bool polygon
)
885 dom_string
*points_str
;
891 svgtiny_parse_paint_attributes(poly
, &state
);
892 svgtiny_parse_transform_attributes(poly
, &state
);
894 exc
= dom_element_get_attribute(poly
, state
.interned_points
,
896 if (exc
!= DOM_NO_ERR
)
897 return svgtiny_LIBDOM_ERROR
;
899 if (points_str
== NULL
) {
900 state
.diagram
->error_line
= -1; /* poly->line; */
901 state
.diagram
->error_message
=
902 "polyline/polygon: missing points attribute";
903 return svgtiny_SVG_ERROR
;
906 s
= points
= strndup(dom_string_data(points_str
),
907 dom_string_byte_length(points_str
));
908 dom_string_unref(points_str
);
909 /* read points attribute */
911 return svgtiny_OUT_OF_MEMORY
;
912 /* allocate space for path: it will never have more elements than s */
913 p
= malloc(sizeof p
[0] * strlen(s
));
916 return svgtiny_OUT_OF_MEMORY
;
919 /* parse s and build path */
920 for (i
= 0; s
[i
]; i
++)
928 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
930 p
[i
++] = svgtiny_PATH_MOVE
;
932 p
[i
++] = svgtiny_PATH_LINE
;
941 p
[i
++] = svgtiny_PATH_CLOSE
;
945 return svgtiny_add_path(p
, i
, &state
);
950 * Parse a <text> or <tspan> element node.
953 svgtiny_code
svgtiny_parse_text(dom_element
*text
,
954 struct svgtiny_parse_state state
)
956 float x
, y
, width
, height
;
961 svgtiny_parse_position_attributes(text
, state
,
962 &x
, &y
, &width
, &height
);
963 svgtiny_parse_font_attributes(text
, &state
);
964 svgtiny_parse_transform_attributes(text
, &state
);
966 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
967 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
968 /* state.ctm.e = px - state.origin_x; */
969 /* state.ctm.f = py - state.origin_y; */
971 /*struct css_style style = state.style;
972 style.font_size.value.length.value *= state.ctm.a;*/
974 exc
= dom_node_get_first_child(text
, &child
);
975 if (exc
!= DOM_NO_ERR
)
976 return svgtiny_LIBDOM_ERROR
;
977 while (child
!= NULL
) {
979 dom_node_type nodetype
;
980 svgtiny_code code
= svgtiny_OK
;
982 exc
= dom_node_get_node_type(child
, &nodetype
);
983 if (exc
!= DOM_NO_ERR
) {
984 dom_node_unref(child
);
985 return svgtiny_LIBDOM_ERROR
;
987 if (nodetype
== DOM_ELEMENT_NODE
) {
988 dom_string
*nodename
;
989 exc
= dom_node_get_node_name(child
, &nodename
);
990 if (exc
!= DOM_NO_ERR
) {
991 dom_node_unref(child
);
992 return svgtiny_LIBDOM_ERROR
;
994 if (dom_string_caseless_isequal(nodename
,
995 state
.interned_tspan
))
996 code
= svgtiny_parse_text((dom_element
*)child
,
998 dom_string_unref(nodename
);
999 } else if (nodetype
== DOM_TEXT_NODE
) {
1000 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
1001 dom_string
*content
;
1002 if (shape
== NULL
) {
1003 dom_node_unref(child
);
1004 return svgtiny_OUT_OF_MEMORY
;
1006 exc
= dom_text_get_whole_text(child
, &content
);
1007 if (exc
!= DOM_NO_ERR
) {
1008 dom_node_unref(child
);
1009 return svgtiny_LIBDOM_ERROR
;
1011 if (content
!= NULL
) {
1012 shape
->text
= strndup(dom_string_data(content
),
1013 dom_string_byte_length(content
));
1014 dom_string_unref(content
);
1016 shape
->text
= strdup("");
1020 state
.diagram
->shape_count
++;
1023 if (code
!= svgtiny_OK
) {
1024 dom_node_unref(child
);
1027 exc
= dom_node_get_next_sibling(child
, &next
);
1028 dom_node_unref(child
);
1029 if (exc
!= DOM_NO_ERR
)
1030 return svgtiny_LIBDOM_ERROR
;
1039 * Parse x, y, width, and height attributes, if present.
1042 void svgtiny_parse_position_attributes(dom_element
*node
,
1043 const struct svgtiny_parse_state state
,
1044 float *x
, float *y
, float *width
, float *height
)
1051 *width
= state
.viewport_width
;
1052 *height
= state
.viewport_height
;
1054 exc
= dom_element_get_attribute(node
, state
.interned_x
, &attr
);
1055 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1056 *x
= svgtiny_parse_length(attr
, state
.viewport_width
, state
);
1057 dom_string_unref(attr
);
1060 exc
= dom_element_get_attribute(node
, state
.interned_y
, &attr
);
1061 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1062 *y
= svgtiny_parse_length(attr
, state
.viewport_height
, state
);
1063 dom_string_unref(attr
);
1066 exc
= dom_element_get_attribute(node
, state
.interned_width
, &attr
);
1067 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1068 *width
= svgtiny_parse_length(attr
, state
.viewport_width
,
1070 dom_string_unref(attr
);
1073 exc
= dom_element_get_attribute(node
, state
.interned_height
, &attr
);
1074 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1075 *height
= svgtiny_parse_length(attr
, state
.viewport_height
,
1077 dom_string_unref(attr
);
1083 * Parse a length as a number of pixels.
1086 static float _svgtiny_parse_length(const char *s
, int viewport_size
,
1087 const struct svgtiny_parse_state state
)
1089 int num_length
= strspn(s
, "0123456789+-.");
1090 const char *unit
= s
+ num_length
;
1091 float n
= atof((const char *) s
);
1092 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1098 } else if (unit
[0] == '%') {
1099 return n
/ 100.0 * viewport_size
;
1100 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
1101 return n
* font_size
;
1102 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
1103 return n
/ 2.0 * font_size
;
1104 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
1106 } else if (unit
[0] == 'p' && unit
[1] == 't') {
1108 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
1110 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
1111 return n
* 3.543307;
1112 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
1113 return n
* 35.43307;
1114 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
1121 float svgtiny_parse_length(dom_string
*s
, int viewport_size
,
1122 const struct svgtiny_parse_state state
)
1124 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1125 float ret
= _svgtiny_parse_length(ss
, viewport_size
, state
);
1131 * Parse paint attributes, if present.
1134 void svgtiny_parse_paint_attributes(dom_element
*node
,
1135 struct svgtiny_parse_state
*state
)
1140 exc
= dom_element_get_attribute(node
, state
->interned_fill
, &attr
);
1141 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1142 svgtiny_parse_color(attr
, &state
->fill
, state
);
1143 dom_string_unref(attr
);
1146 exc
= dom_element_get_attribute(node
, state
->interned_stroke
, &attr
);
1147 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1148 svgtiny_parse_color(attr
, &state
->stroke
, state
);
1149 dom_string_unref(attr
);
1152 exc
= dom_element_get_attribute(node
, state
->interned_stroke_width
, &attr
);
1153 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1154 state
->stroke_width
= svgtiny_parse_length(attr
,
1155 state
->viewport_width
, *state
);
1156 dom_string_unref(attr
);
1159 exc
= dom_element_get_attribute(node
, state
->interned_style
, &attr
);
1160 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1161 char *style
= strndup(dom_string_data(attr
),
1162 dom_string_byte_length(attr
));
1165 if ((s
= strstr(style
, "fill:"))) {
1169 value
= strndup(s
, strcspn(s
, "; "));
1170 _svgtiny_parse_color(value
, &state
->fill
, state
);
1173 if ((s
= strstr(style
, "stroke:"))) {
1177 value
= strndup(s
, strcspn(s
, "; "));
1178 _svgtiny_parse_color(value
, &state
->stroke
, state
);
1181 if ((s
= strstr(style
, "stroke-width:"))) {
1185 value
= strndup(s
, strcspn(s
, "; "));
1186 state
->stroke_width
= _svgtiny_parse_length(value
,
1187 state
->viewport_width
, *state
);
1191 dom_string_unref(attr
);
1200 static void _svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
1201 struct svgtiny_parse_state
*state
)
1203 unsigned int r
, g
, b
;
1205 size_t len
= strlen(s
);
1206 char *id
= 0, *rparen
;
1208 if (len
== 4 && s
[0] == '#') {
1209 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
1210 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1212 } else if (len
== 7 && s
[0] == '#') {
1213 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1214 *c
= svgtiny_RGB(r
, g
, b
);
1216 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1217 s
[3] == '(' && s
[len
- 1] == ')') {
1218 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1219 *c
= svgtiny_RGB(r
, g
, b
);
1220 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1224 *c
= svgtiny_RGB(r
, g
, b
);
1227 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1228 *c
= svgtiny_TRANSPARENT
;
1230 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1236 rparen
= strchr(id
, ')');
1239 svgtiny_find_gradient(id
, state
);
1241 fprintf(stderr
, "linear_gradient_stop_count %i\n",
1242 state
->linear_gradient_stop_count
);
1243 if (state
->linear_gradient_stop_count
== 0)
1244 *c
= svgtiny_TRANSPARENT
;
1245 else if (state
->linear_gradient_stop_count
== 1)
1246 *c
= state
->gradient_stop
[0].color
;
1248 *c
= svgtiny_LINEAR_GRADIENT
;
1252 const struct svgtiny_named_color
*named_color
;
1253 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1255 *c
= named_color
->color
;
1259 void svgtiny_parse_color(dom_string
*s
, svgtiny_colour
*c
,
1260 struct svgtiny_parse_state
*state
)
1262 char *ss
= strndup(dom_string_data(s
), dom_string_byte_length(s
));
1263 _svgtiny_parse_color(ss
, c
, state
);
1268 * Parse font attributes, if present.
1271 void svgtiny_parse_font_attributes(dom_element
*node
,
1272 struct svgtiny_parse_state
*state
)
1274 /* TODO: Implement this, it never used to be */
1277 #ifdef WRITTEN_THIS_PROPERLY
1278 const xmlAttr
*attr
;
1282 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1283 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1284 /*if (css_parse_length(
1285 (const char *) attr->children->content,
1286 &state->style.font_size.value.length,
1288 state->style.font_size.size =
1289 CSS_FONT_SIZE_LENGTH;
1298 * Parse transform attributes, if present.
1300 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1303 void svgtiny_parse_transform_attributes(dom_element
*node
,
1304 struct svgtiny_parse_state
*state
)
1310 exc
= dom_element_get_attribute(node
, state
->interned_transform
,
1312 if (exc
== DOM_NO_ERR
&& attr
!= NULL
) {
1313 transform
= strndup(dom_string_data(attr
),
1314 dom_string_byte_length(attr
));
1315 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1316 &state
->ctm
.c
, &state
->ctm
.d
,
1317 &state
->ctm
.e
, &state
->ctm
.f
);
1319 dom_string_unref(attr
);
1325 * Parse a transform string.
1328 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1329 float *mc
, float *md
, float *me
, float *mf
)
1331 float a
, b
, c
, d
, e
, f
;
1332 float za
, zb
, zc
, zd
, ze
, zf
;
1337 for (i
= 0; s
[i
]; i
++)
1345 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
1346 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
1348 else if (sscanf(s
, "translate (%f %f) %n",
1351 else if (sscanf(s
, "translate (%f) %n",
1354 else if (sscanf(s
, "scale (%f %f) %n",
1357 else if (sscanf(s
, "scale (%f) %n",
1360 else if (sscanf(s
, "rotate (%f %f %f) %n",
1361 &angle
, &x
, &y
, &n
) == 3) {
1362 angle
= angle
/ 180 * M_PI
;
1367 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1368 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1369 } else if (sscanf(s
, "rotate (%f) %n",
1371 angle
= angle
/ 180 * M_PI
;
1376 } else if (sscanf(s
, "skewX (%f) %n",
1378 angle
= angle
/ 180 * M_PI
;
1380 } else if (sscanf(s
, "skewY (%f) %n",
1382 angle
= angle
/ 180 * M_PI
;
1386 za
= *ma
* a
+ *mc
* b
;
1387 zb
= *mb
* a
+ *md
* b
;
1388 zc
= *ma
* c
+ *mc
* d
;
1389 zd
= *mb
* c
+ *md
* d
;
1390 ze
= *ma
* e
+ *mc
* f
+ *me
;
1391 zf
= *mb
* e
+ *md
* f
+ *mf
;
1404 * Add a path to the svgtiny_diagram.
1407 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1408 struct svgtiny_parse_state
*state
)
1410 struct svgtiny_shape
*shape
;
1412 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1413 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1415 svgtiny_transform_path(p
, n
, state
);
1417 shape
= svgtiny_add_shape(state
);
1420 return svgtiny_OUT_OF_MEMORY
;
1423 shape
->path_length
= n
;
1424 state
->diagram
->shape_count
++;
1431 * Add a svgtiny_shape to the svgtiny_diagram.
1434 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1436 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1437 (state
->diagram
->shape_count
+ 1) *
1438 sizeof (state
->diagram
->shape
[0]));
1441 state
->diagram
->shape
= shape
;
1443 shape
+= state
->diagram
->shape_count
;
1445 shape
->path_length
= 0;
1447 shape
->fill
= state
->fill
;
1448 shape
->stroke
= state
->stroke
;
1449 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1450 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1451 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1452 shape
->stroke_width
= 1;
1459 * Apply the current transformation matrix to a path.
1462 void svgtiny_transform_path(float *p
, unsigned int n
,
1463 struct svgtiny_parse_state
*state
)
1467 for (j
= 0; j
!= n
; ) {
1468 unsigned int points
= 0;
1470 switch ((int) p
[j
]) {
1471 case svgtiny_PATH_MOVE
:
1472 case svgtiny_PATH_LINE
:
1475 case svgtiny_PATH_CLOSE
:
1478 case svgtiny_PATH_BEZIER
:
1485 for (k
= 0; k
!= points
; k
++) {
1486 float x0
= p
[j
], y0
= p
[j
+ 1];
1487 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1489 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1500 * Free all memory used by a diagram.
1503 void svgtiny_free(struct svgtiny_diagram
*svg
)
1508 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1509 free(svg
->shape
[i
].path
);
1510 free(svg
->shape
[i
].text
);
1518 #ifndef HAVE_STRNDUP
1519 char *svgtiny_strndup(const char *s
, size_t n
)
1524 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1527 s2
= malloc(len
+ 1);