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_node
*svg
,
30 struct svgtiny_parse_state state
);
31 static svgtiny_code
svgtiny_parse_path(dom_node
*path
,
32 struct svgtiny_parse_state state
);
33 static svgtiny_code
svgtiny_parse_rect(dom_node
*rect
,
34 struct svgtiny_parse_state state
);
35 static svgtiny_code
svgtiny_parse_circle(dom_node
*circle
,
36 struct svgtiny_parse_state state
);
37 static svgtiny_code
svgtiny_parse_ellipse(dom_node
*ellipse
,
38 struct svgtiny_parse_state state
);
39 static svgtiny_code
svgtiny_parse_line(dom_node
*line
,
40 struct svgtiny_parse_state state
);
41 static svgtiny_code
svgtiny_parse_poly(dom_node
*poly
,
42 struct svgtiny_parse_state state
, bool polygon
);
43 static svgtiny_code
svgtiny_parse_text(dom_node
*text
,
44 struct svgtiny_parse_state state
);
45 static void svgtiny_parse_position_attributes(const dom_node
*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_node
*node
,
49 struct svgtiny_parse_state
*state
);
50 static void svgtiny_parse_font_attributes(const dom_node
*node
,
51 struct svgtiny_parse_state
*state
);
52 static void svgtiny_parse_transform_attributes(dom_node
*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
= malloc(sizeof *diagram
);
71 diagram
->shape_count
= 0;
72 diagram
->error_line
= 0;
73 diagram
->error_message
= 0;
78 static void ignore_msg(uint32_t severity
, void *ctx
, const char *msg
, ...)
86 * Parse a block of memory into a svgtiny_diagram.
89 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
90 const char *buffer
, size_t size
, const char *url
,
91 int viewport_width
, int viewport_height
)
93 dom_document
*document
;
95 dom_xml_parser
*parser
;
99 lwc_string
*svg_name_lwc
;
100 struct svgtiny_parse_state state
;
101 float x
, y
, width
, height
;
110 parser
= dom_xml_parser_create(NULL
, NULL
,
111 ignore_msg
, NULL
, &document
);
114 return svgtiny_LIBDOM_ERROR
;
116 err
= dom_xml_parser_parse_chunk(parser
, (uint8_t *)buffer
, size
);
117 if (err
!= DOM_XML_OK
) {
118 dom_node_unref(document
);
119 dom_xml_parser_destroy(parser
);
120 return svgtiny_LIBDOM_ERROR
;
123 err
= dom_xml_parser_completed(parser
);
124 if (err
!= DOM_XML_OK
) {
125 dom_node_unref(document
);
126 dom_xml_parser_destroy(parser
);
127 return svgtiny_LIBDOM_ERROR
;
130 /* We're done parsing, drop the parser.
131 * We now own the document entirely.
133 dom_xml_parser_destroy(parser
);
135 /* find root <svg> element */
136 exc
= dom_document_get_document_element(document
, &svg
);
137 if (exc
!= DOM_NO_ERR
) {
138 dom_node_unref(document
);
139 return svgtiny_LIBDOM_ERROR
;
141 exc
= dom_node_get_node_name(svg
, &svg_name
);
142 if (exc
!= DOM_NO_ERR
) {
144 dom_node_unref(document
);
145 return svgtiny_LIBDOM_ERROR
;
147 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
148 &svg_name_lwc
) != lwc_error_ok
) {
149 dom_string_unref(svg_name
);
151 dom_node_unref(document
);
152 return svgtiny_LIBDOM_ERROR
;
154 if (!dom_string_caseless_lwc_isequal(svg_name
, svg_name_lwc
)) {
155 lwc_string_unref(svg_name_lwc
);
156 dom_string_unref(svg_name
);
158 dom_node_unref(document
);
159 return svgtiny_NOT_SVG
;
162 lwc_string_unref(svg_name_lwc
);
163 dom_string_unref(svg_name
);
165 /* get graphic dimensions */
166 state
.diagram
= diagram
;
167 state
.document
= document
;
168 state
.viewport_width
= viewport_width
;
169 state
.viewport_height
= viewport_height
;
170 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
171 diagram
->width
= width
;
172 diagram
->height
= height
;
174 /* set up parsing state */
175 state
.viewport_width
= width
;
176 state
.viewport_height
= height
;
177 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
180 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
181 state
.ctm
.e
= 0; /*x;*/
182 state
.ctm
.f
= 0; /*y;*/
183 /*state.style = css_base_style;
184 state.style.font_size.value.length.value = option_font_size * 0.1;*/
185 state
.fill
= 0x000000;
186 state
.stroke
= svgtiny_TRANSPARENT
;
187 state
.stroke_width
= 1;
188 state
.linear_gradient_stop_count
= 0;
191 code
= svgtiny_parse_svg(svg
, state
);
194 dom_node_unref(document
);
201 * Parse a <svg> or <g> element node.
204 svgtiny_code
svgtiny_parse_svg(dom_node
*svg
,
205 struct svgtiny_parse_state state
)
207 float x
, y
, width
, height
;
211 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
212 svgtiny_parse_paint_attributes(svg
, &state
);
213 svgtiny_parse_font_attributes(svg
, &state
);
216 view_box
= xmlHasProp(svg
, (const xmlChar
*) "viewBox");
218 const char *s
= (const char *) view_box
->children
->content
;
219 float min_x
, min_y
, vwidth
, vheight
;
220 if (sscanf(s
, "%f,%f,%f,%f",
221 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
222 sscanf(s
, "%f %f %f %f",
223 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
224 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
225 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
226 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
227 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
231 svgtiny_parse_transform_attributes(svg
, &state
);
233 for (child
= svg
->children
; child
; child
= child
->next
) {
234 svgtiny_code code
= svgtiny_OK
;
236 if (child
->type
== XML_ELEMENT_NODE
) {
237 const char *name
= (const char *) child
->name
;
238 if (strcmp(name
, "svg") == 0)
239 code
= svgtiny_parse_svg(child
, state
);
240 else if (strcmp(name
, "g") == 0)
241 code
= svgtiny_parse_svg(child
, state
);
242 else if (strcmp(name
, "a") == 0)
243 code
= svgtiny_parse_svg(child
, state
);
244 else if (strcmp(name
, "path") == 0)
245 code
= svgtiny_parse_path(child
, state
);
246 else if (strcmp(name
, "rect") == 0)
247 code
= svgtiny_parse_rect(child
, state
);
248 else if (strcmp(name
, "circle") == 0)
249 code
= svgtiny_parse_circle(child
, state
);
250 else if (strcmp(name
, "ellipse") == 0)
251 code
= svgtiny_parse_ellipse(child
, state
);
252 else if (strcmp(name
, "line") == 0)
253 code
= svgtiny_parse_line(child
, state
);
254 else if (strcmp(name
, "polyline") == 0)
255 code
= svgtiny_parse_poly(child
, state
, false);
256 else if (strcmp(name
, "polygon") == 0)
257 code
= svgtiny_parse_poly(child
, state
, true);
258 else if (strcmp(name
, "text") == 0)
259 code
= svgtiny_parse_text(child
, state
);
262 if (code
!= svgtiny_OK
)
272 * Parse a <path> element node.
274 * http://www.w3.org/TR/SVG11/paths#PathElement
277 svgtiny_code
svgtiny_parse_path(dom_node
*path
,
278 struct svgtiny_parse_state state
)
283 float last_x
= 0, last_y
= 0;
284 float last_cubic_x
= 0, last_cubic_y
= 0;
285 float last_quad_x
= 0, last_quad_y
= 0;
287 svgtiny_parse_paint_attributes(path
, &state
);
288 svgtiny_parse_transform_attributes(path
, &state
);
290 /* read d attribute */
291 s
= path_d
= (char *) xmlGetProp(path
, (const xmlChar
*) "d");
293 state
.diagram
->error_line
= path
->line
;
294 state
.diagram
->error_message
= "path: missing d attribute";
295 return svgtiny_SVG_ERROR
;
298 /* allocate space for path: it will never have more elements than d */
299 p
= malloc(sizeof p
[0] * strlen(s
));
301 return svgtiny_OUT_OF_MEMORY
;
303 /* parse d and build path */
304 for (i
= 0; s
[i
]; i
++)
311 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
314 /* moveto (M, m), lineto (L, l) (2 arguments) */
315 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
316 /*LOG(("moveto or lineto"));*/
317 if (*command
== 'M' || *command
== 'm')
318 plot_command
= svgtiny_PATH_MOVE
;
320 plot_command
= svgtiny_PATH_LINE
;
322 p
[i
++] = plot_command
;
323 if ('a' <= *command
) {
327 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
329 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
332 plot_command
= svgtiny_PATH_LINE
;
333 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
335 /* closepath (Z, z) (no arguments) */
336 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
337 /*LOG(("closepath"));*/
338 p
[i
++] = svgtiny_PATH_CLOSE
;
341 /* horizontal lineto (H, h) (1 argument) */
342 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
343 /*LOG(("horizontal lineto"));*/
345 p
[i
++] = svgtiny_PATH_LINE
;
348 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
350 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
352 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
354 /* vertical lineto (V, v) (1 argument) */
355 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
356 /*LOG(("vertical lineto"));*/
358 p
[i
++] = svgtiny_PATH_LINE
;
361 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
362 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
365 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
367 /* curveto (C, c) (6 arguments) */
368 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
369 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
370 /*LOG(("curveto"));*/
372 p
[i
++] = svgtiny_PATH_BEZIER
;
373 if (*command
== 'c') {
383 p
[i
++] = last_cubic_x
= x2
;
384 p
[i
++] = last_cubic_y
= y2
;
385 p
[i
++] = last_quad_x
= last_x
= x
;
386 p
[i
++] = last_quad_y
= last_y
= y
;
388 } while (sscanf(s
, "%f %f %f %f %f %f %n",
389 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
391 /* shorthand/smooth curveto (S, s) (4 arguments) */
392 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
393 &x2
, &y2
, &x
, &y
, &n
) == 5) {
394 /*LOG(("shorthand/smooth curveto"));*/
396 p
[i
++] = svgtiny_PATH_BEZIER
;
397 x1
= last_x
+ (last_x
- last_cubic_x
);
398 y1
= last_y
+ (last_y
- last_cubic_y
);
399 if (*command
== 's') {
407 p
[i
++] = last_cubic_x
= x2
;
408 p
[i
++] = last_cubic_y
= y2
;
409 p
[i
++] = last_quad_x
= last_x
= x
;
410 p
[i
++] = last_quad_y
= last_y
= y
;
412 } while (sscanf(s
, "%f %f %f %f %n",
413 &x2
, &y2
, &x
, &y
, &n
) == 4);
415 /* quadratic Bezier curveto (Q, q) (4 arguments) */
416 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
417 &x1
, &y1
, &x
, &y
, &n
) == 5) {
418 /*LOG(("quadratic Bezier curveto"));*/
420 p
[i
++] = svgtiny_PATH_BEZIER
;
423 if (*command
== 'q') {
429 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
430 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
431 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
432 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
433 p
[i
++] = last_cubic_x
= last_x
= x
;
434 p
[i
++] = last_cubic_y
= last_y
= y
;
436 } while (sscanf(s
, "%f %f %f %f %n",
437 &x1
, &y1
, &x
, &y
, &n
) == 4);
439 /* shorthand/smooth quadratic Bezier curveto (T, t)
441 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
443 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
445 p
[i
++] = svgtiny_PATH_BEZIER
;
446 x1
= last_x
+ (last_x
- last_quad_x
);
447 y1
= last_y
+ (last_y
- last_quad_y
);
450 if (*command
== 't') {
456 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
457 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
458 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
459 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
460 p
[i
++] = last_cubic_x
= last_x
= x
;
461 p
[i
++] = last_cubic_y
= last_y
= y
;
463 } while (sscanf(s
, "%f %f %n",
466 /* elliptical arc (A, a) (7 arguments) */
467 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
468 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
471 p
[i
++] = svgtiny_PATH_LINE
;
472 if (*command
== 'a') {
476 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
478 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
481 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
482 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
486 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
494 /* no real segments in path */
499 return svgtiny_add_path(p
, i
, &state
);
504 * Parse a <rect> element node.
506 * http://www.w3.org/TR/SVG11/shapes#RectElement
509 svgtiny_code
svgtiny_parse_rect(dom_node
*rect
,
510 struct svgtiny_parse_state state
)
512 float x
, y
, width
, height
;
515 svgtiny_parse_position_attributes(rect
, state
,
516 &x
, &y
, &width
, &height
);
517 svgtiny_parse_paint_attributes(rect
, &state
);
518 svgtiny_parse_transform_attributes(rect
, &state
);
520 p
= malloc(13 * sizeof p
[0]);
522 return svgtiny_OUT_OF_MEMORY
;
524 p
[0] = svgtiny_PATH_MOVE
;
527 p
[3] = svgtiny_PATH_LINE
;
530 p
[6] = svgtiny_PATH_LINE
;
533 p
[9] = svgtiny_PATH_LINE
;
536 p
[12] = svgtiny_PATH_CLOSE
;
538 return svgtiny_add_path(p
, 13, &state
);
543 * Parse a <circle> element node.
546 svgtiny_code
svgtiny_parse_circle(dom_node
*circle
,
547 struct svgtiny_parse_state state
)
549 float x
= 0, y
= 0, r
= -1;
553 for (attr
= circle
->properties
; attr
; attr
= attr
->next
) {
554 const char *name
= (const char *) attr
->name
;
555 const char *content
= (const char *) attr
->children
->content
;
556 if (strcmp(name
, "cx") == 0)
557 x
= svgtiny_parse_length(content
,
558 state
.viewport_width
, state
);
559 else if (strcmp(name
, "cy") == 0)
560 y
= svgtiny_parse_length(content
,
561 state
.viewport_height
, state
);
562 else if (strcmp(name
, "r") == 0)
563 r
= svgtiny_parse_length(content
,
564 state
.viewport_width
, state
);
566 svgtiny_parse_paint_attributes(circle
, &state
);
567 svgtiny_parse_transform_attributes(circle
, &state
);
570 state
.diagram
->error_line
= circle
->line
;
571 state
.diagram
->error_message
= "circle: r missing or negative";
572 return svgtiny_SVG_ERROR
;
577 p
= malloc(32 * sizeof p
[0]);
579 return svgtiny_OUT_OF_MEMORY
;
581 p
[0] = svgtiny_PATH_MOVE
;
584 p
[3] = svgtiny_PATH_BEZIER
;
586 p
[5] = y
+ r
* KAPPA
;
587 p
[6] = x
+ r
* KAPPA
;
591 p
[10] = svgtiny_PATH_BEZIER
;
592 p
[11] = x
- r
* KAPPA
;
595 p
[14] = y
+ r
* KAPPA
;
598 p
[17] = svgtiny_PATH_BEZIER
;
600 p
[19] = y
- r
* KAPPA
;
601 p
[20] = x
- r
* KAPPA
;
605 p
[24] = svgtiny_PATH_BEZIER
;
606 p
[25] = x
+ r
* KAPPA
;
609 p
[28] = y
- r
* KAPPA
;
612 p
[31] = svgtiny_PATH_CLOSE
;
614 return svgtiny_add_path(p
, 32, &state
);
619 * Parse an <ellipse> element node.
622 svgtiny_code
svgtiny_parse_ellipse(dom_node
*ellipse
,
623 struct svgtiny_parse_state state
)
625 float x
= 0, y
= 0, rx
= -1, ry
= -1;
629 for (attr
= ellipse
->properties
; attr
; attr
= attr
->next
) {
630 const char *name
= (const char *) attr
->name
;
631 const char *content
= (const char *) attr
->children
->content
;
632 if (strcmp(name
, "cx") == 0)
633 x
= svgtiny_parse_length(content
,
634 state
.viewport_width
, state
);
635 else if (strcmp(name
, "cy") == 0)
636 y
= svgtiny_parse_length(content
,
637 state
.viewport_height
, state
);
638 else if (strcmp(name
, "rx") == 0)
639 rx
= svgtiny_parse_length(content
,
640 state
.viewport_width
, state
);
641 else if (strcmp(name
, "ry") == 0)
642 ry
= svgtiny_parse_length(content
,
643 state
.viewport_width
, state
);
645 svgtiny_parse_paint_attributes(ellipse
, &state
);
646 svgtiny_parse_transform_attributes(ellipse
, &state
);
648 if (rx
< 0 || ry
< 0) {
649 state
.diagram
->error_line
= ellipse
->line
;
650 state
.diagram
->error_message
= "ellipse: rx or ry missing "
652 return svgtiny_SVG_ERROR
;
654 if (rx
== 0 || ry
== 0)
657 p
= malloc(32 * sizeof p
[0]);
659 return svgtiny_OUT_OF_MEMORY
;
661 p
[0] = svgtiny_PATH_MOVE
;
664 p
[3] = svgtiny_PATH_BEZIER
;
666 p
[5] = y
+ ry
* KAPPA
;
667 p
[6] = x
+ rx
* KAPPA
;
671 p
[10] = svgtiny_PATH_BEZIER
;
672 p
[11] = x
- rx
* KAPPA
;
675 p
[14] = y
+ ry
* KAPPA
;
678 p
[17] = svgtiny_PATH_BEZIER
;
680 p
[19] = y
- ry
* KAPPA
;
681 p
[20] = x
- rx
* KAPPA
;
685 p
[24] = svgtiny_PATH_BEZIER
;
686 p
[25] = x
+ rx
* KAPPA
;
689 p
[28] = y
- ry
* KAPPA
;
692 p
[31] = svgtiny_PATH_CLOSE
;
694 return svgtiny_add_path(p
, 32, &state
);
699 * Parse a <line> element node.
702 svgtiny_code
svgtiny_parse_line(dom_node
*line
,
703 struct svgtiny_parse_state state
)
705 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
709 for (attr
= line
->properties
; attr
; attr
= attr
->next
) {
710 const char *name
= (const char *) attr
->name
;
711 const char *content
= (const char *) attr
->children
->content
;
712 if (strcmp(name
, "x1") == 0)
713 x1
= svgtiny_parse_length(content
,
714 state
.viewport_width
, state
);
715 else if (strcmp(name
, "y1") == 0)
716 y1
= svgtiny_parse_length(content
,
717 state
.viewport_height
, state
);
718 else if (strcmp(name
, "x2") == 0)
719 x2
= svgtiny_parse_length(content
,
720 state
.viewport_width
, state
);
721 else if (strcmp(name
, "y2") == 0)
722 y2
= svgtiny_parse_length(content
,
723 state
.viewport_height
, state
);
725 svgtiny_parse_paint_attributes(line
, &state
);
726 svgtiny_parse_transform_attributes(line
, &state
);
728 p
= malloc(7 * sizeof p
[0]);
730 return svgtiny_OUT_OF_MEMORY
;
732 p
[0] = svgtiny_PATH_MOVE
;
735 p
[3] = svgtiny_PATH_LINE
;
738 p
[6] = svgtiny_PATH_CLOSE
;
740 return svgtiny_add_path(p
, 7, &state
);
745 * Parse a <polyline> or <polygon> element node.
747 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
748 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
751 svgtiny_code
svgtiny_parse_poly(dom_node
*poly
,
752 struct svgtiny_parse_state state
, bool polygon
)
758 svgtiny_parse_paint_attributes(poly
, &state
);
759 svgtiny_parse_transform_attributes(poly
, &state
);
761 /* read points attribute */
762 s
= points
= (char *) xmlGetProp(poly
, (const xmlChar
*) "points");
764 state
.diagram
->error_line
= poly
->line
;
765 state
.diagram
->error_message
=
766 "polyline/polygon: missing points attribute";
767 return svgtiny_SVG_ERROR
;
770 /* allocate space for path: it will never have more elements than s */
771 p
= malloc(sizeof p
[0] * strlen(s
));
774 return svgtiny_OUT_OF_MEMORY
;
777 /* parse s and build path */
778 for (i
= 0; s
[i
]; i
++)
786 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
788 p
[i
++] = svgtiny_PATH_MOVE
;
790 p
[i
++] = svgtiny_PATH_LINE
;
799 p
[i
++] = svgtiny_PATH_CLOSE
;
803 return svgtiny_add_path(p
, i
, &state
);
808 * Parse a <text> or <tspan> element node.
811 svgtiny_code
svgtiny_parse_text(dom_node
*text
,
812 struct svgtiny_parse_state state
)
814 float x
, y
, width
, height
;
818 svgtiny_parse_position_attributes(text
, state
,
819 &x
, &y
, &width
, &height
);
820 svgtiny_parse_font_attributes(text
, &state
);
821 svgtiny_parse_transform_attributes(text
, &state
);
823 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
824 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
825 /* state.ctm.e = px - state.origin_x; */
826 /* state.ctm.f = py - state.origin_y; */
828 /*struct css_style style = state.style;
829 style.font_size.value.length.value *= state.ctm.a;*/
831 for (child
= text
->children
; child
; child
= child
->next
) {
832 svgtiny_code code
= svgtiny_OK
;
834 if (child
->type
== XML_TEXT_NODE
) {
835 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
837 return svgtiny_OUT_OF_MEMORY
;
838 shape
->text
= strdup((const char *) child
->content
);
841 state
.diagram
->shape_count
++;
843 } else if (child
->type
== XML_ELEMENT_NODE
&&
844 strcmp((const char *) child
->name
,
846 code
= svgtiny_parse_text(child
, state
);
849 if (!code
!= svgtiny_OK
)
858 * Parse x, y, width, and height attributes, if present.
861 void svgtiny_parse_position_attributes(const dom_node
*node
,
862 const struct svgtiny_parse_state state
,
863 float *x
, float *y
, float *width
, float *height
)
869 *width
= state
.viewport_width
;
870 *height
= state
.viewport_height
;
872 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
873 const char *name
= (const char *) attr
->name
;
874 const char *content
= (const char *) attr
->children
->content
;
875 if (strcmp(name
, "x") == 0)
876 *x
= svgtiny_parse_length(content
,
877 state
.viewport_width
, state
);
878 else if (strcmp(name
, "y") == 0)
879 *y
= svgtiny_parse_length(content
,
880 state
.viewport_height
, state
);
881 else if (strcmp(name
, "width") == 0)
882 *width
= svgtiny_parse_length(content
,
883 state
.viewport_width
, state
);
884 else if (strcmp(name
, "height") == 0)
885 *height
= svgtiny_parse_length(content
,
886 state
.viewport_height
, state
);
892 * Parse a length as a number of pixels.
895 float svgtiny_parse_length(const char *s
, int viewport_size
,
896 const struct svgtiny_parse_state state
)
898 int num_length
= strspn(s
, "0123456789+-.");
899 const char *unit
= s
+ num_length
;
900 float n
= atof((const char *) s
);
901 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
907 } else if (unit
[0] == '%') {
908 return n
/ 100.0 * viewport_size
;
909 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
910 return n
* font_size
;
911 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
912 return n
/ 2.0 * font_size
;
913 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
915 } else if (unit
[0] == 'p' && unit
[1] == 't') {
917 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
919 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
921 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
923 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
932 * Parse paint attributes, if present.
935 void svgtiny_parse_paint_attributes(const dom_node
*node
,
936 struct svgtiny_parse_state
*state
)
940 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
941 const char *name
= (const char *) attr
->name
;
942 const char *content
= (const char *) attr
->children
->content
;
943 if (strcmp(name
, "fill") == 0)
944 svgtiny_parse_color(content
, &state
->fill
, state
);
945 else if (strcmp(name
, "stroke") == 0)
946 svgtiny_parse_color(content
, &state
->stroke
, state
);
947 else if (strcmp(name
, "stroke-width") == 0)
948 state
->stroke_width
= svgtiny_parse_length(content
,
949 state
->viewport_width
, *state
);
950 else if (strcmp(name
, "style") == 0) {
951 const char *style
= (const char *)
952 attr
->children
->content
;
955 if ((s
= strstr(style
, "fill:"))) {
959 value
= strndup(s
, strcspn(s
, "; "));
960 svgtiny_parse_color(value
, &state
->fill
, state
);
963 if ((s
= strstr(style
, "stroke:"))) {
967 value
= strndup(s
, strcspn(s
, "; "));
968 svgtiny_parse_color(value
, &state
->stroke
, state
);
971 if ((s
= strstr(style
, "stroke-width:"))) {
975 value
= strndup(s
, strcspn(s
, "; "));
976 state
->stroke_width
= svgtiny_parse_length(value
,
977 state
->viewport_width
, *state
);
989 void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
990 struct svgtiny_parse_state
*state
)
992 unsigned int r
, g
, b
;
994 size_t len
= strlen(s
);
995 char *id
= 0, *rparen
;
997 if (len
== 4 && s
[0] == '#') {
998 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
999 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
1001 } else if (len
== 7 && s
[0] == '#') {
1002 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
1003 *c
= svgtiny_RGB(r
, g
, b
);
1005 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
1006 s
[3] == '(' && s
[len
- 1] == ')') {
1007 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
1008 *c
= svgtiny_RGB(r
, g
, b
);
1009 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
1013 *c
= svgtiny_RGB(r
, g
, b
);
1016 } else if (len
== 4 && strcmp(s
, "none") == 0) {
1017 *c
= svgtiny_TRANSPARENT
;
1019 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
1025 rparen
= strchr(id
, ')');
1028 svgtiny_find_gradient(id
, state
);
1030 fprintf(stderr
, "linear_gradient_stop_count %i\n",
1031 state
->linear_gradient_stop_count
);
1032 if (state
->linear_gradient_stop_count
== 0)
1033 *c
= svgtiny_TRANSPARENT
;
1034 else if (state
->linear_gradient_stop_count
== 1)
1035 *c
= state
->gradient_stop
[0].color
;
1037 *c
= svgtiny_LINEAR_GRADIENT
;
1041 const struct svgtiny_named_color
*named_color
;
1042 named_color
= svgtiny_color_lookup(s
, strlen(s
));
1044 *c
= named_color
->color
;
1050 * Parse font attributes, if present.
1053 void svgtiny_parse_font_attributes(const dom_node
*node
,
1054 struct svgtiny_parse_state
*state
)
1056 const xmlAttr
*attr
;
1060 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1061 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1062 /*if (css_parse_length(
1063 (const char *) attr->children->content,
1064 &state->style.font_size.value.length,
1066 state->style.font_size.size =
1067 CSS_FONT_SIZE_LENGTH;
1075 * Parse transform attributes, if present.
1077 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1080 void svgtiny_parse_transform_attributes(dom_node
*node
,
1081 struct svgtiny_parse_state
*state
)
1085 /* parse transform */
1086 transform
= (char *) xmlGetProp(node
, (const xmlChar
*) "transform");
1088 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1089 &state
->ctm
.c
, &state
->ctm
.d
,
1090 &state
->ctm
.e
, &state
->ctm
.f
);
1097 * Parse a transform string.
1100 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1101 float *mc
, float *md
, float *me
, float *mf
)
1103 float a
, b
, c
, d
, e
, f
;
1104 float za
, zb
, zc
, zd
, ze
, zf
;
1109 for (i
= 0; s
[i
]; i
++)
1117 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
1118 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
1120 else if (sscanf(s
, "translate (%f %f) %n",
1123 else if (sscanf(s
, "translate (%f) %n",
1126 else if (sscanf(s
, "scale (%f %f) %n",
1129 else if (sscanf(s
, "scale (%f) %n",
1132 else if (sscanf(s
, "rotate (%f %f %f) %n",
1133 &angle
, &x
, &y
, &n
) == 3) {
1134 angle
= angle
/ 180 * M_PI
;
1139 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1140 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1141 } else if (sscanf(s
, "rotate (%f) %n",
1143 angle
= angle
/ 180 * M_PI
;
1148 } else if (sscanf(s
, "skewX (%f) %n",
1150 angle
= angle
/ 180 * M_PI
;
1152 } else if (sscanf(s
, "skewY (%f) %n",
1154 angle
= angle
/ 180 * M_PI
;
1158 za
= *ma
* a
+ *mc
* b
;
1159 zb
= *mb
* a
+ *md
* b
;
1160 zc
= *ma
* c
+ *mc
* d
;
1161 zd
= *mb
* c
+ *md
* d
;
1162 ze
= *ma
* e
+ *mc
* f
+ *me
;
1163 zf
= *mb
* e
+ *md
* f
+ *mf
;
1176 * Add a path to the svgtiny_diagram.
1179 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1180 struct svgtiny_parse_state
*state
)
1182 struct svgtiny_shape
*shape
;
1184 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1185 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1187 svgtiny_transform_path(p
, n
, state
);
1189 shape
= svgtiny_add_shape(state
);
1192 return svgtiny_OUT_OF_MEMORY
;
1195 shape
->path_length
= n
;
1196 state
->diagram
->shape_count
++;
1203 * Add a svgtiny_shape to the svgtiny_diagram.
1206 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1208 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1209 (state
->diagram
->shape_count
+ 1) *
1210 sizeof (state
->diagram
->shape
[0]));
1213 state
->diagram
->shape
= shape
;
1215 shape
+= state
->diagram
->shape_count
;
1217 shape
->path_length
= 0;
1219 shape
->fill
= state
->fill
;
1220 shape
->stroke
= state
->stroke
;
1221 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1222 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1223 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1224 shape
->stroke_width
= 1;
1231 * Apply the current transformation matrix to a path.
1234 void svgtiny_transform_path(float *p
, unsigned int n
,
1235 struct svgtiny_parse_state
*state
)
1239 for (j
= 0; j
!= n
; ) {
1240 unsigned int points
= 0;
1242 switch ((int) p
[j
]) {
1243 case svgtiny_PATH_MOVE
:
1244 case svgtiny_PATH_LINE
:
1247 case svgtiny_PATH_CLOSE
:
1250 case svgtiny_PATH_BEZIER
:
1257 for (k
= 0; k
!= points
; k
++) {
1258 float x0
= p
[j
], y0
= p
[j
+ 1];
1259 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1261 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1272 * Free all memory used by a diagram.
1275 void svgtiny_free(struct svgtiny_diagram
*svg
)
1280 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1281 free(svg
->shape
[i
].path
);
1282 free(svg
->shape
[i
].text
);
1290 #ifndef HAVE_STRNDUP
1291 char *svgtiny_strndup(const char *s
, size_t n
)
1296 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1299 s2
= malloc(len
+ 1);