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>
15 #include <libxml/parser.h>
16 #include <libxml/debugXML.h>
18 #include "svgtiny_internal.h"
21 #define M_PI 3.14159265358979323846
24 #define KAPPA 0.5522847498
26 static svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
27 struct svgtiny_parse_state state
);
28 static svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
29 struct svgtiny_parse_state state
);
30 static svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
31 struct svgtiny_parse_state state
);
32 static svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
33 struct svgtiny_parse_state state
);
34 static svgtiny_code
svgtiny_parse_ellipse(xmlNode
*ellipse
,
35 struct svgtiny_parse_state state
);
36 static svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
37 struct svgtiny_parse_state state
);
38 static svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
39 struct svgtiny_parse_state state
, bool polygon
);
40 static svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
41 struct svgtiny_parse_state state
);
42 static void svgtiny_parse_position_attributes(const xmlNode
*node
,
43 const struct svgtiny_parse_state state
,
44 float *x
, float *y
, float *width
, float *height
);
45 static void svgtiny_parse_paint_attributes(const xmlNode
*node
,
46 struct svgtiny_parse_state
*state
);
47 static void svgtiny_parse_font_attributes(const xmlNode
*node
,
48 struct svgtiny_parse_state
*state
);
49 static void svgtiny_parse_transform_attributes(xmlNode
*node
,
50 struct svgtiny_parse_state
*state
);
51 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
52 struct svgtiny_parse_state
*state
);
56 * Create a new svgtiny_diagram structure.
59 struct svgtiny_diagram
*svgtiny_create(void)
61 struct svgtiny_diagram
*diagram
;
63 diagram
= malloc(sizeof *diagram
);
68 diagram
->shape_count
= 0;
69 diagram
->error_line
= 0;
70 diagram
->error_message
= 0;
77 * Parse a block of memory into a svgtiny_diagram.
80 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
81 const char *buffer
, size_t size
, const char *url
,
82 int viewport_width
, int viewport_height
)
86 struct svgtiny_parse_state state
;
87 float x
, y
, width
, height
;
94 /* parse XML to tree */
95 document
= xmlReadMemory(buffer
, size
, url
, 0,
96 XML_PARSE_NONET
| XML_PARSE_COMPACT
);
98 return svgtiny_LIBXML_ERROR
;
100 /*xmlDebugDumpDocument(stderr, document);*/
102 /* find root <svg> element */
103 svg
= xmlDocGetRootElement(document
);
105 return svgtiny_NOT_SVG
;
106 if (strcmp((const char *) svg
->name
, "svg") != 0)
107 return svgtiny_NOT_SVG
;
109 /* get graphic dimensions */
110 state
.diagram
= diagram
;
111 state
.document
= document
;
112 state
.viewport_width
= viewport_width
;
113 state
.viewport_height
= viewport_height
;
114 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
115 diagram
->width
= width
;
116 diagram
->height
= height
;
118 /* set up parsing state */
119 state
.viewport_width
= width
;
120 state
.viewport_height
= height
;
121 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
124 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
125 state
.ctm
.e
= 0; /*x;*/
126 state
.ctm
.f
= 0; /*y;*/
127 /*state.style = css_base_style;
128 state.style.font_size.value.length.value = option_font_size * 0.1;*/
129 state
.fill
= 0x000000;
130 state
.stroke
= svgtiny_TRANSPARENT
;
131 state
.stroke_width
= 1;
132 state
.linear_gradient_stop_count
= 0;
135 code
= svgtiny_parse_svg(svg
, state
);
138 xmlFreeDoc(document
);
145 * Parse a <svg> or <g> element node.
148 svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
149 struct svgtiny_parse_state state
)
151 float x
, y
, width
, height
;
155 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
156 svgtiny_parse_paint_attributes(svg
, &state
);
157 svgtiny_parse_font_attributes(svg
, &state
);
160 view_box
= xmlHasProp(svg
, (const xmlChar
*) "viewBox");
162 const char *s
= (const char *) view_box
->children
->content
;
163 float min_x
, min_y
, vwidth
, vheight
;
164 if (sscanf(s
, "%f,%f,%f,%f",
165 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
166 sscanf(s
, "%f %f %f %f",
167 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
168 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
169 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
170 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
171 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
175 svgtiny_parse_transform_attributes(svg
, &state
);
177 for (child
= svg
->children
; child
; child
= child
->next
) {
178 svgtiny_code code
= svgtiny_OK
;
180 if (child
->type
== XML_ELEMENT_NODE
) {
181 const char *name
= (const char *) child
->name
;
182 if (strcmp(name
, "svg") == 0)
183 code
= svgtiny_parse_svg(child
, state
);
184 else if (strcmp(name
, "g") == 0)
185 code
= svgtiny_parse_svg(child
, state
);
186 else if (strcmp(name
, "a") == 0)
187 code
= svgtiny_parse_svg(child
, state
);
188 else if (strcmp(name
, "path") == 0)
189 code
= svgtiny_parse_path(child
, state
);
190 else if (strcmp(name
, "rect") == 0)
191 code
= svgtiny_parse_rect(child
, state
);
192 else if (strcmp(name
, "circle") == 0)
193 code
= svgtiny_parse_circle(child
, state
);
194 else if (strcmp(name
, "ellipse") == 0)
195 code
= svgtiny_parse_ellipse(child
, state
);
196 else if (strcmp(name
, "line") == 0)
197 code
= svgtiny_parse_line(child
, state
);
198 else if (strcmp(name
, "polyline") == 0)
199 code
= svgtiny_parse_poly(child
, state
, false);
200 else if (strcmp(name
, "polygon") == 0)
201 code
= svgtiny_parse_poly(child
, state
, true);
202 else if (strcmp(name
, "text") == 0)
203 code
= svgtiny_parse_text(child
, state
);
206 if (code
!= svgtiny_OK
)
216 * Parse a <path> element node.
218 * http://www.w3.org/TR/SVG11/paths#PathElement
221 svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
222 struct svgtiny_parse_state state
)
227 float last_x
= 0, last_y
= 0;
228 float last_cubic_x
= 0, last_cubic_y
= 0;
229 float last_quad_x
= 0, last_quad_y
= 0;
231 svgtiny_parse_paint_attributes(path
, &state
);
232 svgtiny_parse_transform_attributes(path
, &state
);
234 /* read d attribute */
235 s
= path_d
= (char *) xmlGetProp(path
, (const xmlChar
*) "d");
237 state
.diagram
->error_line
= path
->line
;
238 state
.diagram
->error_message
= "path: missing d attribute";
239 return svgtiny_SVG_ERROR
;
242 /* allocate space for path: it will never have more elements than d */
243 p
= malloc(sizeof p
[0] * strlen(s
));
245 return svgtiny_OUT_OF_MEMORY
;
247 /* parse d and build path */
248 for (i
= 0; s
[i
]; i
++)
255 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
258 /* moveto (M, m), lineto (L, l) (2 arguments) */
259 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
260 /*LOG(("moveto or lineto"));*/
261 if (*command
== 'M' || *command
== 'm')
262 plot_command
= svgtiny_PATH_MOVE
;
264 plot_command
= svgtiny_PATH_LINE
;
266 p
[i
++] = plot_command
;
267 if ('a' <= *command
) {
271 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
273 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
276 plot_command
= svgtiny_PATH_LINE
;
277 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
279 /* closepath (Z, z) (no arguments) */
280 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
281 /*LOG(("closepath"));*/
282 p
[i
++] = svgtiny_PATH_CLOSE
;
285 /* horizontal lineto (H, h) (1 argument) */
286 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
287 /*LOG(("horizontal lineto"));*/
289 p
[i
++] = svgtiny_PATH_LINE
;
292 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
294 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
296 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
298 /* vertical lineto (V, v) (1 argument) */
299 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
300 /*LOG(("vertical lineto"));*/
302 p
[i
++] = svgtiny_PATH_LINE
;
305 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
306 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
309 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
311 /* curveto (C, c) (6 arguments) */
312 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
313 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
314 /*LOG(("curveto"));*/
316 p
[i
++] = svgtiny_PATH_BEZIER
;
317 if (*command
== 'c') {
327 p
[i
++] = last_cubic_x
= x2
;
328 p
[i
++] = last_cubic_y
= y2
;
329 p
[i
++] = last_quad_x
= last_x
= x
;
330 p
[i
++] = last_quad_y
= last_y
= y
;
332 } while (sscanf(s
, "%f %f %f %f %f %f %n",
333 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
335 /* shorthand/smooth curveto (S, s) (4 arguments) */
336 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
337 &x2
, &y2
, &x
, &y
, &n
) == 5) {
338 /*LOG(("shorthand/smooth curveto"));*/
340 p
[i
++] = svgtiny_PATH_BEZIER
;
341 x1
= last_x
+ (last_x
- last_cubic_x
);
342 y1
= last_y
+ (last_y
- last_cubic_y
);
343 if (*command
== 's') {
351 p
[i
++] = last_cubic_x
= x2
;
352 p
[i
++] = last_cubic_y
= y2
;
353 p
[i
++] = last_quad_x
= last_x
= x
;
354 p
[i
++] = last_quad_y
= last_y
= y
;
356 } while (sscanf(s
, "%f %f %f %f %n",
357 &x2
, &y2
, &x
, &y
, &n
) == 4);
359 /* quadratic Bezier curveto (Q, q) (4 arguments) */
360 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
361 &x1
, &y1
, &x
, &y
, &n
) == 5) {
362 /*LOG(("quadratic Bezier curveto"));*/
364 p
[i
++] = svgtiny_PATH_BEZIER
;
367 if (*command
== 'q') {
373 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
374 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
375 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
376 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
377 p
[i
++] = last_cubic_x
= last_x
= x
;
378 p
[i
++] = last_cubic_y
= last_y
= y
;
380 } while (sscanf(s
, "%f %f %f %f %n",
381 &x1
, &y1
, &x
, &y
, &n
) == 4);
383 /* shorthand/smooth quadratic Bezier curveto (T, t)
385 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
387 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
389 p
[i
++] = svgtiny_PATH_BEZIER
;
390 x1
= last_x
+ (last_x
- last_quad_x
);
391 y1
= last_y
+ (last_y
- last_quad_y
);
394 if (*command
== 't') {
400 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
401 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
402 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
403 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
404 p
[i
++] = last_cubic_x
= last_x
= x
;
405 p
[i
++] = last_cubic_y
= last_y
= y
;
407 } while (sscanf(s
, "%f %f %n",
410 /* elliptical arc (A, a) (7 arguments) */
411 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
412 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
415 p
[i
++] = svgtiny_PATH_LINE
;
416 if (*command
== 'a') {
420 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
422 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
425 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
426 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
430 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
438 /* no real segments in path */
443 return svgtiny_add_path(p
, i
, &state
);
448 * Parse a <rect> element node.
450 * http://www.w3.org/TR/SVG11/shapes#RectElement
453 svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
454 struct svgtiny_parse_state state
)
456 float x
, y
, width
, height
;
459 svgtiny_parse_position_attributes(rect
, state
,
460 &x
, &y
, &width
, &height
);
461 svgtiny_parse_paint_attributes(rect
, &state
);
462 svgtiny_parse_transform_attributes(rect
, &state
);
464 p
= malloc(13 * sizeof p
[0]);
466 return svgtiny_OUT_OF_MEMORY
;
468 p
[0] = svgtiny_PATH_MOVE
;
471 p
[3] = svgtiny_PATH_LINE
;
474 p
[6] = svgtiny_PATH_LINE
;
477 p
[9] = svgtiny_PATH_LINE
;
480 p
[12] = svgtiny_PATH_CLOSE
;
482 return svgtiny_add_path(p
, 13, &state
);
487 * Parse a <circle> element node.
490 svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
491 struct svgtiny_parse_state state
)
493 float x
= 0, y
= 0, r
= -1;
497 for (attr
= circle
->properties
; attr
; attr
= attr
->next
) {
498 const char *name
= (const char *) attr
->name
;
499 const char *content
= (const char *) attr
->children
->content
;
500 if (strcmp(name
, "cx") == 0)
501 x
= svgtiny_parse_length(content
,
502 state
.viewport_width
, state
);
503 else if (strcmp(name
, "cy") == 0)
504 y
= svgtiny_parse_length(content
,
505 state
.viewport_height
, state
);
506 else if (strcmp(name
, "r") == 0)
507 r
= svgtiny_parse_length(content
,
508 state
.viewport_width
, state
);
510 svgtiny_parse_paint_attributes(circle
, &state
);
511 svgtiny_parse_transform_attributes(circle
, &state
);
514 state
.diagram
->error_line
= circle
->line
;
515 state
.diagram
->error_message
= "circle: r missing or negative";
516 return svgtiny_SVG_ERROR
;
521 p
= malloc(32 * sizeof p
[0]);
523 return svgtiny_OUT_OF_MEMORY
;
525 p
[0] = svgtiny_PATH_MOVE
;
528 p
[3] = svgtiny_PATH_BEZIER
;
530 p
[5] = y
+ r
* KAPPA
;
531 p
[6] = x
+ r
* KAPPA
;
535 p
[10] = svgtiny_PATH_BEZIER
;
536 p
[11] = x
- r
* KAPPA
;
539 p
[14] = y
+ r
* KAPPA
;
542 p
[17] = svgtiny_PATH_BEZIER
;
544 p
[19] = y
- r
* KAPPA
;
545 p
[20] = x
- r
* KAPPA
;
549 p
[24] = svgtiny_PATH_BEZIER
;
550 p
[25] = x
+ r
* KAPPA
;
553 p
[28] = y
- r
* KAPPA
;
556 p
[31] = svgtiny_PATH_CLOSE
;
558 return svgtiny_add_path(p
, 32, &state
);
563 * Parse an <ellipse> element node.
566 svgtiny_code
svgtiny_parse_ellipse(xmlNode
*ellipse
,
567 struct svgtiny_parse_state state
)
569 float x
= 0, y
= 0, rx
= -1, ry
= -1;
573 for (attr
= ellipse
->properties
; attr
; attr
= attr
->next
) {
574 const char *name
= (const char *) attr
->name
;
575 const char *content
= (const char *) attr
->children
->content
;
576 if (strcmp(name
, "cx") == 0)
577 x
= svgtiny_parse_length(content
,
578 state
.viewport_width
, state
);
579 else if (strcmp(name
, "cy") == 0)
580 y
= svgtiny_parse_length(content
,
581 state
.viewport_height
, state
);
582 else if (strcmp(name
, "rx") == 0)
583 rx
= svgtiny_parse_length(content
,
584 state
.viewport_width
, state
);
585 else if (strcmp(name
, "ry") == 0)
586 ry
= svgtiny_parse_length(content
,
587 state
.viewport_width
, state
);
589 svgtiny_parse_paint_attributes(ellipse
, &state
);
590 svgtiny_parse_transform_attributes(ellipse
, &state
);
592 if (rx
< 0 || ry
< 0) {
593 state
.diagram
->error_line
= ellipse
->line
;
594 state
.diagram
->error_message
= "ellipse: rx or ry missing "
596 return svgtiny_SVG_ERROR
;
598 if (rx
== 0 || ry
== 0)
601 p
= malloc(32 * sizeof p
[0]);
603 return svgtiny_OUT_OF_MEMORY
;
605 p
[0] = svgtiny_PATH_MOVE
;
608 p
[3] = svgtiny_PATH_BEZIER
;
610 p
[5] = y
+ ry
* KAPPA
;
611 p
[6] = x
+ rx
* KAPPA
;
615 p
[10] = svgtiny_PATH_BEZIER
;
616 p
[11] = x
- rx
* KAPPA
;
619 p
[14] = y
+ ry
* KAPPA
;
622 p
[17] = svgtiny_PATH_BEZIER
;
624 p
[19] = y
- ry
* KAPPA
;
625 p
[20] = x
- rx
* KAPPA
;
629 p
[24] = svgtiny_PATH_BEZIER
;
630 p
[25] = x
+ rx
* KAPPA
;
633 p
[28] = y
- ry
* KAPPA
;
636 p
[31] = svgtiny_PATH_CLOSE
;
638 return svgtiny_add_path(p
, 32, &state
);
643 * Parse a <line> element node.
646 svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
647 struct svgtiny_parse_state state
)
649 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
653 for (attr
= line
->properties
; attr
; attr
= attr
->next
) {
654 const char *name
= (const char *) attr
->name
;
655 const char *content
= (const char *) attr
->children
->content
;
656 if (strcmp(name
, "x1") == 0)
657 x1
= svgtiny_parse_length(content
,
658 state
.viewport_width
, state
);
659 else if (strcmp(name
, "y1") == 0)
660 y1
= svgtiny_parse_length(content
,
661 state
.viewport_height
, state
);
662 else if (strcmp(name
, "x2") == 0)
663 x2
= svgtiny_parse_length(content
,
664 state
.viewport_width
, state
);
665 else if (strcmp(name
, "y2") == 0)
666 y2
= svgtiny_parse_length(content
,
667 state
.viewport_height
, state
);
669 svgtiny_parse_paint_attributes(line
, &state
);
670 svgtiny_parse_transform_attributes(line
, &state
);
672 p
= malloc(7 * sizeof p
[0]);
674 return svgtiny_OUT_OF_MEMORY
;
676 p
[0] = svgtiny_PATH_MOVE
;
679 p
[3] = svgtiny_PATH_LINE
;
682 p
[6] = svgtiny_PATH_CLOSE
;
684 return svgtiny_add_path(p
, 7, &state
);
689 * Parse a <polyline> or <polygon> element node.
691 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
692 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
695 svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
696 struct svgtiny_parse_state state
, bool polygon
)
702 svgtiny_parse_paint_attributes(poly
, &state
);
703 svgtiny_parse_transform_attributes(poly
, &state
);
705 /* read points attribute */
706 s
= points
= (char *) xmlGetProp(poly
, (const xmlChar
*) "points");
708 state
.diagram
->error_line
= poly
->line
;
709 state
.diagram
->error_message
=
710 "polyline/polygon: missing points attribute";
711 return svgtiny_SVG_ERROR
;
714 /* allocate space for path: it will never have more elements than s */
715 p
= malloc(sizeof p
[0] * strlen(s
));
718 return svgtiny_OUT_OF_MEMORY
;
721 /* parse s and build path */
722 for (i
= 0; s
[i
]; i
++)
730 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
732 p
[i
++] = svgtiny_PATH_MOVE
;
734 p
[i
++] = svgtiny_PATH_LINE
;
743 p
[i
++] = svgtiny_PATH_CLOSE
;
747 return svgtiny_add_path(p
, i
, &state
);
752 * Parse a <text> or <tspan> element node.
755 svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
756 struct svgtiny_parse_state state
)
758 float x
, y
, width
, height
;
762 svgtiny_parse_position_attributes(text
, state
,
763 &x
, &y
, &width
, &height
);
764 svgtiny_parse_font_attributes(text
, &state
);
765 svgtiny_parse_transform_attributes(text
, &state
);
767 px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
768 py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
769 /* state.ctm.e = px - state.origin_x; */
770 /* state.ctm.f = py - state.origin_y; */
772 /*struct css_style style = state.style;
773 style.font_size.value.length.value *= state.ctm.a;*/
775 for (child
= text
->children
; child
; child
= child
->next
) {
776 svgtiny_code code
= svgtiny_OK
;
778 if (child
->type
== XML_TEXT_NODE
) {
779 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
781 return svgtiny_OUT_OF_MEMORY
;
782 shape
->text
= strdup((const char *) child
->content
);
785 state
.diagram
->shape_count
++;
787 } else if (child
->type
== XML_ELEMENT_NODE
&&
788 strcmp((const char *) child
->name
,
790 code
= svgtiny_parse_text(child
, state
);
793 if (!code
!= svgtiny_OK
)
802 * Parse x, y, width, and height attributes, if present.
805 void svgtiny_parse_position_attributes(const xmlNode
*node
,
806 const struct svgtiny_parse_state state
,
807 float *x
, float *y
, float *width
, float *height
)
813 *width
= state
.viewport_width
;
814 *height
= state
.viewport_height
;
816 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
817 const char *name
= (const char *) attr
->name
;
818 const char *content
= (const char *) attr
->children
->content
;
819 if (strcmp(name
, "x") == 0)
820 *x
= svgtiny_parse_length(content
,
821 state
.viewport_width
, state
);
822 else if (strcmp(name
, "y") == 0)
823 *y
= svgtiny_parse_length(content
,
824 state
.viewport_height
, state
);
825 else if (strcmp(name
, "width") == 0)
826 *width
= svgtiny_parse_length(content
,
827 state
.viewport_width
, state
);
828 else if (strcmp(name
, "height") == 0)
829 *height
= svgtiny_parse_length(content
,
830 state
.viewport_height
, state
);
836 * Parse a length as a number of pixels.
839 float svgtiny_parse_length(const char *s
, int viewport_size
,
840 const struct svgtiny_parse_state state
)
842 int num_length
= strspn(s
, "0123456789+-.");
843 const char *unit
= s
+ num_length
;
844 float n
= atof((const char *) s
);
845 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
851 } else if (unit
[0] == '%') {
852 return n
/ 100.0 * viewport_size
;
853 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
854 return n
* font_size
;
855 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
856 return n
/ 2.0 * font_size
;
857 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
859 } else if (unit
[0] == 'p' && unit
[1] == 't') {
861 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
863 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
865 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
867 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
876 * Parse paint attributes, if present.
879 void svgtiny_parse_paint_attributes(const xmlNode
*node
,
880 struct svgtiny_parse_state
*state
)
884 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
885 const char *name
= (const char *) attr
->name
;
886 const char *content
= (const char *) attr
->children
->content
;
887 if (strcmp(name
, "fill") == 0)
888 svgtiny_parse_color(content
, &state
->fill
, state
);
889 else if (strcmp(name
, "stroke") == 0)
890 svgtiny_parse_color(content
, &state
->stroke
, state
);
891 else if (strcmp(name
, "stroke-width") == 0)
892 state
->stroke_width
= svgtiny_parse_length(content
,
893 state
->viewport_width
, *state
);
894 else if (strcmp(name
, "style") == 0) {
895 const char *style
= (const char *)
896 attr
->children
->content
;
899 if ((s
= strstr(style
, "fill:"))) {
903 value
= strndup(s
, strcspn(s
, "; "));
904 svgtiny_parse_color(value
, &state
->fill
, state
);
907 if ((s
= strstr(style
, "stroke:"))) {
911 value
= strndup(s
, strcspn(s
, "; "));
912 svgtiny_parse_color(value
, &state
->stroke
, state
);
915 if ((s
= strstr(style
, "stroke-width:"))) {
919 value
= strndup(s
, strcspn(s
, "; "));
920 state
->stroke_width
= svgtiny_parse_length(value
,
921 state
->viewport_width
, *state
);
933 void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
934 struct svgtiny_parse_state
*state
)
936 unsigned int r
, g
, b
;
938 size_t len
= strlen(s
);
939 char *id
= 0, *rparen
;
941 if (len
== 4 && s
[0] == '#') {
942 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
943 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
945 } else if (len
== 7 && s
[0] == '#') {
946 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
947 *c
= svgtiny_RGB(r
, g
, b
);
949 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
950 s
[3] == '(' && s
[len
- 1] == ')') {
951 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
952 *c
= svgtiny_RGB(r
, g
, b
);
953 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
957 *c
= svgtiny_RGB(r
, g
, b
);
960 } else if (len
== 4 && strcmp(s
, "none") == 0) {
961 *c
= svgtiny_TRANSPARENT
;
963 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
969 rparen
= strchr(id
, ')');
972 svgtiny_find_gradient(id
, state
);
974 fprintf(stderr
, "linear_gradient_stop_count %i\n",
975 state
->linear_gradient_stop_count
);
976 if (state
->linear_gradient_stop_count
== 0)
977 *c
= svgtiny_TRANSPARENT
;
978 else if (state
->linear_gradient_stop_count
== 1)
979 *c
= state
->gradient_stop
[0].color
;
981 *c
= svgtiny_LINEAR_GRADIENT
;
985 const struct svgtiny_named_color
*named_color
;
986 named_color
= svgtiny_color_lookup(s
, strlen(s
));
988 *c
= named_color
->color
;
994 * Parse font attributes, if present.
997 void svgtiny_parse_font_attributes(const xmlNode
*node
,
998 struct svgtiny_parse_state
*state
)
1000 const xmlAttr
*attr
;
1004 for (attr
= node
->properties
; attr
; attr
= attr
->next
) {
1005 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
1006 /*if (css_parse_length(
1007 (const char *) attr->children->content,
1008 &state->style.font_size.value.length,
1010 state->style.font_size.size =
1011 CSS_FONT_SIZE_LENGTH;
1019 * Parse transform attributes, if present.
1021 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1024 void svgtiny_parse_transform_attributes(xmlNode
*node
,
1025 struct svgtiny_parse_state
*state
)
1029 /* parse transform */
1030 transform
= (char *) xmlGetProp(node
, (const xmlChar
*) "transform");
1032 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1033 &state
->ctm
.c
, &state
->ctm
.d
,
1034 &state
->ctm
.e
, &state
->ctm
.f
);
1041 * Parse a transform string.
1044 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1045 float *mc
, float *md
, float *me
, float *mf
)
1047 float a
, b
, c
, d
, e
, f
;
1048 float za
, zb
, zc
, zd
, ze
, zf
;
1053 for (i
= 0; s
[i
]; i
++)
1061 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
1062 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
1064 else if (sscanf(s
, "translate (%f %f) %n",
1067 else if (sscanf(s
, "translate (%f) %n",
1070 else if (sscanf(s
, "scale (%f %f) %n",
1073 else if (sscanf(s
, "scale (%f) %n",
1076 else if (sscanf(s
, "rotate (%f %f %f) %n",
1077 &angle
, &x
, &y
, &n
) == 3) {
1078 angle
= angle
/ 180 * M_PI
;
1083 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1084 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1085 } else if (sscanf(s
, "rotate (%f) %n",
1087 angle
= angle
/ 180 * M_PI
;
1092 } else if (sscanf(s
, "skewX (%f) %n",
1094 angle
= angle
/ 180 * M_PI
;
1096 } else if (sscanf(s
, "skewY (%f) %n",
1098 angle
= angle
/ 180 * M_PI
;
1102 za
= *ma
* a
+ *mc
* b
;
1103 zb
= *mb
* a
+ *md
* b
;
1104 zc
= *ma
* c
+ *mc
* d
;
1105 zd
= *mb
* c
+ *md
* d
;
1106 ze
= *ma
* e
+ *mc
* f
+ *me
;
1107 zf
= *mb
* e
+ *md
* f
+ *mf
;
1120 * Add a path to the svgtiny_diagram.
1123 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1124 struct svgtiny_parse_state
*state
)
1126 struct svgtiny_shape
*shape
;
1128 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1129 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1131 svgtiny_transform_path(p
, n
, state
);
1133 shape
= svgtiny_add_shape(state
);
1136 return svgtiny_OUT_OF_MEMORY
;
1139 shape
->path_length
= n
;
1140 state
->diagram
->shape_count
++;
1147 * Add a svgtiny_shape to the svgtiny_diagram.
1150 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1152 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1153 (state
->diagram
->shape_count
+ 1) *
1154 sizeof (state
->diagram
->shape
[0]));
1157 state
->diagram
->shape
= shape
;
1159 shape
+= state
->diagram
->shape_count
;
1161 shape
->path_length
= 0;
1163 shape
->fill
= state
->fill
;
1164 shape
->stroke
= state
->stroke
;
1165 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1166 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1167 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1168 shape
->stroke_width
= 1;
1175 * Apply the current transformation matrix to a path.
1178 void svgtiny_transform_path(float *p
, unsigned int n
,
1179 struct svgtiny_parse_state
*state
)
1183 for (j
= 0; j
!= n
; ) {
1184 unsigned int points
= 0;
1186 switch ((int) p
[j
]) {
1187 case svgtiny_PATH_MOVE
:
1188 case svgtiny_PATH_LINE
:
1191 case svgtiny_PATH_CLOSE
:
1194 case svgtiny_PATH_BEZIER
:
1201 for (k
= 0; k
!= points
; k
++) {
1202 float x0
= p
[j
], y0
= p
[j
+ 1];
1203 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1205 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1216 * Free all memory used by a diagram.
1219 void svgtiny_free(struct svgtiny_diagram
*svg
)
1224 for (i
= 0; i
!= svg
->shape_count
; i
++) {
1225 free(svg
->shape
[i
].path
);
1226 free(svg
->shape
[i
].text
);
1234 #ifndef HAVE_STRNDUP
1235 char *svgtiny_strndup(const char *s
, size_t n
)
1240 for (len
= 0; len
!= n
&& s
[len
]; len
++)
1243 s2
= malloc(len
+ 1);