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>
21 struct svgtiny_parse_state
{
22 struct svgtiny_diagram
*diagram
;
26 float viewport_height
;
28 /* current transformation matrix */
30 float a
, b
, c
, d
, e
, f
;
33 /*struct css_style style;*/
35 /* paint attributes */
37 svgtiny_colour stroke
;
42 static bool svgtiny_parse_svg(xmlNode
*svg
, struct svgtiny_parse_state state
);
43 static bool svgtiny_parse_path(xmlNode
*path
, struct svgtiny_parse_state state
);
44 static bool svgtiny_parse_rect(xmlNode
*rect
, struct svgtiny_parse_state state
);
45 static bool svgtiny_parse_circle(xmlNode
*circle
,
46 struct svgtiny_parse_state state
);
47 static bool svgtiny_parse_line(xmlNode
*line
, struct svgtiny_parse_state state
);
48 static bool svgtiny_parse_poly(xmlNode
*poly
, struct svgtiny_parse_state state
,
50 static bool svgtiny_parse_text(xmlNode
*text
, struct svgtiny_parse_state state
);
51 static void svgtiny_parse_position_attributes(const xmlNode
*node
,
52 const struct svgtiny_parse_state state
,
53 float *x
, float *y
, float *width
, float *height
);
54 static float svgtiny_parse_length(const char *s
, int viewport_size
,
55 const struct svgtiny_parse_state state
);
56 static void svgtiny_parse_paint_attributes(const xmlNode
*node
,
57 struct svgtiny_parse_state
*state
);
58 static void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
59 struct svgtiny_parse_state
*state
);
60 static void svgtiny_parse_font_attributes(const xmlNode
*node
,
61 struct svgtiny_parse_state
*state
);
62 static void svgtiny_parse_transform_attributes(xmlNode
*node
,
63 struct svgtiny_parse_state
*state
);
64 static struct svgtiny_shape
*svgtiny_add_shape(
65 struct svgtiny_parse_state
*state
);
66 static void svgtiny_transform_path(float *p
, unsigned int n
,
67 struct svgtiny_parse_state
*state
);
70 struct svgtiny_diagram
*svgtiny_create(void)
72 struct svgtiny_diagram
*diagram
;
74 diagram
= malloc(sizeof *diagram
);
81 diagram
->shape_count
= 0;
87 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
88 const char *buffer
, size_t size
, const char *url
,
89 int viewport_width
, int viewport_height
)
93 struct svgtiny_parse_state state
;
99 /* parse XML to tree */
100 document
= xmlReadMemory(buffer
, size
, url
, 0,
101 XML_PARSE_NONET
| XML_PARSE_COMPACT
|
102 XML_PARSE_DTDVALID
/* needed for xmlGetID to work */);
104 return svgtiny_LIBXML_ERROR
;
105 diagram
->doc
= document
;
107 /*xmlDebugDumpDocument(stderr, document);*/
109 /* find root <svg> element */
110 svg
= xmlDocGetRootElement(document
);
112 return svgtiny_NOT_SVG
;
113 if (strcmp((const char *) svg
->name
, "svg") != 0)
114 return svgtiny_NOT_SVG
;
117 /* get graphic dimensions */
118 float x
, y
, width
, height
;
119 state
.diagram
= diagram
;
120 state
.document
= diagram
->doc
;
121 state
.viewport_width
= viewport_width
;
122 state
.viewport_height
= viewport_height
;
123 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
124 diagram
->width
= width
;
125 diagram
->height
= height
;
127 state
.viewport_width
= width
;
128 state
.viewport_height
= height
;
129 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
132 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
133 state
.ctm
.e
= 0; /*x;*/
134 state
.ctm
.f
= 0; /*y;*/
135 /*state.style = css_base_style;
136 state.style.font_size.value.length.value = option_font_size * 0.1;*/
137 state
.fill
= 0x000000;
138 state
.stroke
= svgtiny_TRANSPARENT
;
139 state
.stroke_width
= 1;
141 return svgtiny_parse_svg(svg
, state
);
146 * Parse a <svg> or <g> element node.
149 bool svgtiny_parse_svg(xmlNode
*svg
, struct svgtiny_parse_state state
)
151 float x
, y
, width
, height
;
153 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
154 svgtiny_parse_paint_attributes(svg
, &state
);
155 svgtiny_parse_font_attributes(svg
, &state
);
158 xmlAttr
*view_box
= xmlHasProp(svg
, (const xmlChar
*) "viewBox");
160 const char *s
= (const char *) view_box
->children
->content
;
161 float min_x
, min_y
, vwidth
, vheight
;
162 if (sscanf(s
, "%f,%f,%f,%f",
163 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
164 sscanf(s
, "%f %f %f %f",
165 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
166 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
167 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
168 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
169 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
173 svgtiny_parse_transform_attributes(svg
, &state
);
175 for (xmlNode
*child
= svg
->children
; child
; child
= child
->next
) {
178 if (child
->type
== XML_ELEMENT_NODE
) {
179 const char *name
= (const char *) child
->name
;
180 if (strcmp(name
, "svg") == 0)
181 ok
= svgtiny_parse_svg(child
, state
);
182 else if (strcmp(name
, "g") == 0)
183 ok
= svgtiny_parse_svg(child
, state
);
184 else if (strcmp(name
, "a") == 0)
185 ok
= svgtiny_parse_svg(child
, state
);
186 else if (strcmp(name
, "path") == 0)
187 ok
= svgtiny_parse_path(child
, state
);
188 else if (strcmp(name
, "rect") == 0)
189 ok
= svgtiny_parse_rect(child
, state
);
190 else if (strcmp(name
, "circle") == 0)
191 ok
= svgtiny_parse_circle(child
, state
);
192 else if (strcmp(name
, "line") == 0)
193 ok
= svgtiny_parse_line(child
, state
);
194 else if (strcmp(name
, "polyline") == 0)
195 ok
= svgtiny_parse_poly(child
, state
, false);
196 else if (strcmp(name
, "polygon") == 0)
197 ok
= svgtiny_parse_poly(child
, state
, true);
198 else if (strcmp(name
, "text") == 0)
199 ok
= svgtiny_parse_text(child
, state
);
211 * Parse a <path> element node.
213 * http://www.w3.org/TR/SVG11/paths#PathElement
216 bool svgtiny_parse_path(xmlNode
*path
, struct svgtiny_parse_state state
)
220 svgtiny_parse_paint_attributes(path
, &state
);
221 svgtiny_parse_transform_attributes(path
, &state
);
223 /* read d attribute */
224 s
= path_d
= (char *) xmlGetProp(path
, (const xmlChar
*) "d");
226 /*LOG(("path missing d attribute"));*/
230 /* allocate space for path: it will never have more elements than d */
231 float *p
= malloc(sizeof p
[0] * strlen(s
));
233 /*LOG(("out of memory"));*/
237 /* parse d and build path */
238 for (unsigned int i
= 0; s
[i
]; i
++)
242 float last_x
= 0, last_y
= 0;
243 float last_cubic_x
= 0, last_cubic_y
= 0;
244 float last_quad_x
= 0, last_quad_y
= 0;
248 float x
, y
, x1
, y1
, x2
, y2
;
251 /* moveto (M, m), lineto (L, l) (2 arguments) */
252 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
253 /*LOG(("moveto or lineto"));*/
254 if (*command
== 'M' || *command
== 'm')
255 plot_command
= svgtiny_PATH_MOVE
;
257 plot_command
= svgtiny_PATH_LINE
;
259 p
[i
++] = plot_command
;
260 if ('a' <= *command
) {
264 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
266 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
269 plot_command
= svgtiny_PATH_LINE
;
270 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
272 /* closepath (Z, z) (no arguments) */
273 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
274 /*LOG(("closepath"));*/
275 p
[i
++] = svgtiny_PATH_CLOSE
;
278 /* horizontal lineto (H, h) (1 argument) */
279 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
280 /*LOG(("horizontal lineto"));*/
282 p
[i
++] = svgtiny_PATH_LINE
;
285 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
287 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
289 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
291 /* vertical lineto (V, v) (1 argument) */
292 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
293 /*LOG(("vertical lineto"));*/
295 p
[i
++] = svgtiny_PATH_LINE
;
298 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
299 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
302 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
304 /* curveto (C, c) (6 arguments) */
305 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
306 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
307 /*LOG(("curveto"));*/
309 p
[i
++] = svgtiny_PATH_BEZIER
;
310 if (*command
== 'c') {
320 p
[i
++] = last_cubic_x
= x2
;
321 p
[i
++] = last_cubic_y
= y2
;
322 p
[i
++] = last_quad_x
= last_x
= x
;
323 p
[i
++] = last_quad_y
= last_y
= y
;
325 } while (sscanf(s
, "%f %f %f %f %f %f %n",
326 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
328 /* shorthand/smooth curveto (S, s) (4 arguments) */
329 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
330 &x2
, &y2
, &x
, &y
, &n
) == 5) {
331 /*LOG(("shorthand/smooth curveto"));*/
333 p
[i
++] = svgtiny_PATH_BEZIER
;
334 x1
= last_x
+ (last_x
- last_cubic_x
);
335 y1
= last_y
+ (last_y
- last_cubic_y
);
336 if (*command
== 's') {
344 p
[i
++] = last_cubic_x
= x2
;
345 p
[i
++] = last_cubic_y
= y2
;
346 p
[i
++] = last_quad_x
= last_x
= x
;
347 p
[i
++] = last_quad_y
= last_y
= y
;
349 } while (sscanf(s
, "%f %f %f %f %n",
350 &x2
, &y2
, &x
, &y
, &n
) == 4);
352 /* quadratic Bezier curveto (Q, q) (4 arguments) */
353 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
354 &x1
, &y1
, &x
, &y
, &n
) == 5) {
355 /*LOG(("quadratic Bezier curveto"));*/
357 p
[i
++] = svgtiny_PATH_BEZIER
;
360 if (*command
== 'q') {
366 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
367 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
368 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
369 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
370 p
[i
++] = last_cubic_x
= last_x
= x
;
371 p
[i
++] = last_cubic_y
= last_y
= y
;
373 } while (sscanf(s
, "%f %f %f %f %n",
374 &x1
, &y1
, &x
, &y
, &n
) == 4);
376 /* shorthand/smooth quadratic Bezier curveto (T, t)
378 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
380 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
382 p
[i
++] = svgtiny_PATH_BEZIER
;
383 x1
= last_x
+ (last_x
- last_quad_x
);
384 y1
= last_y
+ (last_y
- last_quad_y
);
387 if (*command
== 't') {
393 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
394 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
395 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
396 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
397 p
[i
++] = last_cubic_x
= last_x
= x
;
398 p
[i
++] = last_cubic_y
= last_y
= y
;
400 } while (sscanf(s
, "%f %f %n",
404 /*LOG(("parse failed at \"%s\"", s));*/
411 svgtiny_transform_path(p
, i
, &state
);
413 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
419 shape
->path_length
= i
;
420 state
.diagram
->shape_count
++;
427 * Parse a <rect> element node.
429 * http://www.w3.org/TR/SVG11/shapes#RectElement
432 bool svgtiny_parse_rect(xmlNode
*rect
, struct svgtiny_parse_state state
)
434 float x
, y
, width
, height
;
436 svgtiny_parse_position_attributes(rect
, state
,
437 &x
, &y
, &width
, &height
);
438 svgtiny_parse_paint_attributes(rect
, &state
);
439 svgtiny_parse_transform_attributes(rect
, &state
);
441 float *p
= malloc(13 * sizeof p
[0]);
445 p
[0] = svgtiny_PATH_MOVE
;
448 p
[3] = svgtiny_PATH_LINE
;
451 p
[6] = svgtiny_PATH_LINE
;
454 p
[9] = svgtiny_PATH_LINE
;
457 p
[12] = svgtiny_PATH_CLOSE
;
459 svgtiny_transform_path(p
, 13, &state
);
461 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
467 shape
->path_length
= 13;
468 state
.diagram
->shape_count
++;
475 * Parse a <circle> element node.
478 bool svgtiny_parse_circle(xmlNode
*circle
, 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]);
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 svgtiny_transform_path(p
, 32, &state
);
538 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
544 shape
->path_length
= 32;
545 state
.diagram
->shape_count
++;
552 * Parse a <line> element node.
555 bool svgtiny_parse_line(xmlNode
*line
, struct svgtiny_parse_state state
)
557 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
559 for (xmlAttr
*attr
= line
->properties
; attr
; attr
= attr
->next
) {
560 const char *name
= (const char *) attr
->name
;
561 const char *content
= (const char *) attr
->children
->content
;
562 if (strcmp(name
, "x1") == 0)
563 x1
= svgtiny_parse_length(content
,
564 state
.viewport_width
, state
);
565 else if (strcmp(name
, "y1") == 0)
566 y1
= svgtiny_parse_length(content
,
567 state
.viewport_height
, state
);
568 else if (strcmp(name
, "x2") == 0)
569 x2
= svgtiny_parse_length(content
,
570 state
.viewport_width
, state
);
571 else if (strcmp(name
, "y2") == 0)
572 y2
= svgtiny_parse_length(content
,
573 state
.viewport_height
, state
);
575 svgtiny_parse_paint_attributes(line
, &state
);
576 svgtiny_parse_transform_attributes(line
, &state
);
578 float *p
= malloc(7 * sizeof p
[0]);
582 p
[0] = svgtiny_PATH_MOVE
;
585 p
[3] = svgtiny_PATH_LINE
;
588 p
[6] = svgtiny_PATH_CLOSE
;
590 svgtiny_transform_path(p
, 7, &state
);
592 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
598 shape
->path_length
= 7;
599 state
.diagram
->shape_count
++;
606 * Parse a <polyline> or <polygon> element node.
608 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
609 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
612 bool svgtiny_parse_poly(xmlNode
*poly
, struct svgtiny_parse_state state
,
617 svgtiny_parse_paint_attributes(poly
, &state
);
618 svgtiny_parse_transform_attributes(poly
, &state
);
620 /* read d attribute */
621 s
= points
= (char *) xmlGetProp(poly
, (const xmlChar
*) "points");
623 /*LOG(("poly missing d attribute"));*/
627 /* allocate space for path: it will never have more elements than s */
628 float *p
= malloc(sizeof p
[0] * strlen(s
));
630 /*LOG(("out of memory"));*/
634 /* parse s and build path */
635 for (unsigned int i
= 0; s
[i
]; i
++)
643 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
645 p
[i
++] = svgtiny_PATH_MOVE
;
647 p
[i
++] = svgtiny_PATH_LINE
;
656 p
[i
++] = svgtiny_PATH_CLOSE
;
660 svgtiny_transform_path(p
, i
, &state
);
662 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
668 shape
->path_length
= i
;
669 state
.diagram
->shape_count
++;
676 * Parse a <text> or <tspan> element node.
679 bool svgtiny_parse_text(xmlNode
*text
, struct svgtiny_parse_state state
)
681 float x
, y
, width
, height
;
683 svgtiny_parse_position_attributes(text
, state
,
684 &x
, &y
, &width
, &height
);
685 svgtiny_parse_font_attributes(text
, &state
);
686 svgtiny_parse_transform_attributes(text
, &state
);
688 float px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
689 float py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
690 /* state.ctm.e = px - state.origin_x; */
691 /* state.ctm.f = py - state.origin_y; */
693 /*struct css_style style = state.style;
694 style.font_size.value.length.value *= state.ctm.a;*/
696 for (xmlNode
*child
= text
->children
; child
; child
= child
->next
) {
699 if (child
->type
== XML_TEXT_NODE
) {
700 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
703 shape
->text
= strdup((const char *) child
->content
);
706 state
.diagram
->shape_count
++;
708 } else if (child
->type
== XML_ELEMENT_NODE
&&
709 strcmp((const char *) child
->name
,
711 ok
= svgtiny_parse_text(child
, state
);
723 * Parse x, y, width, and height attributes, if present.
726 void svgtiny_parse_position_attributes(const xmlNode
*node
,
727 const struct svgtiny_parse_state state
,
728 float *x
, float *y
, float *width
, float *height
)
732 *width
= state
.viewport_width
;
733 *height
= state
.viewport_height
;
735 for (xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
736 const char *name
= (const char *) attr
->name
;
737 const char *content
= (const char *) attr
->children
->content
;
738 if (strcmp(name
, "x") == 0)
739 *x
= svgtiny_parse_length(content
,
740 state
.viewport_width
, state
);
741 else if (strcmp(name
, "y") == 0)
742 *y
= svgtiny_parse_length(content
,
743 state
.viewport_height
, state
);
744 else if (strcmp(name
, "width") == 0)
745 *width
= svgtiny_parse_length(content
,
746 state
.viewport_width
, state
);
747 else if (strcmp(name
, "height") == 0)
748 *height
= svgtiny_parse_length(content
,
749 state
.viewport_height
, state
);
755 * Parse a length as a number of pixels.
758 float svgtiny_parse_length(const char *s
, int viewport_size
,
759 const struct svgtiny_parse_state state
)
761 int num_length
= strspn(s
, "0123456789+-.");
762 const char *unit
= s
+ num_length
;
763 float n
= atof((const char *) s
);
764 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
768 } else if (unit
[0] == '%') {
769 return n
/ 100.0 * viewport_size
;
770 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
771 return n
* font_size
;
772 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
773 return n
/ 2.0 * font_size
;
774 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
776 } else if (unit
[0] == 'p' && unit
[1] == 't') {
778 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
780 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
782 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
784 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
793 * Parse paint attributes, if present.
796 void svgtiny_parse_paint_attributes(const xmlNode
*node
,
797 struct svgtiny_parse_state
*state
)
799 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
800 const char *name
= (const char *) attr
->name
;
801 const char *content
= (const char *) attr
->children
->content
;
802 if (strcmp(name
, "fill") == 0)
803 svgtiny_parse_color(content
, &state
->fill
, state
);
804 else if (strcmp(name
, "stroke") == 0)
805 svgtiny_parse_color(content
, &state
->stroke
, state
);
806 else if (strcmp(name
, "stroke-width") == 0)
807 state
->stroke_width
= svgtiny_parse_length(content
,
808 state
->viewport_width
, *state
);
809 else if (strcmp(name
, "style") == 0) {
810 const char *style
= (const char *)
811 attr
->children
->content
;
814 if ((s
= strstr(style
, "fill:"))) {
818 value
= strndup(s
, strcspn(s
, "; "));
819 svgtiny_parse_color(value
, &state
->fill
, state
);
822 if ((s
= strstr(style
, "stroke:"))) {
826 value
= strndup(s
, strcspn(s
, "; "));
827 svgtiny_parse_color(value
, &state
->stroke
, state
);
830 if ((s
= strstr(style
, "stroke-width:"))) {
834 state
->stroke_width
= svgtiny_parse_length(s
,
835 state
->viewport_width
, *state
);
846 void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
847 struct svgtiny_parse_state
*state
)
849 unsigned int r
, g
, b
;
851 size_t len
= strlen(s
);
852 char *id
= 0, *rparen
;
855 if (len
== 4 && s
[0] == '#') {
856 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
857 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
859 } else if (len
== 7 && s
[0] == '#') {
860 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
861 *c
= svgtiny_RGB(r
, g
, b
);
863 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
864 s
[3] == '(' && s
[len
- 1] == ')') {
865 if (sscanf(s
+ 4, "%i,%i,%i", &r
, &g
, &b
) == 3)
866 *c
= svgtiny_RGB(r
, g
, b
);
867 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
871 *c
= svgtiny_RGB(r
, g
, b
);
874 } else if (len
== 4 && strcmp(s
, "none") == 0) {
875 *c
= svgtiny_TRANSPARENT
;
877 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
883 rparen
= strchr(id
, ')');
886 id_attr
= xmlGetID(state
->document
,
887 (const xmlChar
*) id
);
889 fprintf(stderr
, "id \"%s\" not found\n", id
);
893 fprintf(stderr
, "id \"%s\" at %p\n", id
, id_attr
);
898 const struct svgtiny_named_color
*named_color
;
899 named_color
= svgtiny_color_lookup(s
, strlen(s
));
901 *c
= named_color
->color
;
907 * Parse font attributes, if present.
910 void svgtiny_parse_font_attributes(const xmlNode
*node
,
911 struct svgtiny_parse_state
*state
)
913 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
914 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
915 /*if (css_parse_length(
916 (const char *) attr->children->content,
917 &state->style.font_size.value.length,
919 state->style.font_size.size =
920 CSS_FONT_SIZE_LENGTH;
928 * Parse transform attributes, if present.
930 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
933 void svgtiny_parse_transform_attributes(xmlNode
*node
,
934 struct svgtiny_parse_state
*state
)
937 float a
, b
, c
, d
, e
, f
;
938 float ctm_a
, ctm_b
, ctm_c
, ctm_d
, ctm_e
, ctm_f
;
942 /* parse transform */
943 s
= transform
= (char *) xmlGetProp(node
,
944 (const xmlChar
*) "transform");
946 for (unsigned int i
= 0; transform
[i
]; i
++)
947 if (transform
[i
] == ',')
954 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
955 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
957 else if (sscanf(s
, "translate (%f %f) %n",
960 else if (sscanf(s
, "translate (%f) %n",
963 else if (sscanf(s
, "scale (%f %f) %n",
966 else if (sscanf(s
, "scale (%f) %n",
969 else if (sscanf(s
, "rotate (%f %f %f) %n",
970 &angle
, &x
, &y
, &n
) == 3) {
971 angle
= angle
/ 180 * M_PI
;
976 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
977 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
978 } else if (sscanf(s
, "rotate (%f) %n",
980 angle
= angle
/ 180 * M_PI
;
985 } else if (sscanf(s
, "skewX (%f) %n",
987 angle
= angle
/ 180 * M_PI
;
989 } else if (sscanf(s
, "skewY (%f) %n",
991 angle
= angle
/ 180 * M_PI
;
995 ctm_a
= state
->ctm
.a
* a
+ state
->ctm
.c
* b
;
996 ctm_b
= state
->ctm
.b
* a
+ state
->ctm
.d
* b
;
997 ctm_c
= state
->ctm
.a
* c
+ state
->ctm
.c
* d
;
998 ctm_d
= state
->ctm
.b
* c
+ state
->ctm
.d
* d
;
999 ctm_e
= state
->ctm
.a
* e
+ state
->ctm
.c
* f
+
1001 ctm_f
= state
->ctm
.b
* e
+ state
->ctm
.d
* f
+
1003 state
->ctm
.a
= ctm_a
;
1004 state
->ctm
.b
= ctm_b
;
1005 state
->ctm
.c
= ctm_c
;
1006 state
->ctm
.d
= ctm_d
;
1007 state
->ctm
.e
= ctm_e
;
1008 state
->ctm
.f
= ctm_f
;
1018 * Add a svgtiny_shape to the svgtiny_diagram.
1021 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1023 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1024 (state
->diagram
->shape_count
+ 1) *
1025 sizeof (state
->diagram
->shape
[0]));
1028 state
->diagram
->shape
= shape
;
1030 shape
+= state
->diagram
->shape_count
;
1032 shape
->path_length
= 0;
1034 shape
->fill
= state
->fill
;
1035 shape
->stroke
= state
->stroke
;
1036 shape
->stroke_width
= state
->stroke_width
;
1042 void svgtiny_transform_path(float *p
, unsigned int n
,
1043 struct svgtiny_parse_state
*state
)
1045 for (unsigned int j
= 0; j
!= n
; ) {
1046 unsigned int points
= 0;
1047 switch ((int) p
[j
]) {
1048 case svgtiny_PATH_MOVE
:
1049 case svgtiny_PATH_LINE
:
1052 case svgtiny_PATH_CLOSE
:
1055 case svgtiny_PATH_BEZIER
:
1062 for (unsigned int k
= 0; k
!= points
; k
++) {
1063 float x0
= p
[j
], y0
= p
[j
+ 1];
1064 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1066 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+