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>
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 #define KAPPA 0.5522847498
27 static svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
28 struct svgtiny_parse_state state
);
29 static svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
30 struct svgtiny_parse_state state
);
31 static svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
32 struct svgtiny_parse_state state
);
33 static svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
34 struct svgtiny_parse_state state
);
35 static svgtiny_code
svgtiny_parse_ellipse(xmlNode
*ellipse
,
36 struct svgtiny_parse_state state
);
37 static svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
38 struct svgtiny_parse_state state
);
39 static svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
40 struct svgtiny_parse_state state
, bool polygon
);
41 static svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
42 struct svgtiny_parse_state state
);
43 static void svgtiny_parse_position_attributes(const xmlNode
*node
,
44 const struct svgtiny_parse_state state
,
45 float *x
, float *y
, float *width
, float *height
);
46 static void svgtiny_parse_paint_attributes(const xmlNode
*node
,
47 struct svgtiny_parse_state
*state
);
48 static void svgtiny_parse_font_attributes(const xmlNode
*node
,
49 struct svgtiny_parse_state
*state
);
50 static void svgtiny_parse_transform_attributes(xmlNode
*node
,
51 struct svgtiny_parse_state
*state
);
52 static svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
53 struct svgtiny_parse_state
*state
);
57 * Create a new svgtiny_diagram structure.
60 struct svgtiny_diagram
*svgtiny_create(void)
62 struct svgtiny_diagram
*diagram
;
64 diagram
= malloc(sizeof *diagram
);
69 diagram
->shape_count
= 0;
70 diagram
->error_line
= 0;
71 diagram
->error_message
= 0;
78 * Parse a block of memory into a svgtiny_diagram.
81 svgtiny_code
svgtiny_parse(struct svgtiny_diagram
*diagram
,
82 const char *buffer
, size_t size
, const char *url
,
83 int viewport_width
, int viewport_height
)
87 struct svgtiny_parse_state state
;
88 float x
, y
, width
, height
;
95 /* parse XML to tree */
96 document
= xmlReadMemory(buffer
, size
, url
, 0,
97 XML_PARSE_NONET
| XML_PARSE_COMPACT
);
99 return svgtiny_LIBXML_ERROR
;
101 /*xmlDebugDumpDocument(stderr, document);*/
103 /* find root <svg> element */
104 svg
= xmlDocGetRootElement(document
);
106 return svgtiny_NOT_SVG
;
107 if (strcmp((const char *) svg
->name
, "svg") != 0)
108 return svgtiny_NOT_SVG
;
110 /* get graphic dimensions */
111 state
.diagram
= diagram
;
112 state
.document
= document
;
113 state
.viewport_width
= viewport_width
;
114 state
.viewport_height
= viewport_height
;
115 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
116 diagram
->width
= width
;
117 diagram
->height
= height
;
119 /* set up parsing state */
120 state
.viewport_width
= width
;
121 state
.viewport_height
= height
;
122 state
.ctm
.a
= 1; /*(float) viewport_width / (float) width;*/
125 state
.ctm
.d
= 1; /*(float) viewport_height / (float) height;*/
126 state
.ctm
.e
= 0; /*x;*/
127 state
.ctm
.f
= 0; /*y;*/
128 /*state.style = css_base_style;
129 state.style.font_size.value.length.value = option_font_size * 0.1;*/
130 state
.fill
= 0x000000;
131 state
.stroke
= svgtiny_TRANSPARENT
;
132 state
.stroke_width
= 1;
133 state
.linear_gradient_stop_count
= 0;
136 code
= svgtiny_parse_svg(svg
, state
);
139 xmlFreeDoc(document
);
146 * Parse a <svg> or <g> element node.
149 svgtiny_code
svgtiny_parse_svg(xmlNode
*svg
,
150 struct svgtiny_parse_state state
)
152 float x
, y
, width
, height
;
154 svgtiny_parse_position_attributes(svg
, state
, &x
, &y
, &width
, &height
);
155 svgtiny_parse_paint_attributes(svg
, &state
);
156 svgtiny_parse_font_attributes(svg
, &state
);
159 xmlAttr
*view_box
= xmlHasProp(svg
, (const xmlChar
*) "viewBox");
161 const char *s
= (const char *) view_box
->children
->content
;
162 float min_x
, min_y
, vwidth
, vheight
;
163 if (sscanf(s
, "%f,%f,%f,%f",
164 &min_x
, &min_y
, &vwidth
, &vheight
) == 4 ||
165 sscanf(s
, "%f %f %f %f",
166 &min_x
, &min_y
, &vwidth
, &vheight
) == 4) {
167 state
.ctm
.a
= (float) state
.viewport_width
/ vwidth
;
168 state
.ctm
.d
= (float) state
.viewport_height
/ vheight
;
169 state
.ctm
.e
+= -min_x
* state
.ctm
.a
;
170 state
.ctm
.f
+= -min_y
* state
.ctm
.d
;
174 svgtiny_parse_transform_attributes(svg
, &state
);
176 for (xmlNode
*child
= svg
->children
; child
; child
= child
->next
) {
177 svgtiny_code code
= svgtiny_OK
;
179 if (child
->type
== XML_ELEMENT_NODE
) {
180 const char *name
= (const char *) child
->name
;
181 if (strcmp(name
, "svg") == 0)
182 code
= svgtiny_parse_svg(child
, state
);
183 else if (strcmp(name
, "g") == 0)
184 code
= svgtiny_parse_svg(child
, state
);
185 else if (strcmp(name
, "a") == 0)
186 code
= svgtiny_parse_svg(child
, state
);
187 else if (strcmp(name
, "path") == 0)
188 code
= svgtiny_parse_path(child
, state
);
189 else if (strcmp(name
, "rect") == 0)
190 code
= svgtiny_parse_rect(child
, state
);
191 else if (strcmp(name
, "circle") == 0)
192 code
= svgtiny_parse_circle(child
, state
);
193 else if (strcmp(name
, "ellipse") == 0)
194 code
= svgtiny_parse_ellipse(child
, state
);
195 else if (strcmp(name
, "line") == 0)
196 code
= svgtiny_parse_line(child
, state
);
197 else if (strcmp(name
, "polyline") == 0)
198 code
= svgtiny_parse_poly(child
, state
, false);
199 else if (strcmp(name
, "polygon") == 0)
200 code
= svgtiny_parse_poly(child
, state
, true);
201 else if (strcmp(name
, "text") == 0)
202 code
= svgtiny_parse_text(child
, state
);
205 if (code
!= svgtiny_OK
)
215 * Parse a <path> element node.
217 * http://www.w3.org/TR/SVG11/paths#PathElement
220 svgtiny_code
svgtiny_parse_path(xmlNode
*path
,
221 struct svgtiny_parse_state state
)
225 svgtiny_parse_paint_attributes(path
, &state
);
226 svgtiny_parse_transform_attributes(path
, &state
);
228 /* read d attribute */
229 s
= path_d
= (char *) xmlGetProp(path
, (const xmlChar
*) "d");
231 state
.diagram
->error_line
= path
->line
;
232 state
.diagram
->error_message
= "path: missing d attribute";
233 return svgtiny_SVG_ERROR
;
236 /* allocate space for path: it will never have more elements than d */
237 float *p
= malloc(sizeof p
[0] * strlen(s
));
239 return svgtiny_OUT_OF_MEMORY
;
241 /* parse d and build path */
242 for (unsigned int i
= 0; s
[i
]; i
++)
246 float last_x
= 0, last_y
= 0;
247 float last_cubic_x
= 0, last_cubic_y
= 0;
248 float last_quad_x
= 0, last_quad_y
= 0;
252 float x
, y
, x1
, y1
, x2
, y2
, rx
, ry
, rotation
, large_arc
, sweep
;
255 /* moveto (M, m), lineto (L, l) (2 arguments) */
256 if (sscanf(s
, " %1[MmLl] %f %f %n", command
, &x
, &y
, &n
) == 3) {
257 /*LOG(("moveto or lineto"));*/
258 if (*command
== 'M' || *command
== 'm')
259 plot_command
= svgtiny_PATH_MOVE
;
261 plot_command
= svgtiny_PATH_LINE
;
263 p
[i
++] = plot_command
;
264 if ('a' <= *command
) {
268 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
270 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
273 plot_command
= svgtiny_PATH_LINE
;
274 } while (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2);
276 /* closepath (Z, z) (no arguments) */
277 } else if (sscanf(s
, " %1[Zz] %n", command
, &n
) == 1) {
278 /*LOG(("closepath"));*/
279 p
[i
++] = svgtiny_PATH_CLOSE
;
282 /* horizontal lineto (H, h) (1 argument) */
283 } else if (sscanf(s
, " %1[Hh] %f %n", command
, &x
, &n
) == 2) {
284 /*LOG(("horizontal lineto"));*/
286 p
[i
++] = svgtiny_PATH_LINE
;
289 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
291 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
;
293 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
295 /* vertical lineto (V, v) (1 argument) */
296 } else if (sscanf(s
, " %1[Vv] %f %n", command
, &y
, &n
) == 2) {
297 /*LOG(("vertical lineto"));*/
299 p
[i
++] = svgtiny_PATH_LINE
;
302 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
;
303 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
306 } while (sscanf(s
, "%f %n", &x
, &n
) == 1);
308 /* curveto (C, c) (6 arguments) */
309 } else if (sscanf(s
, " %1[Cc] %f %f %f %f %f %f %n", command
,
310 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 7) {
311 /*LOG(("curveto"));*/
313 p
[i
++] = svgtiny_PATH_BEZIER
;
314 if (*command
== 'c') {
324 p
[i
++] = last_cubic_x
= x2
;
325 p
[i
++] = last_cubic_y
= y2
;
326 p
[i
++] = last_quad_x
= last_x
= x
;
327 p
[i
++] = last_quad_y
= last_y
= y
;
329 } while (sscanf(s
, "%f %f %f %f %f %f %n",
330 &x1
, &y1
, &x2
, &y2
, &x
, &y
, &n
) == 6);
332 /* shorthand/smooth curveto (S, s) (4 arguments) */
333 } else if (sscanf(s
, " %1[Ss] %f %f %f %f %n", command
,
334 &x2
, &y2
, &x
, &y
, &n
) == 5) {
335 /*LOG(("shorthand/smooth curveto"));*/
337 p
[i
++] = svgtiny_PATH_BEZIER
;
338 x1
= last_x
+ (last_x
- last_cubic_x
);
339 y1
= last_y
+ (last_y
- last_cubic_y
);
340 if (*command
== 's') {
348 p
[i
++] = last_cubic_x
= x2
;
349 p
[i
++] = last_cubic_y
= y2
;
350 p
[i
++] = last_quad_x
= last_x
= x
;
351 p
[i
++] = last_quad_y
= last_y
= y
;
353 } while (sscanf(s
, "%f %f %f %f %n",
354 &x2
, &y2
, &x
, &y
, &n
) == 4);
356 /* quadratic Bezier curveto (Q, q) (4 arguments) */
357 } else if (sscanf(s
, " %1[Qq] %f %f %f %f %n", command
,
358 &x1
, &y1
, &x
, &y
, &n
) == 5) {
359 /*LOG(("quadratic Bezier curveto"));*/
361 p
[i
++] = svgtiny_PATH_BEZIER
;
364 if (*command
== 'q') {
370 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
371 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
372 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
373 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
374 p
[i
++] = last_cubic_x
= last_x
= x
;
375 p
[i
++] = last_cubic_y
= last_y
= y
;
377 } while (sscanf(s
, "%f %f %f %f %n",
378 &x1
, &y1
, &x
, &y
, &n
) == 4);
380 /* shorthand/smooth quadratic Bezier curveto (T, t)
382 } else if (sscanf(s
, " %1[Tt] %f %f %n", command
,
384 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
386 p
[i
++] = svgtiny_PATH_BEZIER
;
387 x1
= last_x
+ (last_x
- last_quad_x
);
388 y1
= last_y
+ (last_y
- last_quad_y
);
391 if (*command
== 't') {
397 p
[i
++] = 1./3 * last_x
+ 2./3 * x1
;
398 p
[i
++] = 1./3 * last_y
+ 2./3 * y1
;
399 p
[i
++] = 2./3 * x1
+ 1./3 * x
;
400 p
[i
++] = 2./3 * y1
+ 1./3 * y
;
401 p
[i
++] = last_cubic_x
= last_x
= x
;
402 p
[i
++] = last_cubic_y
= last_y
= y
;
404 } while (sscanf(s
, "%f %f %n",
407 /* elliptical arc (A, a) (7 arguments) */
408 } else if (sscanf(s
, " %1[Aa] %f %f %f %f %f %f %f %n", command
,
409 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
412 p
[i
++] = svgtiny_PATH_LINE
;
413 if (*command
== 'a') {
417 p
[i
++] = last_cubic_x
= last_quad_x
= last_x
419 p
[i
++] = last_cubic_y
= last_quad_y
= last_y
422 } while (sscanf(s
, "%f %f %f %f %f %f %f %n",
423 &rx
, &ry
, &rotation
, &large_arc
, &sweep
,
427 fprintf(stderr
, "parse failed at \"%s\"\n", s
);
435 /* no real segments in path */
440 return svgtiny_add_path(p
, i
, &state
);
445 * Parse a <rect> element node.
447 * http://www.w3.org/TR/SVG11/shapes#RectElement
450 svgtiny_code
svgtiny_parse_rect(xmlNode
*rect
,
451 struct svgtiny_parse_state state
)
453 float x
, y
, width
, height
;
455 svgtiny_parse_position_attributes(rect
, state
,
456 &x
, &y
, &width
, &height
);
457 svgtiny_parse_paint_attributes(rect
, &state
);
458 svgtiny_parse_transform_attributes(rect
, &state
);
460 float *p
= malloc(13 * sizeof p
[0]);
462 return svgtiny_OUT_OF_MEMORY
;
464 p
[0] = svgtiny_PATH_MOVE
;
467 p
[3] = svgtiny_PATH_LINE
;
470 p
[6] = svgtiny_PATH_LINE
;
473 p
[9] = svgtiny_PATH_LINE
;
476 p
[12] = svgtiny_PATH_CLOSE
;
478 return svgtiny_add_path(p
, 13, &state
);
483 * Parse a <circle> element node.
486 svgtiny_code
svgtiny_parse_circle(xmlNode
*circle
,
487 struct svgtiny_parse_state state
)
489 float x
= 0, y
= 0, r
= -1;
491 for (xmlAttr
*attr
= circle
->properties
; attr
; attr
= attr
->next
) {
492 const char *name
= (const char *) attr
->name
;
493 const char *content
= (const char *) attr
->children
->content
;
494 if (strcmp(name
, "cx") == 0)
495 x
= svgtiny_parse_length(content
,
496 state
.viewport_width
, state
);
497 else if (strcmp(name
, "cy") == 0)
498 y
= svgtiny_parse_length(content
,
499 state
.viewport_height
, state
);
500 else if (strcmp(name
, "r") == 0)
501 r
= svgtiny_parse_length(content
,
502 state
.viewport_width
, state
);
504 svgtiny_parse_paint_attributes(circle
, &state
);
505 svgtiny_parse_transform_attributes(circle
, &state
);
508 state
.diagram
->error_line
= circle
->line
;
509 state
.diagram
->error_message
= "circle: r missing or negative";
510 return svgtiny_SVG_ERROR
;
515 float *p
= malloc(32 * sizeof p
[0]);
517 return svgtiny_OUT_OF_MEMORY
;
519 p
[0] = svgtiny_PATH_MOVE
;
522 p
[3] = svgtiny_PATH_BEZIER
;
524 p
[5] = y
+ r
* KAPPA
;
525 p
[6] = x
+ r
* KAPPA
;
529 p
[10] = svgtiny_PATH_BEZIER
;
530 p
[11] = x
- r
* KAPPA
;
533 p
[14] = y
+ r
* KAPPA
;
536 p
[17] = svgtiny_PATH_BEZIER
;
538 p
[19] = y
- r
* KAPPA
;
539 p
[20] = x
- r
* KAPPA
;
543 p
[24] = svgtiny_PATH_BEZIER
;
544 p
[25] = x
+ r
* KAPPA
;
547 p
[28] = y
- r
* KAPPA
;
550 p
[31] = svgtiny_PATH_CLOSE
;
552 return svgtiny_add_path(p
, 32, &state
);
557 * Parse an <ellipse> element node.
560 svgtiny_code
svgtiny_parse_ellipse(xmlNode
*ellipse
,
561 struct svgtiny_parse_state state
)
563 float x
= 0, y
= 0, rx
= -1, ry
= -1;
565 for (xmlAttr
*attr
= ellipse
->properties
; attr
; attr
= attr
->next
) {
566 const char *name
= (const char *) attr
->name
;
567 const char *content
= (const char *) attr
->children
->content
;
568 if (strcmp(name
, "cx") == 0)
569 x
= svgtiny_parse_length(content
,
570 state
.viewport_width
, state
);
571 else if (strcmp(name
, "cy") == 0)
572 y
= svgtiny_parse_length(content
,
573 state
.viewport_height
, state
);
574 else if (strcmp(name
, "rx") == 0)
575 rx
= svgtiny_parse_length(content
,
576 state
.viewport_width
, state
);
577 else if (strcmp(name
, "ry") == 0)
578 ry
= svgtiny_parse_length(content
,
579 state
.viewport_width
, state
);
581 svgtiny_parse_paint_attributes(ellipse
, &state
);
582 svgtiny_parse_transform_attributes(ellipse
, &state
);
584 if (rx
< 0 || ry
< 0) {
585 state
.diagram
->error_line
= ellipse
->line
;
586 state
.diagram
->error_message
= "ellipse: rx or ry missing "
588 return svgtiny_SVG_ERROR
;
590 if (rx
== 0 || ry
== 0)
593 float *p
= malloc(32 * sizeof p
[0]);
595 return svgtiny_OUT_OF_MEMORY
;
597 p
[0] = svgtiny_PATH_MOVE
;
600 p
[3] = svgtiny_PATH_BEZIER
;
602 p
[5] = y
+ ry
* KAPPA
;
603 p
[6] = x
+ rx
* KAPPA
;
607 p
[10] = svgtiny_PATH_BEZIER
;
608 p
[11] = x
- rx
* KAPPA
;
611 p
[14] = y
+ ry
* KAPPA
;
614 p
[17] = svgtiny_PATH_BEZIER
;
616 p
[19] = y
- ry
* KAPPA
;
617 p
[20] = x
- rx
* KAPPA
;
621 p
[24] = svgtiny_PATH_BEZIER
;
622 p
[25] = x
+ rx
* KAPPA
;
625 p
[28] = y
- ry
* KAPPA
;
628 p
[31] = svgtiny_PATH_CLOSE
;
630 return svgtiny_add_path(p
, 32, &state
);
635 * Parse a <line> element node.
638 svgtiny_code
svgtiny_parse_line(xmlNode
*line
,
639 struct svgtiny_parse_state state
)
641 float x1
= 0, y1
= 0, x2
= 0, y2
= 0;
643 for (xmlAttr
*attr
= line
->properties
; attr
; attr
= attr
->next
) {
644 const char *name
= (const char *) attr
->name
;
645 const char *content
= (const char *) attr
->children
->content
;
646 if (strcmp(name
, "x1") == 0)
647 x1
= svgtiny_parse_length(content
,
648 state
.viewport_width
, state
);
649 else if (strcmp(name
, "y1") == 0)
650 y1
= svgtiny_parse_length(content
,
651 state
.viewport_height
, state
);
652 else if (strcmp(name
, "x2") == 0)
653 x2
= svgtiny_parse_length(content
,
654 state
.viewport_width
, state
);
655 else if (strcmp(name
, "y2") == 0)
656 y2
= svgtiny_parse_length(content
,
657 state
.viewport_height
, state
);
659 svgtiny_parse_paint_attributes(line
, &state
);
660 svgtiny_parse_transform_attributes(line
, &state
);
662 float *p
= malloc(7 * sizeof p
[0]);
664 return svgtiny_OUT_OF_MEMORY
;
666 p
[0] = svgtiny_PATH_MOVE
;
669 p
[3] = svgtiny_PATH_LINE
;
672 p
[6] = svgtiny_PATH_CLOSE
;
674 return svgtiny_add_path(p
, 7, &state
);
679 * Parse a <polyline> or <polygon> element node.
681 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
682 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
685 svgtiny_code
svgtiny_parse_poly(xmlNode
*poly
,
686 struct svgtiny_parse_state state
, bool polygon
)
690 svgtiny_parse_paint_attributes(poly
, &state
);
691 svgtiny_parse_transform_attributes(poly
, &state
);
693 /* read points attribute */
694 s
= points
= (char *) xmlGetProp(poly
, (const xmlChar
*) "points");
696 state
.diagram
->error_line
= poly
->line
;
697 state
.diagram
->error_message
=
698 "polyline/polygon: missing points attribute";
699 return svgtiny_SVG_ERROR
;
702 /* allocate space for path: it will never have more elements than s */
703 float *p
= malloc(sizeof p
[0] * strlen(s
));
706 return svgtiny_OUT_OF_MEMORY
;
709 /* parse s and build path */
710 for (unsigned int i
= 0; s
[i
]; i
++)
718 if (sscanf(s
, "%f %f %n", &x
, &y
, &n
) == 2) {
720 p
[i
++] = svgtiny_PATH_MOVE
;
722 p
[i
++] = svgtiny_PATH_LINE
;
731 p
[i
++] = svgtiny_PATH_CLOSE
;
735 return svgtiny_add_path(p
, i
, &state
);
740 * Parse a <text> or <tspan> element node.
743 svgtiny_code
svgtiny_parse_text(xmlNode
*text
,
744 struct svgtiny_parse_state state
)
746 float x
, y
, width
, height
;
748 svgtiny_parse_position_attributes(text
, state
,
749 &x
, &y
, &width
, &height
);
750 svgtiny_parse_font_attributes(text
, &state
);
751 svgtiny_parse_transform_attributes(text
, &state
);
753 float px
= state
.ctm
.a
* x
+ state
.ctm
.c
* y
+ state
.ctm
.e
;
754 float py
= state
.ctm
.b
* x
+ state
.ctm
.d
* y
+ state
.ctm
.f
;
755 /* state.ctm.e = px - state.origin_x; */
756 /* state.ctm.f = py - state.origin_y; */
758 /*struct css_style style = state.style;
759 style.font_size.value.length.value *= state.ctm.a;*/
761 for (xmlNode
*child
= text
->children
; child
; child
= child
->next
) {
762 svgtiny_code code
= svgtiny_OK
;
764 if (child
->type
== XML_TEXT_NODE
) {
765 struct svgtiny_shape
*shape
= svgtiny_add_shape(&state
);
767 return svgtiny_OUT_OF_MEMORY
;
768 shape
->text
= strdup((const char *) child
->content
);
771 state
.diagram
->shape_count
++;
773 } else if (child
->type
== XML_ELEMENT_NODE
&&
774 strcmp((const char *) child
->name
,
776 code
= svgtiny_parse_text(child
, state
);
779 if (!code
!= svgtiny_OK
)
788 * Parse x, y, width, and height attributes, if present.
791 void svgtiny_parse_position_attributes(const xmlNode
*node
,
792 const struct svgtiny_parse_state state
,
793 float *x
, float *y
, float *width
, float *height
)
797 *width
= state
.viewport_width
;
798 *height
= state
.viewport_height
;
800 for (xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
801 const char *name
= (const char *) attr
->name
;
802 const char *content
= (const char *) attr
->children
->content
;
803 if (strcmp(name
, "x") == 0)
804 *x
= svgtiny_parse_length(content
,
805 state
.viewport_width
, state
);
806 else if (strcmp(name
, "y") == 0)
807 *y
= svgtiny_parse_length(content
,
808 state
.viewport_height
, state
);
809 else if (strcmp(name
, "width") == 0)
810 *width
= svgtiny_parse_length(content
,
811 state
.viewport_width
, state
);
812 else if (strcmp(name
, "height") == 0)
813 *height
= svgtiny_parse_length(content
,
814 state
.viewport_height
, state
);
820 * Parse a length as a number of pixels.
823 float svgtiny_parse_length(const char *s
, int viewport_size
,
824 const struct svgtiny_parse_state state
)
826 int num_length
= strspn(s
, "0123456789+-.");
827 const char *unit
= s
+ num_length
;
828 float n
= atof((const char *) s
);
829 float font_size
= 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
835 } else if (unit
[0] == '%') {
836 return n
/ 100.0 * viewport_size
;
837 } else if (unit
[0] == 'e' && unit
[1] == 'm') {
838 return n
* font_size
;
839 } else if (unit
[0] == 'e' && unit
[1] == 'x') {
840 return n
/ 2.0 * font_size
;
841 } else if (unit
[0] == 'p' && unit
[1] == 'x') {
843 } else if (unit
[0] == 'p' && unit
[1] == 't') {
845 } else if (unit
[0] == 'p' && unit
[1] == 'c') {
847 } else if (unit
[0] == 'm' && unit
[1] == 'm') {
849 } else if (unit
[0] == 'c' && unit
[1] == 'm') {
851 } else if (unit
[0] == 'i' && unit
[1] == 'n') {
860 * Parse paint attributes, if present.
863 void svgtiny_parse_paint_attributes(const xmlNode
*node
,
864 struct svgtiny_parse_state
*state
)
866 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
867 const char *name
= (const char *) attr
->name
;
868 const char *content
= (const char *) attr
->children
->content
;
869 if (strcmp(name
, "fill") == 0)
870 svgtiny_parse_color(content
, &state
->fill
, state
);
871 else if (strcmp(name
, "stroke") == 0)
872 svgtiny_parse_color(content
, &state
->stroke
, state
);
873 else if (strcmp(name
, "stroke-width") == 0)
874 state
->stroke_width
= svgtiny_parse_length(content
,
875 state
->viewport_width
, *state
);
876 else if (strcmp(name
, "style") == 0) {
877 const char *style
= (const char *)
878 attr
->children
->content
;
881 if ((s
= strstr(style
, "fill:"))) {
885 value
= strndup(s
, strcspn(s
, "; "));
886 svgtiny_parse_color(value
, &state
->fill
, state
);
889 if ((s
= strstr(style
, "stroke:"))) {
893 value
= strndup(s
, strcspn(s
, "; "));
894 svgtiny_parse_color(value
, &state
->stroke
, state
);
897 if ((s
= strstr(style
, "stroke-width:"))) {
901 state
->stroke_width
= svgtiny_parse_length(s
,
902 state
->viewport_width
, *state
);
913 void svgtiny_parse_color(const char *s
, svgtiny_colour
*c
,
914 struct svgtiny_parse_state
*state
)
916 unsigned int r
, g
, b
;
918 size_t len
= strlen(s
);
919 char *id
= 0, *rparen
;
921 if (len
== 4 && s
[0] == '#') {
922 if (sscanf(s
+ 1, "%1x%1x%1x", &r
, &g
, &b
) == 3)
923 *c
= svgtiny_RGB(r
| r
<< 4, g
| g
<< 4, b
| b
<< 4);
925 } else if (len
== 7 && s
[0] == '#') {
926 if (sscanf(s
+ 1, "%2x%2x%2x", &r
, &g
, &b
) == 3)
927 *c
= svgtiny_RGB(r
, g
, b
);
929 } else if (10 <= len
&& s
[0] == 'r' && s
[1] == 'g' && s
[2] == 'b' &&
930 s
[3] == '(' && s
[len
- 1] == ')') {
931 if (sscanf(s
+ 4, "%u,%u,%u", &r
, &g
, &b
) == 3)
932 *c
= svgtiny_RGB(r
, g
, b
);
933 else if (sscanf(s
+ 4, "%f%%,%f%%,%f%%", &rf
, &gf
, &bf
) == 3) {
937 *c
= svgtiny_RGB(r
, g
, b
);
940 } else if (len
== 4 && strcmp(s
, "none") == 0) {
941 *c
= svgtiny_TRANSPARENT
;
943 } else if (5 < len
&& s
[0] == 'u' && s
[1] == 'r' && s
[2] == 'l' &&
949 rparen
= strchr(id
, ')');
952 svgtiny_find_gradient(id
, state
);
954 fprintf(stderr
, "linear_gradient_stop_count %i\n",
955 state
->linear_gradient_stop_count
);
956 if (state
->linear_gradient_stop_count
== 0)
957 *c
= svgtiny_TRANSPARENT
;
958 else if (state
->linear_gradient_stop_count
== 1)
959 *c
= state
->gradient_stop
[0].color
;
961 *c
= svgtiny_LINEAR_GRADIENT
;
965 const struct svgtiny_named_color
*named_color
;
966 named_color
= svgtiny_color_lookup(s
, strlen(s
));
968 *c
= named_color
->color
;
974 * Parse font attributes, if present.
977 void svgtiny_parse_font_attributes(const xmlNode
*node
,
978 struct svgtiny_parse_state
*state
)
982 for (const xmlAttr
*attr
= node
->properties
; attr
; attr
= attr
->next
) {
983 if (strcmp((const char *) attr
->name
, "font-size") == 0) {
984 /*if (css_parse_length(
985 (const char *) attr->children->content,
986 &state->style.font_size.value.length,
988 state->style.font_size.size =
989 CSS_FONT_SIZE_LENGTH;
997 * Parse transform attributes, if present.
999 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1002 void svgtiny_parse_transform_attributes(xmlNode
*node
,
1003 struct svgtiny_parse_state
*state
)
1007 /* parse transform */
1008 transform
= (char *) xmlGetProp(node
, (const xmlChar
*) "transform");
1010 svgtiny_parse_transform(transform
, &state
->ctm
.a
, &state
->ctm
.b
,
1011 &state
->ctm
.c
, &state
->ctm
.d
,
1012 &state
->ctm
.e
, &state
->ctm
.f
);
1019 * Parse a transform string.
1022 void svgtiny_parse_transform(char *s
, float *ma
, float *mb
,
1023 float *mc
, float *md
, float *me
, float *mf
)
1025 float a
, b
, c
, d
, e
, f
;
1026 float za
, zb
, zc
, zd
, ze
, zf
;
1030 for (unsigned int i
= 0; s
[i
]; i
++)
1038 if (sscanf(s
, "matrix (%f %f %f %f %f %f) %n",
1039 &a
, &b
, &c
, &d
, &e
, &f
, &n
) == 6)
1041 else if (sscanf(s
, "translate (%f %f) %n",
1044 else if (sscanf(s
, "translate (%f) %n",
1047 else if (sscanf(s
, "scale (%f %f) %n",
1050 else if (sscanf(s
, "scale (%f) %n",
1053 else if (sscanf(s
, "rotate (%f %f %f) %n",
1054 &angle
, &x
, &y
, &n
) == 3) {
1055 angle
= angle
/ 180 * M_PI
;
1060 e
= -x
* cos(angle
) + y
* sin(angle
) + x
;
1061 f
= -x
* sin(angle
) - y
* cos(angle
) + y
;
1062 } else if (sscanf(s
, "rotate (%f) %n",
1064 angle
= angle
/ 180 * M_PI
;
1069 } else if (sscanf(s
, "skewX (%f) %n",
1071 angle
= angle
/ 180 * M_PI
;
1073 } else if (sscanf(s
, "skewY (%f) %n",
1075 angle
= angle
/ 180 * M_PI
;
1079 za
= *ma
* a
+ *mc
* b
;
1080 zb
= *mb
* a
+ *md
* b
;
1081 zc
= *ma
* c
+ *mc
* d
;
1082 zd
= *mb
* c
+ *md
* d
;
1083 ze
= *ma
* e
+ *mc
* f
+ *me
;
1084 zf
= *mb
* e
+ *md
* f
+ *mf
;
1097 * Add a path to the svgtiny_diagram.
1100 svgtiny_code
svgtiny_add_path(float *p
, unsigned int n
,
1101 struct svgtiny_parse_state
*state
)
1103 if (state
->fill
== svgtiny_LINEAR_GRADIENT
)
1104 return svgtiny_add_path_linear_gradient(p
, n
, state
);
1106 svgtiny_transform_path(p
, n
, state
);
1108 struct svgtiny_shape
*shape
= svgtiny_add_shape(state
);
1111 return svgtiny_OUT_OF_MEMORY
;
1114 shape
->path_length
= n
;
1115 state
->diagram
->shape_count
++;
1122 * Add a svgtiny_shape to the svgtiny_diagram.
1125 struct svgtiny_shape
*svgtiny_add_shape(struct svgtiny_parse_state
*state
)
1127 struct svgtiny_shape
*shape
= realloc(state
->diagram
->shape
,
1128 (state
->diagram
->shape_count
+ 1) *
1129 sizeof (state
->diagram
->shape
[0]));
1132 state
->diagram
->shape
= shape
;
1134 shape
+= state
->diagram
->shape_count
;
1136 shape
->path_length
= 0;
1138 shape
->fill
= state
->fill
;
1139 shape
->stroke
= state
->stroke
;
1140 shape
->stroke_width
= lroundf((float) state
->stroke_width
*
1141 (state
->ctm
.a
+ state
->ctm
.d
) / 2.0);
1142 if (0 < state
->stroke_width
&& shape
->stroke_width
== 0)
1143 shape
->stroke_width
= 1;
1150 * Apply the current transformation matrix to a path.
1153 void svgtiny_transform_path(float *p
, unsigned int n
,
1154 struct svgtiny_parse_state
*state
)
1156 for (unsigned int j
= 0; j
!= n
; ) {
1157 unsigned int points
= 0;
1158 switch ((int) p
[j
]) {
1159 case svgtiny_PATH_MOVE
:
1160 case svgtiny_PATH_LINE
:
1163 case svgtiny_PATH_CLOSE
:
1166 case svgtiny_PATH_BEZIER
:
1173 for (unsigned int k
= 0; k
!= points
; k
++) {
1174 float x0
= p
[j
], y0
= p
[j
+ 1];
1175 float x
= state
->ctm
.a
* x0
+ state
->ctm
.c
* y0
+
1177 float y
= state
->ctm
.b
* x0
+ state
->ctm
.d
* y0
+
1188 * Free all memory used by a diagram.
1191 void svgtiny_free(struct svgtiny_diagram
*svg
)
1195 for (unsigned int i
= 0; i
!= svg
->shape_count
; i
++) {
1196 free(svg
->shape
[i
].path
);
1197 free(svg
->shape
[i
].text
);