2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2008 James Bursa <james@semichrome.net>
8 #define _GNU_SOURCE /* for strndup */
16 #include <libxml/parser.h>
17 #include <libxml/debugXML.h>
19 #include "svgtiny_internal.h"
22 static svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
23 struct svgtiny_parse_state state
);
24 static svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
25 struct svgtiny_parse_state state
);
26 static svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
27 struct svgtiny_parse_state state
);
28 static svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
29 struct svgtiny_parse_state state
);
30 static svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
31 struct svgtiny_parse_state state
);
32 static svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
33 struct svgtiny_parse_state state
, bool polygon
);
34 static svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
35 struct svgtiny_parse_state state
);
36 static void svgtiny_parse_position_attributes(const xmlNode
*node
,
37 const struct svgtiny_parse_state state
,
38 float *x
, float *y
, float *width
, float *height
);
39 static void svgtiny_parse_paint_attributes(const xmlNode
*node
,
40 struct svgtiny_parse_state
*state
);
41 static void svgtiny_parse_font_attributes(const xmlNode
*node
,
42 struct svgtiny_parse_state
*state
);
43 static void svgtiny_parse_transform_attributes(xmlNode
*node
,
44 struct svgtiny_parse_state
*state
);
45 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
46 struct svgtiny_parse_state
*state
);
50 * Create a new svgtiny_diagram structure.
53 struct svgtiny_diagram
*svgtiny_create(void)
55 struct svgtiny_diagram
*diagram
;
57 diagram
= malloc(sizeof *diagram
);
62 diagram
->shape_count
= 0;
63 diagram
->error_line
= 0;
64 diagram
->error_message
= 0;
71 * Parse a block of memory into a svgtiny_diagram.
74 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
75 const char *buffer
, size_t size
, const char *url
,
76 int viewport_width
, int viewport_height
)
80 struct svgtiny_parse_state state
;
81 float x
, y
, width
, height
;
88 /* parse XML to tree */
89 document
= xmlReadMemory(buffer
, size
, url
, 0,
90 XML_PARSE_NONET
| XML_PARSE_COMPACT
);
92 return svgtiny_LIBXML_ERROR
;
94 /*xmlDebugDumpDocument(stderr, document);*/
96 /* find root <svg> element */
97 svg
= xmlDocGetRootElement(document
);
99 return svgtiny_NOT_SVG
;
100 if (strcmp((const char *) svg
->name
, "svg") != 0)
101 return svgtiny_NOT_SVG
;
103 /* get graphic dimensions */
104 state
.diagram
= diagram
;
105 state
.document
= document
;
106 state
.viewport_width
= viewport_width
;
107 state
.viewport_height
= viewport_height
;
108 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
109 diagram
->width
= width
;
110 diagram
->height
= height
;
112 /* set up parsing state */
113 state
.viewport_width
= width
;
114 state
.viewport_height
= height
;
115 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
118 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
119 state
.ctm
.e
= 0; /*x;*/
120 state
.ctm
.f
= 0; /*y;*/
121 /*state.style = css_base_style;
122 state.style.font_size.value.length.value = option_font_size * 0.1;*/
123 state
.fill
= 0x000000;
124 state
.stroke
= svgtiny_TRANSPARENT
;
125 state
.stroke_width
= 1;
126 state
.linear_gradient_stop_count
= 0;
129 code
= svgtiny_parse_svg(svg
, state
);
132 xmlFreeDoc(document
);
139 * Parse a <svg> or <g> element node.
142 svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
143 struct svgtiny_parse_state state
)
145 float x
, y
, width
, height
;
147 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
148 svgtiny_parse_paint_attributes(svg
, &state
);
149 svgtiny_parse_font_attributes(svg
, &state
);
152 xmlAttr
*view_box
= xmlHasProp(svg
, (const xmlChar
*) "viewBox");
154 const char *s
= (const char *) view_box
->children
->content
;
155 float min_x
, min_y
, vwidth
, vheight
;
156 if (sscanf(s
, "%f,%f,%f,%f",
157 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
158 sscanf(s
, "%f %f %f %f",
159 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
160 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
161 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
162 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
163 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
167 svgtiny_parse_transform_attributes(svg
, &state
);
169 for (xmlNode
*child
= svg
->children
; child
; child
= child
->next
) {
170 svgtiny_code code
= svgtiny_OK
;
172 if (child
->type
== XML_ELEMENT_NODE
) {
173 const char *name
= (const char *) child
->name
;
174 if (strcmp(name
, "svg") == 0)
175 code
= svgtiny_parse_svg(child
, state
);
176 else if (strcmp(name
, "g") == 0)
177 code
= svgtiny_parse_svg(child
, state
);
178 else if (strcmp(name
, "a") == 0)
179 code
= svgtiny_parse_svg(child
, state
);
180 else if (strcmp(name
, "path") == 0)
181 code
= svgtiny_parse_path(child
, state
);
182 else if (strcmp(name
, "rect") == 0)
183 code
= svgtiny_parse_rect(child
, state
);
184 else if (strcmp(name
, "circle") == 0)
185 code
= svgtiny_parse_circle(child
, state
);
186 else if (strcmp(name
, "line") == 0)
187 code
= svgtiny_parse_line(child
, state
);
188 else if (strcmp(name
, "polyline") == 0)
189 code
= svgtiny_parse_poly(child
, state
, false);
190 else if (strcmp(name
, "polygon") == 0)
191 code
= svgtiny_parse_poly(child
, state
, true);
192 else if (strcmp(name
, "text") == 0)
193 code
= svgtiny_parse_text(child
, state
);
196 if (code
!= svgtiny_OK
)
206 * Parse a <path> element node.
208 * http://www.w3.org/TR/SVG11/paths#PathElement
211 svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
212 struct svgtiny_parse_state state
)
216 svgtiny_parse_paint_attributes(path
, &state
);
217 svgtiny_parse_transform_attributes(path
, &state
);
219 /* read d attribute */
220 s
= path_d
= (char *) xmlGetProp(path
, (const xmlChar
*) "d");
222 state
.diagram
->error_line
= path
->line
;
223 state
.diagram
->error_message
= "path: missing d attribute";
224 return svgtiny_SVG_ERROR
;
227 /* allocate space for path: it will never have more elements than d */
228 float *p
= malloc(sizeof p
[0] * strlen(s
));
230 return svgtiny_OUT_OF_MEMORY
;
232 /* parse d and build path */
233 for (unsigned int i
= 0; s
[i
]; i
++)
237 float last_x
= 0, last_y
= 0;
238 float last_cubic_x
= 0, last_cubic_y
= 0;
239 float last_quad_x
= 0, last_quad_y
= 0;
243 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
246 /* moveto (M, m), lineto (L, l) (2 arguments) */
247 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
248 /*LOG(("moveto or lineto"));*/
249 if (*command
== 'M' || *command
== 'm')
250 plot_command
= svgtiny_PATH_MOVE
;
252 plot_command
= svgtiny_PATH_LINE
;
254 p
[i
++] = plot_command
;
255 if ('a' <= *command
) {
259 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
261 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
264 plot_command
= svgtiny_PATH_LINE
;
265 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
267 /* closepath (Z, z) (no arguments) */
268 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
269 /*LOG(("closepath"));*/
270 p
[i
++] = svgtiny_PATH_CLOSE
;
273 /* horizontal lineto (H, h) (1 argument) */
274 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
275 /*LOG(("horizontal lineto"));*/
277 p
[i
++] = svgtiny_PATH_LINE
;
280 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
282 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
284 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
286 /* vertical lineto (V, v) (1 argument) */
287 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
288 /*LOG(("vertical lineto"));*/
290 p
[i
++] = svgtiny_PATH_LINE
;
293 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
294 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
297 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
299 /* curveto (C, c) (6 arguments) */
300 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
301 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
302 /*LOG(("curveto"));*/
304 p
[i
++] = svgtiny_PATH_BEZIER
;
305 if (*command
== 'c') {
315 p
[i
++] = last_cubic_x
= x2
;
316 p
[i
++] = last_cubic_y
= y2
;
317 p
[i
++] = last_quad_x
= last_x
= x
;
318 p
[i
++] = last_quad_y
= last_y
= y
;
320 } while (sscanf(s
, "%f %f %f %f %f %f %n",
321 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
323 /* shorthand/smooth curveto (S, s) (4 arguments) */
324 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
325 &x2
, &y2
, &x
, &y
, &n
) == 5) {
326 /*LOG(("shorthand/smooth curveto"));*/
328 p
[i
++] = svgtiny_PATH_BEZIER
;
329 x1
= last_x
+ (last_x
- last_cubic_x
);
330 y1
= last_y
+ (last_y
- last_cubic_y
);
331 if (*command
== 's') {
339 p
[i
++] = last_cubic_x
= x2
;
340 p
[i
++] = last_cubic_y
= y2
;
341 p
[i
++] = last_quad_x
= last_x
= x
;
342 p
[i
++] = last_quad_y
= last_y
= y
;
344 } while (sscanf(s
, "%f %f %f %f %n",
345 &x2
, &y2
, &x
, &y
, &n
) == 4);
347 /* quadratic Bezier curveto (Q, q) (4 arguments) */
348 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
349 &x1
, &y1
, &x
, &y
, &n
) == 5) {
350 /*LOG(("quadratic Bezier curveto"));*/
352 p
[i
++] = svgtiny_PATH_BEZIER
;
355 if (*command
== 'q') {
361 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
362 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
363 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
364 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
365 p
[i
++] = last_cubic_x
= last_x
= x
;
366 p
[i
++] = last_cubic_y
= last_y
= y
;
368 } while (sscanf(s
, "%f %f %f %f %n",
369 &x1
, &y1
, &x
, &y
, &n
) == 4);
371 /* shorthand/smooth quadratic Bezier curveto (T, t)
373 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
375 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
377 p
[i
++] = svgtiny_PATH_BEZIER
;
378 x1
= last_x
+ (last_x
- last_quad_x
);
379 y1
= last_y
+ (last_y
- last_quad_y
);
382 if (*command
== 't') {
388 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
389 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
390 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
391 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
392 p
[i
++] = last_cubic_x
= last_x
= x
;
393 p
[i
++] = last_cubic_y
= last_y
= y
;
395 } while (sscanf(s
, "%f %f %n",
398 /* elliptical arc (A, a) (7 arguments) */
399 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
400 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
403 p
[i
++] = svgtiny_PATH_LINE
;
404 if (*command
== 'a') {
408 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
410 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
413 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
414 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
418 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
426 /* no real segments in path */
431 return svgtiny_add_path(p
, i
, &state
);
436 * Parse a <rect> element node.
438 * http://www.w3.org/TR/SVG11/shapes#RectElement
441 svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
442 struct svgtiny_parse_state state
)
444 float x
, y
, width
, height
;
446 svgtiny_parse_position_attributes(rect
, state
,
447 &x
, &y
, &width
, &height
);
448 svgtiny_parse_paint_attributes(rect
, &state
);
449 svgtiny_parse_transform_attributes(rect
, &state
);
451 float *p
= malloc(13 * sizeof p
[0]);
453 return svgtiny_OUT_OF_MEMORY
;
455 p
[0] = svgtiny_PATH_MOVE
;
458 p
[3] = svgtiny_PATH_LINE
;
461 p
[6] = svgtiny_PATH_LINE
;
464 p
[9] = svgtiny_PATH_LINE
;
467 p
[12] = svgtiny_PATH_CLOSE
;
469 return svgtiny_add_path(p
, 13, &state
);
474 * Parse a <circle> element node.
477 svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
478 struct svgtiny_parse_state state
)
480 float x
= 0, y
= 0, r
= 0;
481 const float kappa
= 0.5522847498;
483 for (xmlAttr
*attr
= circle
->properties
; attr
; attr
= attr
->next
) {
484 const char *name
= (const char *) attr
->name
;
485 const char *content
= (const char *) attr
->children
->content
;
486 if (strcmp(name
, "cx") == 0)
487 x
= svgtiny_parse_length(content
,
488 state
.viewport_width
, state
);
489 else if (strcmp(name
, "cy") == 0)
490 y
= svgtiny_parse_length(content
,
491 state
.viewport_height
, state
);
492 else if (strcmp(name
, "r") == 0)
493 r
= svgtiny_parse_length(content
,
494 state
.viewport_width
, state
);
496 svgtiny_parse_paint_attributes(circle
, &state
);
497 svgtiny_parse_transform_attributes(circle
, &state
);
499 float *p
= malloc(32 * sizeof p
[0]);
501 return svgtiny_OUT_OF_MEMORY
;
503 p
[0] = svgtiny_PATH_MOVE
;
506 p
[3] = svgtiny_PATH_BEZIER
;
508 p
[5] = y
+ r
* kappa
;
509 p
[6] = x
- r
* kappa
;
513 p
[10] = svgtiny_PATH_BEZIER
;
514 p
[11] = x
+ r
* kappa
;
517 p
[14] = y
+ r
* kappa
;
520 p
[17] = svgtiny_PATH_BEZIER
;
522 p
[19] = y
- r
* kappa
;
523 p
[20] = x
+ r
* kappa
;
527 p
[24] = svgtiny_PATH_BEZIER
;
528 p
[25] = x
- r
* kappa
;
531 p
[28] = y
- r
* kappa
;
534 p
[31] = svgtiny_PATH_CLOSE
;
536 return svgtiny_add_path(p
, 32, &state
);
541 * Parse a <line> element node.
544 svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
545 struct svgtiny_parse_state state
)
547 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
549 for (xmlAttr
*attr
= line
->properties
; attr
; attr
= attr
->next
) {
550 const char *name
= (const char *) attr
->name
;
551 const char *content
= (const char *) attr
->children
->content
;
552 if (strcmp(name
, "x1") == 0)
553 x1
= svgtiny_parse_length(content
,
554 state
.viewport_width
, state
);
555 else if (strcmp(name
, "y1") == 0)
556 y1
= svgtiny_parse_length(content
,
557 state
.viewport_height
, state
);
558 else if (strcmp(name
, "x2") == 0)
559 x2
= svgtiny_parse_length(content
,
560 state
.viewport_width
, state
);
561 else if (strcmp(name
, "y2") == 0)
562 y2
= svgtiny_parse_length(content
,
563 state
.viewport_height
, state
);
565 svgtiny_parse_paint_attributes(line
, &state
);
566 svgtiny_parse_transform_attributes(line
, &state
);
568 float *p
= malloc(7 * sizeof p
[0]);
570 return svgtiny_OUT_OF_MEMORY
;
572 p
[0] = svgtiny_PATH_MOVE
;
575 p
[3] = svgtiny_PATH_LINE
;
578 p
[6] = svgtiny_PATH_CLOSE
;
580 return svgtiny_add_path(p
, 7, &state
);
585 * Parse a <polyline> or <polygon> element node.
587 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
588 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
591 svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
592 struct svgtiny_parse_state state
, bool polygon
)
596 svgtiny_parse_paint_attributes(poly
, &state
);
597 svgtiny_parse_transform_attributes(poly
, &state
);
599 /* read points attribute */
600 s
= points
= (char *) xmlGetProp(poly
, (const xmlChar
*) "points");
602 state
.diagram
->error_line
= poly
->line
;
603 state
.diagram
->error_message
=
604 "polyline/polygon: missing points attribute";
605 return svgtiny_SVG_ERROR
;
608 /* allocate space for path: it will never have more elements than s */
609 float *p
= malloc(sizeof p
[0] * strlen(s
));
612 return svgtiny_OUT_OF_MEMORY
;
615 /* parse s and build path */
616 for (unsigned int i
= 0; s
[i
]; i
++)
624 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
626 p
[i
++] = svgtiny_PATH_MOVE
;
628 p
[i
++] = svgtiny_PATH_LINE
;
637 p
[i
++] = svgtiny_PATH_CLOSE
;
641 return svgtiny_add_path(p
, i
, &state
);
646 * Parse a <text> or <tspan> element node.
649 svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
650 struct svgtiny_parse_state state
)
652 float x
, y
, width
, height
;
654 svgtiny_parse_position_attributes(text
, state
,
655 &x
, &y
, &width
, &height
);
656 svgtiny_parse_font_attributes(text
, &state
);
657 svgtiny_parse_transform_attributes(text
, &state
);
659 float px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
660 float py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
661 /* state.ctm.e = px - state.origin_x; */
662 /* state.ctm.f = py - state.origin_y; */
664 /*struct css_style style = state.style;
665 style.font_size.value.length.value *= state.ctm.a;*/
667 for (xmlNode
*child
= text
->children
; child
; child
= child
->next
) {
668 svgtiny_code code
= svgtiny_OK
;
670 if (child
->type
== XML_TEXT_NODE
) {
671 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
673 return svgtiny_OUT_OF_MEMORY
;
674 shape
->text
= strdup((const char *) child
->content
);
677 state
.diagram
->shape_count
++;
679 } else if (child
->type
== XML_ELEMENT_NODE
&&
680 strcmp((const char *) child
->name
,
682 code
= svgtiny_parse_text(child
, state
);
685 if (!code
!= svgtiny_OK
)
694 * Parse x, y, width, and height attributes, if present.
697 void svgtiny_parse_position_attributes(const xmlNode
*node
,
698 const struct svgtiny_parse_state state
,
699 float *x
, float *y
, float *width
, float *height
)
703 *width
= state
.viewport_width
;
704 *height
= state
.viewport_height
;
706 for (xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
707 const char *name
= (const char *) attr
->name
;
708 const char *content
= (const char *) attr
->children
->content
;
709 if (strcmp(name
, "x") == 0)
710 *x
= svgtiny_parse_length(content
,
711 state
.viewport_width
, state
);
712 else if (strcmp(name
, "y") == 0)
713 *y
= svgtiny_parse_length(content
,
714 state
.viewport_height
, state
);
715 else if (strcmp(name
, "width") == 0)
716 *width
= svgtiny_parse_length(content
,
717 state
.viewport_width
, state
);
718 else if (strcmp(name
, "height") == 0)
719 *height
= svgtiny_parse_length(content
,
720 state
.viewport_height
, state
);
726 * Parse a length as a number of pixels.
729 float svgtiny_parse_length(const char *s
, int viewport_size
,
730 const struct svgtiny_parse_state state
)
732 int num_length
= strspn(s
, "0123456789+-.");
733 const char *unit
= s
+ num_length
;
734 float n
= atof((const char *) s
);
735 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
741 } else if (unit
[0] == '%') {
742 return n
/ 100.0 * viewport_size
;
743 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
744 return n
* font_size
;
745 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
746 return n
/ 2.0 * font_size
;
747 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
749 } else if (unit
[0] == 'p' && unit
[1] == 't') {
751 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
753 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
755 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
757 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
766 * Parse paint attributes, if present.
769 void svgtiny_parse_paint_attributes(const xmlNode
*node
,
770 struct svgtiny_parse_state
*state
)
772 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
773 const char *name
= (const char *) attr
->name
;
774 const char *content
= (const char *) attr
->children
->content
;
775 if (strcmp(name
, "fill") == 0)
776 svgtiny_parse_color(content
, &state
->fill
, state
);
777 else if (strcmp(name
, "stroke") == 0)
778 svgtiny_parse_color(content
, &state
->stroke
, state
);
779 else if (strcmp(name
, "stroke-width") == 0)
780 state
->stroke_width
= svgtiny_parse_length(content
,
781 state
->viewport_width
, *state
);
782 else if (strcmp(name
, "style") == 0) {
783 const char *style
= (const char *)
784 attr
->children
->content
;
787 if ((s
= strstr(style
, "fill:"))) {
791 value
= strndup(s
, strcspn(s
, "; "));
792 svgtiny_parse_color(value
, &state
->fill
, state
);
795 if ((s
= strstr(style
, "stroke:"))) {
799 value
= strndup(s
, strcspn(s
, "; "));
800 svgtiny_parse_color(value
, &state
->stroke
, state
);
803 if ((s
= strstr(style
, "stroke-width:"))) {
807 state
->stroke_width
= svgtiny_parse_length(s
,
808 state
->viewport_width
, *state
);
819 void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
820 struct svgtiny_parse_state
*state
)
822 unsigned int r
, g
, b
;
824 size_t len
= strlen(s
);
825 char *id
= 0, *rparen
;
827 if (len
== 4 && s
[0] == '#') {
828 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
829 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
831 } else if (len
== 7 && s
[0] == '#') {
832 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
833 *c
= svgtiny_RGB(r
, g
, b
);
835 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
836 s
[3] == '(' && s
[len
- 1] == ')') {
837 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
838 *c
= svgtiny_RGB(r
, g
, b
);
839 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
843 *c
= svgtiny_RGB(r
, g
, b
);
846 } else if (len
== 4 && strcmp(s
, "none") == 0) {
847 *c
= svgtiny_TRANSPARENT
;
849 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
855 rparen
= strchr(id
, ')');
858 svgtiny_find_gradient(id
, state
);
860 fprintf(stderr
, "linear_gradient_stop_count %i\n",
861 state
->linear_gradient_stop_count
);
862 if (state
->linear_gradient_stop_count
== 0)
863 *c
= svgtiny_TRANSPARENT
;
864 else if (state
->linear_gradient_stop_count
== 1)
865 *c
= state
->gradient_stop
[0].color
;
867 *c
= svgtiny_LINEAR_GRADIENT
;
871 const struct svgtiny_named_color
*named_color
;
872 named_color
= svgtiny_color_lookup(s
, strlen(s
));
874 *c
= named_color
->color
;
880 * Parse font attributes, if present.
883 void svgtiny_parse_font_attributes(const xmlNode
*node
,
884 struct svgtiny_parse_state
*state
)
888 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
889 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
890 /*if (css_parse_length(
891 (const char *) attr->children->content,
892 &state->style.font_size.value.length,
894 state->style.font_size.size =
895 CSS_FONT_SIZE_LENGTH;
903 * Parse transform attributes, if present.
905 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
908 void svgtiny_parse_transform_attributes(xmlNode
*node
,
909 struct svgtiny_parse_state
*state
)
913 /* parse transform */
914 transform
= (char *) xmlGetProp(node
, (const xmlChar
*) "transform");
916 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
917 &state
->ctm
.c
, &state
->ctm
.d
,
918 &state
->ctm
.e
, &state
->ctm
.f
);
925 * Parse a transform string.
928 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
929 float *mc
, float *md
, float *me
, float *mf
)
931 float a
, b
, c
, d
, e
, f
;
932 float za
, zb
, zc
, zd
, ze
, zf
;
936 for (unsigned int i
= 0; s
[i
]; i
++)
944 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
945 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
947 else if (sscanf(s
, "translate (%f %f) %n",
950 else if (sscanf(s
, "translate (%f) %n",
953 else if (sscanf(s
, "scale (%f %f) %n",
956 else if (sscanf(s
, "scale (%f) %n",
959 else if (sscanf(s
, "rotate (%f %f %f) %n",
960 &angle
, &x
, &y
, &n
) == 3) {
961 angle
= angle
/ 180 * M_PI
;
966 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
967 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
968 } else if (sscanf(s
, "rotate (%f) %n",
970 angle
= angle
/ 180 * M_PI
;
975 } else if (sscanf(s
, "skewX (%f) %n",
977 angle
= angle
/ 180 * M_PI
;
979 } else if (sscanf(s
, "skewY (%f) %n",
981 angle
= angle
/ 180 * M_PI
;
985 za
= *ma
* a
+ *mc
* b
;
986 zb
= *mb
* a
+ *md
* b
;
987 zc
= *ma
* c
+ *mc
* d
;
988 zd
= *mb
* c
+ *md
* d
;
989 ze
= *ma
* e
+ *mc
* f
+ *me
;
990 zf
= *mb
* e
+ *md
* f
+ *mf
;
1003 * Add a path to the svgtiny_diagram.
1006 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1007 struct svgtiny_parse_state
*state
)
1009 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1010 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1012 svgtiny_transform_path(p
, n
, state
);
1014 struct svgtiny_shape
*shape
= svgtiny_add_shape(state
);
1017 return svgtiny_OUT_OF_MEMORY
;
1020 shape
->path_length
= n
;
1021 state
->diagram
->shape_count
++;
1028 * Add a svgtiny_shape to the svgtiny_diagram.
1031 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1033 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1034 (state
->diagram
->shape_count
+ 1) *
1035 sizeof (state
->diagram
->shape
[0]));
1038 state
->diagram
->shape
= shape
;
1040 shape
+= state
->diagram
->shape_count
;
1042 shape
->path_length
= 0;
1044 shape
->fill
= state
->fill
;
1045 shape
->stroke
= state
->stroke
;
1046 shape
->stroke_width
= state
->stroke_width
*
1047 (state
->ctm
.a
+ state
->ctm
.d
) / 2;
1054 * Apply the current transformation matrix to a path.
1057 void svgtiny_transform_path(float *p
, unsigned int n
,
1058 struct svgtiny_parse_state
*state
)
1060 for (unsigned int j
= 0; j
!= n
; ) {
1061 unsigned int points
= 0;
1062 switch ((int) p
[j
]) {
1063 case svgtiny_PATH_MOVE
:
1064 case svgtiny_PATH_LINE
:
1067 case svgtiny_PATH_CLOSE
:
1070 case svgtiny_PATH_BEZIER
:
1077 for (unsigned int k
= 0; k
!= points
; k
++) {
1078 float x0
= p
[j
], y0
= p
[j
+ 1];
1079 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1081 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1092 * Free all memory used by a diagram.
1095 void svgtiny_free(struct svgtiny_diagram
*svg
)
1099 for (unsigned int i
= 0; i
!= svg
->shape_count
; i
++) {
1100 free(svg
->shape
[i
].path
);
1101 free(svg
->shape
[i
].text
);