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 #define M_PI 3.14159265358979323846
25 static svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
26 struct svgtiny_parse_state state
);
27 static svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
28 struct svgtiny_parse_state state
);
29 static svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
30 struct svgtiny_parse_state state
);
31 static svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
32 struct svgtiny_parse_state state
);
33 static svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
34 struct svgtiny_parse_state state
);
35 static svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
36 struct svgtiny_parse_state state
, bool polygon
);
37 static svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
38 struct svgtiny_parse_state state
);
39 static void svgtiny_parse_position_attributes(const xmlNode
*node
,
40 const struct svgtiny_parse_state state
,
41 float *x
, float *y
, float *width
, float *height
);
42 static void svgtiny_parse_paint_attributes(const xmlNode
*node
,
43 struct svgtiny_parse_state
*state
);
44 static void svgtiny_parse_font_attributes(const xmlNode
*node
,
45 struct svgtiny_parse_state
*state
);
46 static void svgtiny_parse_transform_attributes(xmlNode
*node
,
47 struct svgtiny_parse_state
*state
);
48 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
49 struct svgtiny_parse_state
*state
);
53 * Create a new svgtiny_diagram structure.
56 struct svgtiny_diagram
*svgtiny_create(void)
58 struct svgtiny_diagram
*diagram
;
60 diagram
= malloc(sizeof *diagram
);
65 diagram
->shape_count
= 0;
66 diagram
->error_line
= 0;
67 diagram
->error_message
= 0;
74 * Parse a block of memory into a svgtiny_diagram.
77 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
78 const char *buffer
, size_t size
, const char *url
,
79 int viewport_width
, int viewport_height
)
83 struct svgtiny_parse_state state
;
84 float x
, y
, width
, height
;
91 /* parse XML to tree */
92 document
= xmlReadMemory(buffer
, size
, url
, 0,
93 XML_PARSE_NONET
| XML_PARSE_COMPACT
);
95 return svgtiny_LIBXML_ERROR
;
97 /*xmlDebugDumpDocument(stderr, document);*/
99 /* find root <svg> element */
100 svg
= xmlDocGetRootElement(document
);
102 return svgtiny_NOT_SVG
;
103 if (strcmp((const char *) svg
->name
, "svg") != 0)
104 return svgtiny_NOT_SVG
;
106 /* get graphic dimensions */
107 state
.diagram
= diagram
;
108 state
.document
= document
;
109 state
.viewport_width
= viewport_width
;
110 state
.viewport_height
= viewport_height
;
111 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
112 diagram
->width
= width
;
113 diagram
->height
= height
;
115 /* set up parsing state */
116 state
.viewport_width
= width
;
117 state
.viewport_height
= height
;
118 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
121 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
122 state
.ctm
.e
= 0; /*x;*/
123 state
.ctm
.f
= 0; /*y;*/
124 /*state.style = css_base_style;
125 state.style.font_size.value.length.value = option_font_size * 0.1;*/
126 state
.fill
= 0x000000;
127 state
.stroke
= svgtiny_TRANSPARENT
;
128 state
.stroke_width
= 1;
129 state
.linear_gradient_stop_count
= 0;
132 code
= svgtiny_parse_svg(svg
, state
);
135 xmlFreeDoc(document
);
142 * Parse a <svg> or <g> element node.
145 svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
146 struct svgtiny_parse_state state
)
148 float x
, y
, width
, height
;
150 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
151 svgtiny_parse_paint_attributes(svg
, &state
);
152 svgtiny_parse_font_attributes(svg
, &state
);
155 xmlAttr
*view_box
= xmlHasProp(svg
, (const xmlChar
*) "viewBox");
157 const char *s
= (const char *) view_box
->children
->content
;
158 float min_x
, min_y
, vwidth
, vheight
;
159 if (sscanf(s
, "%f,%f,%f,%f",
160 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
161 sscanf(s
, "%f %f %f %f",
162 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
163 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
164 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
165 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
166 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
170 svgtiny_parse_transform_attributes(svg
, &state
);
172 for (xmlNode
*child
= svg
->children
; child
; child
= child
->next
) {
173 svgtiny_code code
= svgtiny_OK
;
175 if (child
->type
== XML_ELEMENT_NODE
) {
176 const char *name
= (const char *) child
->name
;
177 if (strcmp(name
, "svg") == 0)
178 code
= svgtiny_parse_svg(child
, state
);
179 else if (strcmp(name
, "g") == 0)
180 code
= svgtiny_parse_svg(child
, state
);
181 else if (strcmp(name
, "a") == 0)
182 code
= svgtiny_parse_svg(child
, state
);
183 else if (strcmp(name
, "path") == 0)
184 code
= svgtiny_parse_path(child
, state
);
185 else if (strcmp(name
, "rect") == 0)
186 code
= svgtiny_parse_rect(child
, state
);
187 else if (strcmp(name
, "circle") == 0)
188 code
= svgtiny_parse_circle(child
, state
);
189 else if (strcmp(name
, "line") == 0)
190 code
= svgtiny_parse_line(child
, state
);
191 else if (strcmp(name
, "polyline") == 0)
192 code
= svgtiny_parse_poly(child
, state
, false);
193 else if (strcmp(name
, "polygon") == 0)
194 code
= svgtiny_parse_poly(child
, state
, true);
195 else if (strcmp(name
, "text") == 0)
196 code
= svgtiny_parse_text(child
, state
);
199 if (code
!= svgtiny_OK
)
209 * Parse a <path> element node.
211 * http://www.w3.org/TR/SVG11/paths#PathElement
214 svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
215 struct svgtiny_parse_state state
)
219 svgtiny_parse_paint_attributes(path
, &state
);
220 svgtiny_parse_transform_attributes(path
, &state
);
222 /* read d attribute */
223 s
= path_d
= (char *) xmlGetProp(path
, (const xmlChar
*) "d");
225 state
.diagram
->error_line
= path
->line
;
226 state
.diagram
->error_message
= "path: missing d attribute";
227 return svgtiny_SVG_ERROR
;
230 /* allocate space for path: it will never have more elements than d */
231 float *p
= malloc(sizeof p
[0] * strlen(s
));
233 return svgtiny_OUT_OF_MEMORY
;
235 /* parse d and build path */
236 for (unsigned int i
= 0; s
[i
]; i
++)
240 float last_x
= 0, last_y
= 0;
241 float last_cubic_x
= 0, last_cubic_y
= 0;
242 float last_quad_x
= 0, last_quad_y
= 0;
246 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
249 /* moveto (M, m), lineto (L, l) (2 arguments) */
250 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
251 /*LOG(("moveto or lineto"));*/
252 if (*command
== 'M' || *command
== 'm')
253 plot_command
= svgtiny_PATH_MOVE
;
255 plot_command
= svgtiny_PATH_LINE
;
257 p
[i
++] = plot_command
;
258 if ('a' <= *command
) {
262 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
264 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
267 plot_command
= svgtiny_PATH_LINE
;
268 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
270 /* closepath (Z, z) (no arguments) */
271 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
272 /*LOG(("closepath"));*/
273 p
[i
++] = svgtiny_PATH_CLOSE
;
276 /* horizontal lineto (H, h) (1 argument) */
277 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
278 /*LOG(("horizontal lineto"));*/
280 p
[i
++] = svgtiny_PATH_LINE
;
283 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
285 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
287 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
289 /* vertical lineto (V, v) (1 argument) */
290 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
291 /*LOG(("vertical lineto"));*/
293 p
[i
++] = svgtiny_PATH_LINE
;
296 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
297 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
300 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
302 /* curveto (C, c) (6 arguments) */
303 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
304 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
305 /*LOG(("curveto"));*/
307 p
[i
++] = svgtiny_PATH_BEZIER
;
308 if (*command
== 'c') {
318 p
[i
++] = last_cubic_x
= x2
;
319 p
[i
++] = last_cubic_y
= y2
;
320 p
[i
++] = last_quad_x
= last_x
= x
;
321 p
[i
++] = last_quad_y
= last_y
= y
;
323 } while (sscanf(s
, "%f %f %f %f %f %f %n",
324 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
326 /* shorthand/smooth curveto (S, s) (4 arguments) */
327 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
328 &x2
, &y2
, &x
, &y
, &n
) == 5) {
329 /*LOG(("shorthand/smooth curveto"));*/
331 p
[i
++] = svgtiny_PATH_BEZIER
;
332 x1
= last_x
+ (last_x
- last_cubic_x
);
333 y1
= last_y
+ (last_y
- last_cubic_y
);
334 if (*command
== 's') {
342 p
[i
++] = last_cubic_x
= x2
;
343 p
[i
++] = last_cubic_y
= y2
;
344 p
[i
++] = last_quad_x
= last_x
= x
;
345 p
[i
++] = last_quad_y
= last_y
= y
;
347 } while (sscanf(s
, "%f %f %f %f %n",
348 &x2
, &y2
, &x
, &y
, &n
) == 4);
350 /* quadratic Bezier curveto (Q, q) (4 arguments) */
351 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
352 &x1
, &y1
, &x
, &y
, &n
) == 5) {
353 /*LOG(("quadratic Bezier curveto"));*/
355 p
[i
++] = svgtiny_PATH_BEZIER
;
358 if (*command
== 'q') {
364 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
365 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
366 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
367 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
368 p
[i
++] = last_cubic_x
= last_x
= x
;
369 p
[i
++] = last_cubic_y
= last_y
= y
;
371 } while (sscanf(s
, "%f %f %f %f %n",
372 &x1
, &y1
, &x
, &y
, &n
) == 4);
374 /* shorthand/smooth quadratic Bezier curveto (T, t)
376 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
378 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
380 p
[i
++] = svgtiny_PATH_BEZIER
;
381 x1
= last_x
+ (last_x
- last_quad_x
);
382 y1
= last_y
+ (last_y
- last_quad_y
);
385 if (*command
== 't') {
391 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
392 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
393 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
394 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
395 p
[i
++] = last_cubic_x
= last_x
= x
;
396 p
[i
++] = last_cubic_y
= last_y
= y
;
398 } while (sscanf(s
, "%f %f %n",
401 /* elliptical arc (A, a) (7 arguments) */
402 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
403 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
406 p
[i
++] = svgtiny_PATH_LINE
;
407 if (*command
== 'a') {
411 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
413 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
416 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
417 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
421 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
429 /* no real segments in path */
434 return svgtiny_add_path(p
, i
, &state
);
439 * Parse a <rect> element node.
441 * http://www.w3.org/TR/SVG11/shapes#RectElement
444 svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
445 struct svgtiny_parse_state state
)
447 float x
, y
, width
, height
;
449 svgtiny_parse_position_attributes(rect
, state
,
450 &x
, &y
, &width
, &height
);
451 svgtiny_parse_paint_attributes(rect
, &state
);
452 svgtiny_parse_transform_attributes(rect
, &state
);
454 float *p
= malloc(13 * sizeof p
[0]);
456 return svgtiny_OUT_OF_MEMORY
;
458 p
[0] = svgtiny_PATH_MOVE
;
461 p
[3] = svgtiny_PATH_LINE
;
464 p
[6] = svgtiny_PATH_LINE
;
467 p
[9] = svgtiny_PATH_LINE
;
470 p
[12] = svgtiny_PATH_CLOSE
;
472 return svgtiny_add_path(p
, 13, &state
);
477 * Parse a <circle> element node.
480 svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
481 struct svgtiny_parse_state state
)
483 float x
= 0, y
= 0, r
= 0;
484 const float kappa
= 0.5522847498;
486 for (xmlAttr
*attr
= circle
->properties
; attr
; attr
= attr
->next
) {
487 const char *name
= (const char *) attr
->name
;
488 const char *content
= (const char *) attr
->children
->content
;
489 if (strcmp(name
, "cx") == 0)
490 x
= svgtiny_parse_length(content
,
491 state
.viewport_width
, state
);
492 else if (strcmp(name
, "cy") == 0)
493 y
= svgtiny_parse_length(content
,
494 state
.viewport_height
, state
);
495 else if (strcmp(name
, "r") == 0)
496 r
= svgtiny_parse_length(content
,
497 state
.viewport_width
, state
);
499 svgtiny_parse_paint_attributes(circle
, &state
);
500 svgtiny_parse_transform_attributes(circle
, &state
);
502 float *p
= malloc(32 * sizeof p
[0]);
504 return svgtiny_OUT_OF_MEMORY
;
506 p
[0] = svgtiny_PATH_MOVE
;
509 p
[3] = svgtiny_PATH_BEZIER
;
511 p
[5] = y
+ r
* kappa
;
512 p
[6] = x
- r
* kappa
;
516 p
[10] = svgtiny_PATH_BEZIER
;
517 p
[11] = x
+ r
* kappa
;
520 p
[14] = y
+ r
* kappa
;
523 p
[17] = svgtiny_PATH_BEZIER
;
525 p
[19] = y
- r
* kappa
;
526 p
[20] = x
+ r
* kappa
;
530 p
[24] = svgtiny_PATH_BEZIER
;
531 p
[25] = x
- r
* kappa
;
534 p
[28] = y
- r
* kappa
;
537 p
[31] = svgtiny_PATH_CLOSE
;
539 return svgtiny_add_path(p
, 32, &state
);
544 * Parse a <line> element node.
547 svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
548 struct svgtiny_parse_state state
)
550 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
552 for (xmlAttr
*attr
= line
->properties
; attr
; attr
= attr
->next
) {
553 const char *name
= (const char *) attr
->name
;
554 const char *content
= (const char *) attr
->children
->content
;
555 if (strcmp(name
, "x1") == 0)
556 x1
= svgtiny_parse_length(content
,
557 state
.viewport_width
, state
);
558 else if (strcmp(name
, "y1") == 0)
559 y1
= svgtiny_parse_length(content
,
560 state
.viewport_height
, state
);
561 else if (strcmp(name
, "x2") == 0)
562 x2
= svgtiny_parse_length(content
,
563 state
.viewport_width
, state
);
564 else if (strcmp(name
, "y2") == 0)
565 y2
= svgtiny_parse_length(content
,
566 state
.viewport_height
, state
);
568 svgtiny_parse_paint_attributes(line
, &state
);
569 svgtiny_parse_transform_attributes(line
, &state
);
571 float *p
= malloc(7 * sizeof p
[0]);
573 return svgtiny_OUT_OF_MEMORY
;
575 p
[0] = svgtiny_PATH_MOVE
;
578 p
[3] = svgtiny_PATH_LINE
;
581 p
[6] = svgtiny_PATH_CLOSE
;
583 return svgtiny_add_path(p
, 7, &state
);
588 * Parse a <polyline> or <polygon> element node.
590 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
591 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
594 svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
595 struct svgtiny_parse_state state
, bool polygon
)
599 svgtiny_parse_paint_attributes(poly
, &state
);
600 svgtiny_parse_transform_attributes(poly
, &state
);
602 /* read points attribute */
603 s
= points
= (char *) xmlGetProp(poly
, (const xmlChar
*) "points");
605 state
.diagram
->error_line
= poly
->line
;
606 state
.diagram
->error_message
=
607 "polyline/polygon: missing points attribute";
608 return svgtiny_SVG_ERROR
;
611 /* allocate space for path: it will never have more elements than s */
612 float *p
= malloc(sizeof p
[0] * strlen(s
));
615 return svgtiny_OUT_OF_MEMORY
;
618 /* parse s and build path */
619 for (unsigned int i
= 0; s
[i
]; i
++)
627 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
629 p
[i
++] = svgtiny_PATH_MOVE
;
631 p
[i
++] = svgtiny_PATH_LINE
;
640 p
[i
++] = svgtiny_PATH_CLOSE
;
644 return svgtiny_add_path(p
, i
, &state
);
649 * Parse a <text> or <tspan> element node.
652 svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
653 struct svgtiny_parse_state state
)
655 float x
, y
, width
, height
;
657 svgtiny_parse_position_attributes(text
, state
,
658 &x
, &y
, &width
, &height
);
659 svgtiny_parse_font_attributes(text
, &state
);
660 svgtiny_parse_transform_attributes(text
, &state
);
662 float px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
663 float py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
664 /* state.ctm.e = px - state.origin_x; */
665 /* state.ctm.f = py - state.origin_y; */
667 /*struct css_style style = state.style;
668 style.font_size.value.length.value *= state.ctm.a;*/
670 for (xmlNode
*child
= text
->children
; child
; child
= child
->next
) {
671 svgtiny_code code
= svgtiny_OK
;
673 if (child
->type
== XML_TEXT_NODE
) {
674 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
676 return svgtiny_OUT_OF_MEMORY
;
677 shape
->text
= strdup((const char *) child
->content
);
680 state
.diagram
->shape_count
++;
682 } else if (child
->type
== XML_ELEMENT_NODE
&&
683 strcmp((const char *) child
->name
,
685 code
= svgtiny_parse_text(child
, state
);
688 if (!code
!= svgtiny_OK
)
697 * Parse x, y, width, and height attributes, if present.
700 void svgtiny_parse_position_attributes(const xmlNode
*node
,
701 const struct svgtiny_parse_state state
,
702 float *x
, float *y
, float *width
, float *height
)
706 *width
= state
.viewport_width
;
707 *height
= state
.viewport_height
;
709 for (xmlAttr
*attr
= node
->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
, "x") == 0)
713 *x
= svgtiny_parse_length(content
,
714 state
.viewport_width
, state
);
715 else if (strcmp(name
, "y") == 0)
716 *y
= svgtiny_parse_length(content
,
717 state
.viewport_height
, state
);
718 else if (strcmp(name
, "width") == 0)
719 *width
= svgtiny_parse_length(content
,
720 state
.viewport_width
, state
);
721 else if (strcmp(name
, "height") == 0)
722 *height
= svgtiny_parse_length(content
,
723 state
.viewport_height
, state
);
729 * Parse a length as a number of pixels.
732 float svgtiny_parse_length(const char *s
, int viewport_size
,
733 const struct svgtiny_parse_state state
)
735 int num_length
= strspn(s
, "0123456789+-.");
736 const char *unit
= s
+ num_length
;
737 float n
= atof((const char *) s
);
738 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
744 } else if (unit
[0] == '%') {
745 return n
/ 100.0 * viewport_size
;
746 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
747 return n
* font_size
;
748 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
749 return n
/ 2.0 * font_size
;
750 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
752 } else if (unit
[0] == 'p' && unit
[1] == 't') {
754 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
756 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
758 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
760 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
769 * Parse paint attributes, if present.
772 void svgtiny_parse_paint_attributes(const xmlNode
*node
,
773 struct svgtiny_parse_state
*state
)
775 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
776 const char *name
= (const char *) attr
->name
;
777 const char *content
= (const char *) attr
->children
->content
;
778 if (strcmp(name
, "fill") == 0)
779 svgtiny_parse_color(content
, &state
->fill
, state
);
780 else if (strcmp(name
, "stroke") == 0)
781 svgtiny_parse_color(content
, &state
->stroke
, state
);
782 else if (strcmp(name
, "stroke-width") == 0)
783 state
->stroke_width
= svgtiny_parse_length(content
,
784 state
->viewport_width
, *state
);
785 else if (strcmp(name
, "style") == 0) {
786 const char *style
= (const char *)
787 attr
->children
->content
;
790 if ((s
= strstr(style
, "fill:"))) {
794 value
= strndup(s
, strcspn(s
, "; "));
795 svgtiny_parse_color(value
, &state
->fill
, state
);
798 if ((s
= strstr(style
, "stroke:"))) {
802 value
= strndup(s
, strcspn(s
, "; "));
803 svgtiny_parse_color(value
, &state
->stroke
, state
);
806 if ((s
= strstr(style
, "stroke-width:"))) {
810 state
->stroke_width
= svgtiny_parse_length(s
,
811 state
->viewport_width
, *state
);
822 void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
823 struct svgtiny_parse_state
*state
)
825 unsigned int r
, g
, b
;
827 size_t len
= strlen(s
);
828 char *id
= 0, *rparen
;
830 if (len
== 4 && s
[0] == '#') {
831 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
832 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
834 } else if (len
== 7 && s
[0] == '#') {
835 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
836 *c
= svgtiny_RGB(r
, g
, b
);
838 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
839 s
[3] == '(' && s
[len
- 1] == ')') {
840 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
841 *c
= svgtiny_RGB(r
, g
, b
);
842 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
846 *c
= svgtiny_RGB(r
, g
, b
);
849 } else if (len
== 4 && strcmp(s
, "none") == 0) {
850 *c
= svgtiny_TRANSPARENT
;
852 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
858 rparen
= strchr(id
, ')');
861 svgtiny_find_gradient(id
, state
);
863 fprintf(stderr
, "linear_gradient_stop_count %i\n",
864 state
->linear_gradient_stop_count
);
865 if (state
->linear_gradient_stop_count
== 0)
866 *c
= svgtiny_TRANSPARENT
;
867 else if (state
->linear_gradient_stop_count
== 1)
868 *c
= state
->gradient_stop
[0].color
;
870 *c
= svgtiny_LINEAR_GRADIENT
;
874 const struct svgtiny_named_color
*named_color
;
875 named_color
= svgtiny_color_lookup(s
, strlen(s
));
877 *c
= named_color
->color
;
883 * Parse font attributes, if present.
886 void svgtiny_parse_font_attributes(const xmlNode
*node
,
887 struct svgtiny_parse_state
*state
)
891 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
892 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
893 /*if (css_parse_length(
894 (const char *) attr->children->content,
895 &state->style.font_size.value.length,
897 state->style.font_size.size =
898 CSS_FONT_SIZE_LENGTH;
906 * Parse transform attributes, if present.
908 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
911 void svgtiny_parse_transform_attributes(xmlNode
*node
,
912 struct svgtiny_parse_state
*state
)
916 /* parse transform */
917 transform
= (char *) xmlGetProp(node
, (const xmlChar
*) "transform");
919 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
920 &state
->ctm
.c
, &state
->ctm
.d
,
921 &state
->ctm
.e
, &state
->ctm
.f
);
928 * Parse a transform string.
931 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
932 float *mc
, float *md
, float *me
, float *mf
)
934 float a
, b
, c
, d
, e
, f
;
935 float za
, zb
, zc
, zd
, ze
, zf
;
939 for (unsigned int i
= 0; s
[i
]; i
++)
947 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
948 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
950 else if (sscanf(s
, "translate (%f %f) %n",
953 else if (sscanf(s
, "translate (%f) %n",
956 else if (sscanf(s
, "scale (%f %f) %n",
959 else if (sscanf(s
, "scale (%f) %n",
962 else if (sscanf(s
, "rotate (%f %f %f) %n",
963 &angle
, &x
, &y
, &n
) == 3) {
964 angle
= angle
/ 180 * M_PI
;
969 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
970 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
971 } else if (sscanf(s
, "rotate (%f) %n",
973 angle
= angle
/ 180 * M_PI
;
978 } else if (sscanf(s
, "skewX (%f) %n",
980 angle
= angle
/ 180 * M_PI
;
982 } else if (sscanf(s
, "skewY (%f) %n",
984 angle
= angle
/ 180 * M_PI
;
988 za
= *ma
* a
+ *mc
* b
;
989 zb
= *mb
* a
+ *md
* b
;
990 zc
= *ma
* c
+ *mc
* d
;
991 zd
= *mb
* c
+ *md
* d
;
992 ze
= *ma
* e
+ *mc
* f
+ *me
;
993 zf
= *mb
* e
+ *md
* f
+ *mf
;
1006 * Add a path to the svgtiny_diagram.
1009 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1010 struct svgtiny_parse_state
*state
)
1012 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1013 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1015 svgtiny_transform_path(p
, n
, state
);
1017 struct svgtiny_shape
*shape
= svgtiny_add_shape(state
);
1020 return svgtiny_OUT_OF_MEMORY
;
1023 shape
->path_length
= n
;
1024 state
->diagram
->shape_count
++;
1031 * Add a svgtiny_shape to the svgtiny_diagram.
1034 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1036 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1037 (state
->diagram
->shape_count
+ 1) *
1038 sizeof (state
->diagram
->shape
[0]));
1041 state
->diagram
->shape
= shape
;
1043 shape
+= state
->diagram
->shape_count
;
1045 shape
->path_length
= 0;
1047 shape
->fill
= state
->fill
;
1048 shape
->stroke
= state
->stroke
;
1049 shape
->stroke_width
= state
->stroke_width
*
1050 (state
->ctm
.a
+ state
->ctm
.d
) / 2;
1057 * Apply the current transformation matrix to a path.
1060 void svgtiny_transform_path(float *p
, unsigned int n
,
1061 struct svgtiny_parse_state
*state
)
1063 for (unsigned int j
= 0; j
!= n
; ) {
1064 unsigned int points
= 0;
1065 switch ((int) p
[j
]) {
1066 case svgtiny_PATH_MOVE
:
1067 case svgtiny_PATH_LINE
:
1070 case svgtiny_PATH_CLOSE
:
1073 case svgtiny_PATH_BEZIER
:
1080 for (unsigned int k
= 0; k
!= points
; k
++) {
1081 float x0
= p
[j
], y0
= p
[j
+ 1];
1082 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1084 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1095 * Free all memory used by a diagram.
1098 void svgtiny_free(struct svgtiny_diagram
*svg
)
1102 for (unsigned int i
= 0; i
!= svg
->shape_count
; i
++) {
1103 free(svg
->shape
[i
].path
);
1104 free(svg
->shape
[i
].text
);