]> gitweb.michael.orlitzky.com - libsvgtiny.git/blob - src/svgtiny.c
Fix leak of gradient dom strings.
[libsvgtiny.git] / src / svgtiny.c
1 /*
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>
6 * Copyright 2012 Daniel Silverstone <dsilvers@netsurf-browser.org>
7 */
8
9 #include <assert.h>
10 #include <math.h>
11 #include <setjmp.h>
12 #include <stdbool.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 #include <dom/dom.h>
18 #include <dom/bindings/xml/xmlparser.h>
19
20 #include "svgtiny.h"
21 #include "svgtiny_internal.h"
22
23 #ifndef M_PI
24 #define M_PI 3.14159265358979323846
25 #endif
26
27 #define KAPPA 0.5522847498
28
29 static svgtiny_code svgtiny_parse_svg(dom_element *svg,
30 struct svgtiny_parse_state state);
31 static svgtiny_code svgtiny_parse_path(dom_element *path,
32 struct svgtiny_parse_state state);
33 static svgtiny_code svgtiny_parse_rect(dom_element *rect,
34 struct svgtiny_parse_state state);
35 static svgtiny_code svgtiny_parse_circle(dom_element *circle,
36 struct svgtiny_parse_state state);
37 static svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse,
38 struct svgtiny_parse_state state);
39 static svgtiny_code svgtiny_parse_line(dom_element *line,
40 struct svgtiny_parse_state state);
41 static svgtiny_code svgtiny_parse_poly(dom_element *poly,
42 struct svgtiny_parse_state state, bool polygon);
43 static svgtiny_code svgtiny_parse_text(dom_element *text,
44 struct svgtiny_parse_state state);
45 static void svgtiny_parse_position_attributes(dom_element *node,
46 const struct svgtiny_parse_state state,
47 float *x, float *y, float *width, float *height);
48 static void svgtiny_parse_paint_attributes(dom_element *node,
49 struct svgtiny_parse_state *state);
50 static void svgtiny_parse_font_attributes(dom_element *node,
51 struct svgtiny_parse_state *state);
52 static void svgtiny_parse_transform_attributes(dom_element *node,
53 struct svgtiny_parse_state *state);
54 static svgtiny_code svgtiny_add_path(float *p, unsigned int n,
55 struct svgtiny_parse_state *state);
56 static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
57 struct svgtiny_parse_state *state);
58
59 /**
60 * Set the local externally-stored parts of a parse state.
61 * Call this in functions that made a new state on the stack.
62 * Doesn't make own copy of global state, such as the interned string list.
63 */
64 static void svgtiny_setup_state_local(struct svgtiny_parse_state *state)
65 {
66 if (state->gradient_x1 != NULL) {
67 dom_string_ref(state->gradient_x1);
68 }
69 if (state->gradient_y1 != NULL) {
70 dom_string_ref(state->gradient_y1);
71 }
72 if (state->gradient_x2 != NULL) {
73 dom_string_ref(state->gradient_x2);
74 }
75 if (state->gradient_y2 != NULL) {
76 dom_string_ref(state->gradient_y2);
77 }
78 }
79
80 /**
81 * Cleanup the local externally-stored parts of a parse state.
82 * Call this in functions that made a new state on the stack.
83 * Doesn't cleanup global state, such as the interned string list.
84 */
85 static void svgtiny_cleanup_state_local(struct svgtiny_parse_state *state)
86 {
87 if (state->gradient_x1 != NULL) {
88 dom_string_unref(state->gradient_x1);
89 state->gradient_x1 = NULL;
90 }
91 if (state->gradient_y1 != NULL) {
92 dom_string_unref(state->gradient_y1);
93 state->gradient_y1 = NULL;
94 }
95 if (state->gradient_x2 != NULL) {
96 dom_string_unref(state->gradient_x2);
97 state->gradient_x2 = NULL;
98 }
99 if (state->gradient_y2 != NULL) {
100 dom_string_unref(state->gradient_y2);
101 state->gradient_y2 = NULL;
102 }
103 }
104
105
106 /**
107 * Create a new svgtiny_diagram structure.
108 */
109
110 struct svgtiny_diagram *svgtiny_create(void)
111 {
112 struct svgtiny_diagram *diagram;
113
114 diagram = calloc(sizeof(*diagram), 1);
115 if (!diagram)
116 return 0;
117
118 return diagram;
119 free(diagram);
120 return NULL;
121 }
122
123 static void ignore_msg(uint32_t severity, void *ctx, const char *msg, ...)
124 {
125 UNUSED(severity);
126 UNUSED(ctx);
127 UNUSED(msg);
128 }
129
130 /**
131 * Parse a block of memory into a svgtiny_diagram.
132 */
133
134 svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
135 const char *buffer, size_t size, const char *url,
136 int viewport_width, int viewport_height)
137 {
138 dom_document *document;
139 dom_exception exc;
140 dom_xml_parser *parser;
141 dom_xml_error err;
142 dom_element *svg;
143 dom_string *svg_name;
144 lwc_string *svg_name_lwc;
145 struct svgtiny_parse_state state;
146 float x, y, width, height;
147 svgtiny_code code;
148
149 assert(diagram);
150 assert(buffer);
151 assert(url);
152
153 UNUSED(url);
154
155 state.gradient_x1 = NULL;
156 state.gradient_y1 = NULL;
157 state.gradient_x2 = NULL;
158 state.gradient_y2 = NULL;
159
160 parser = dom_xml_parser_create(NULL, NULL,
161 ignore_msg, NULL, &document);
162
163 if (parser == NULL)
164 return svgtiny_LIBDOM_ERROR;
165
166 err = dom_xml_parser_parse_chunk(parser, (uint8_t *)buffer, size);
167 if (err != DOM_XML_OK) {
168 dom_node_unref(document);
169 dom_xml_parser_destroy(parser);
170 return svgtiny_LIBDOM_ERROR;
171 }
172
173 err = dom_xml_parser_completed(parser);
174 if (err != DOM_XML_OK) {
175 dom_node_unref(document);
176 dom_xml_parser_destroy(parser);
177 return svgtiny_LIBDOM_ERROR;
178 }
179
180 /* We're done parsing, drop the parser.
181 * We now own the document entirely.
182 */
183 dom_xml_parser_destroy(parser);
184
185 /* find root <svg> element */
186 exc = dom_document_get_document_element(document, &svg);
187 if (exc != DOM_NO_ERR) {
188 dom_node_unref(document);
189 return svgtiny_LIBDOM_ERROR;
190 }
191 exc = dom_node_get_node_name(svg, &svg_name);
192 if (exc != DOM_NO_ERR) {
193 dom_node_unref(svg);
194 dom_node_unref(document);
195 return svgtiny_LIBDOM_ERROR;
196 }
197 if (lwc_intern_string("svg", 3 /* SLEN("svg") */,
198 &svg_name_lwc) != lwc_error_ok) {
199 dom_string_unref(svg_name);
200 dom_node_unref(svg);
201 dom_node_unref(document);
202 return svgtiny_LIBDOM_ERROR;
203 }
204 if (!dom_string_caseless_lwc_isequal(svg_name, svg_name_lwc)) {
205 lwc_string_unref(svg_name_lwc);
206 dom_string_unref(svg_name);
207 dom_node_unref(svg);
208 dom_node_unref(document);
209 return svgtiny_NOT_SVG;
210 }
211
212 lwc_string_unref(svg_name_lwc);
213 dom_string_unref(svg_name);
214
215 /* get graphic dimensions */
216 memset(&state, 0, sizeof(state));
217 state.diagram = diagram;
218 state.document = document;
219 state.viewport_width = viewport_width;
220 state.viewport_height = viewport_height;
221
222 #define SVGTINY_STRING_ACTION2(s,n) \
223 if (dom_string_create_interned((const uint8_t *) #n, \
224 strlen(#n), &state.interned_##s) \
225 != DOM_NO_ERR) { \
226 code = svgtiny_LIBDOM_ERROR; \
227 goto cleanup; \
228 }
229 #include "svgtiny_strings.h"
230 #undef SVGTINY_STRING_ACTION2
231
232 svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
233 diagram->width = width;
234 diagram->height = height;
235
236 /* set up parsing state */
237 state.viewport_width = width;
238 state.viewport_height = height;
239 state.ctm.a = 1; /*(float) viewport_width / (float) width;*/
240 state.ctm.b = 0;
241 state.ctm.c = 0;
242 state.ctm.d = 1; /*(float) viewport_height / (float) height;*/
243 state.ctm.e = 0; /*x;*/
244 state.ctm.f = 0; /*y;*/
245 /*state.style = css_base_style;
246 state.style.font_size.value.length.value = option_font_size * 0.1;*/
247 state.fill = 0x000000;
248 state.stroke = svgtiny_TRANSPARENT;
249 state.stroke_width = 1;
250 state.linear_gradient_stop_count = 0;
251
252 /* parse tree */
253 code = svgtiny_parse_svg(svg, state);
254
255 dom_node_unref(svg);
256 dom_node_unref(document);
257
258 cleanup:
259 svgtiny_cleanup_state_local(&state);
260 #define SVGTINY_STRING_ACTION2(s,n) \
261 if (state.interned_##s != NULL) \
262 dom_string_unref(state.interned_##s);
263 #include "svgtiny_strings.h"
264 #undef SVGTINY_STRING_ACTION2
265 return code;
266 }
267
268
269 /**
270 * Parse a <svg> or <g> element node.
271 */
272
273 svgtiny_code svgtiny_parse_svg(dom_element *svg,
274 struct svgtiny_parse_state state)
275 {
276 float x, y, width, height;
277 dom_string *view_box;
278 dom_element *child;
279 dom_exception exc;
280
281 svgtiny_setup_state_local(&state);
282
283 svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
284 svgtiny_parse_paint_attributes(svg, &state);
285 svgtiny_parse_font_attributes(svg, &state);
286
287 exc = dom_element_get_attribute(svg, state.interned_viewBox,
288 &view_box);
289 if (exc != DOM_NO_ERR) {
290 svgtiny_cleanup_state_local(&state);
291 return svgtiny_LIBDOM_ERROR;
292 }
293
294 if (view_box) {
295 char *s = strndup(dom_string_data(view_box),
296 dom_string_byte_length(view_box));
297 float min_x, min_y, vwidth, vheight;
298 if (sscanf(s, "%f,%f,%f,%f",
299 &min_x, &min_y, &vwidth, &vheight) == 4 ||
300 sscanf(s, "%f %f %f %f",
301 &min_x, &min_y, &vwidth, &vheight) == 4) {
302 state.ctm.a = (float) state.viewport_width / vwidth;
303 state.ctm.d = (float) state.viewport_height / vheight;
304 state.ctm.e += -min_x * state.ctm.a;
305 state.ctm.f += -min_y * state.ctm.d;
306 }
307 free(s);
308 dom_string_unref(view_box);
309 }
310
311 svgtiny_parse_transform_attributes(svg, &state);
312
313 exc = dom_node_get_first_child(svg, (dom_node **) (void *) &child);
314 if (exc != DOM_NO_ERR) {
315 svgtiny_cleanup_state_local(&state);
316 return svgtiny_LIBDOM_ERROR;
317 }
318 while (child != NULL) {
319 dom_element *next;
320 dom_node_type nodetype;
321 svgtiny_code code = svgtiny_OK;
322
323 exc = dom_node_get_node_type(child, &nodetype);
324 if (exc != DOM_NO_ERR) {
325 dom_node_unref(child);
326 return svgtiny_LIBDOM_ERROR;
327 }
328 if (nodetype == DOM_ELEMENT_NODE) {
329 dom_string *nodename;
330 exc = dom_node_get_node_name(child, &nodename);
331 if (exc != DOM_NO_ERR) {
332 dom_node_unref(child);
333 svgtiny_cleanup_state_local(&state);
334 return svgtiny_LIBDOM_ERROR;
335 }
336 if (dom_string_caseless_isequal(state.interned_svg,
337 nodename))
338 code = svgtiny_parse_svg(child, state);
339 else if (dom_string_caseless_isequal(state.interned_g,
340 nodename))
341 code = svgtiny_parse_svg(child, state);
342 else if (dom_string_caseless_isequal(state.interned_a,
343 nodename))
344 code = svgtiny_parse_svg(child, state);
345 else if (dom_string_caseless_isequal(state.interned_path,
346 nodename))
347 code = svgtiny_parse_path(child, state);
348 else if (dom_string_caseless_isequal(state.interned_rect,
349 nodename))
350 code = svgtiny_parse_rect(child, state);
351 else if (dom_string_caseless_isequal(state.interned_circle,
352 nodename))
353 code = svgtiny_parse_circle(child, state);
354 else if (dom_string_caseless_isequal(state.interned_ellipse,
355 nodename))
356 code = svgtiny_parse_ellipse(child, state);
357 else if (dom_string_caseless_isequal(state.interned_line,
358 nodename))
359 code = svgtiny_parse_line(child, state);
360 else if (dom_string_caseless_isequal(state.interned_polyline,
361 nodename))
362 code = svgtiny_parse_poly(child, state, false);
363 else if (dom_string_caseless_isequal(state.interned_polygon,
364 nodename))
365 code = svgtiny_parse_poly(child, state, true);
366 else if (dom_string_caseless_isequal(state.interned_text,
367 nodename))
368 code = svgtiny_parse_text(child, state);
369 dom_string_unref(nodename);
370 }
371 if (code != svgtiny_OK) {
372 dom_node_unref(child);
373 svgtiny_cleanup_state_local(&state);
374 return code;
375 }
376 exc = dom_node_get_next_sibling(child,
377 (dom_node **) (void *) &next);
378 dom_node_unref(child);
379 if (exc != DOM_NO_ERR) {
380 svgtiny_cleanup_state_local(&state);
381 return svgtiny_LIBDOM_ERROR;
382 }
383 child = next;
384 }
385
386 svgtiny_cleanup_state_local(&state);
387 return svgtiny_OK;
388 }
389
390
391
392 /**
393 * Parse a <path> element node.
394 *
395 * http://www.w3.org/TR/SVG11/paths#PathElement
396 */
397
398 svgtiny_code svgtiny_parse_path(dom_element *path,
399 struct svgtiny_parse_state state)
400 {
401 svgtiny_code err;
402 dom_string *path_d_str;
403 dom_exception exc;
404 char *s, *path_d;
405 float *p;
406 unsigned int i;
407 float last_x = 0, last_y = 0;
408 float last_cubic_x = 0, last_cubic_y = 0;
409 float last_quad_x = 0, last_quad_y = 0;
410
411 svgtiny_setup_state_local(&state);
412
413 svgtiny_parse_paint_attributes(path, &state);
414 svgtiny_parse_transform_attributes(path, &state);
415
416 /* read d attribute */
417 exc = dom_element_get_attribute(path, state.interned_d, &path_d_str);
418 if (exc != DOM_NO_ERR) {
419 state.diagram->error_line = -1; /* path->line; */
420 state.diagram->error_message = "path: error retrieving d attribute";
421 svgtiny_cleanup_state_local(&state);
422 return svgtiny_SVG_ERROR;
423 }
424
425 if (path_d_str == NULL) {
426 state.diagram->error_line = -1; /* path->line; */
427 state.diagram->error_message = "path: missing d attribute";
428 svgtiny_cleanup_state_local(&state);
429 return svgtiny_SVG_ERROR;
430 }
431
432 s = path_d = strndup(dom_string_data(path_d_str),
433 dom_string_byte_length(path_d_str));
434 dom_string_unref(path_d_str);
435 if (s == NULL) {
436 svgtiny_cleanup_state_local(&state);
437 return svgtiny_OUT_OF_MEMORY;
438 }
439 /* allocate space for path: it will never have more elements than d */
440 p = malloc(sizeof p[0] * strlen(s));
441 if (!p) {
442 free(path_d);
443 svgtiny_cleanup_state_local(&state);
444 return svgtiny_OUT_OF_MEMORY;
445 }
446
447 /* parse d and build path */
448 for (i = 0; s[i]; i++)
449 if (s[i] == ',')
450 s[i] = ' ';
451 i = 0;
452 while (*s) {
453 char command[2];
454 int plot_command;
455 float x, y, x1, y1, x2, y2, rx, ry, rotation, large_arc, sweep;
456 int n;
457
458 /* moveto (M, m), lineto (L, l) (2 arguments) */
459 if (sscanf(s, " %1[MmLl] %f %f %n", command, &x, &y, &n) == 3) {
460 /*LOG(("moveto or lineto"));*/
461 if (*command == 'M' || *command == 'm')
462 plot_command = svgtiny_PATH_MOVE;
463 else
464 plot_command = svgtiny_PATH_LINE;
465 do {
466 p[i++] = plot_command;
467 if ('a' <= *command) {
468 x += last_x;
469 y += last_y;
470 }
471 p[i++] = last_cubic_x = last_quad_x = last_x
472 = x;
473 p[i++] = last_cubic_y = last_quad_y = last_y
474 = y;
475 s += n;
476 plot_command = svgtiny_PATH_LINE;
477 } while (sscanf(s, "%f %f %n", &x, &y, &n) == 2);
478
479 /* closepath (Z, z) (no arguments) */
480 } else if (sscanf(s, " %1[Zz] %n", command, &n) == 1) {
481 /*LOG(("closepath"));*/
482 p[i++] = svgtiny_PATH_CLOSE;
483 s += n;
484
485 /* horizontal lineto (H, h) (1 argument) */
486 } else if (sscanf(s, " %1[Hh] %f %n", command, &x, &n) == 2) {
487 /*LOG(("horizontal lineto"));*/
488 do {
489 p[i++] = svgtiny_PATH_LINE;
490 if (*command == 'h')
491 x += last_x;
492 p[i++] = last_cubic_x = last_quad_x = last_x
493 = x;
494 p[i++] = last_cubic_y = last_quad_y = last_y;
495 s += n;
496 } while (sscanf(s, "%f %n", &x, &n) == 1);
497
498 /* vertical lineto (V, v) (1 argument) */
499 } else if (sscanf(s, " %1[Vv] %f %n", command, &y, &n) == 2) {
500 /*LOG(("vertical lineto"));*/
501 do {
502 p[i++] = svgtiny_PATH_LINE;
503 if (*command == 'v')
504 y += last_y;
505 p[i++] = last_cubic_x = last_quad_x = last_x;
506 p[i++] = last_cubic_y = last_quad_y = last_y
507 = y;
508 s += n;
509 } while (sscanf(s, "%f %n", &x, &n) == 1);
510
511 /* curveto (C, c) (6 arguments) */
512 } else if (sscanf(s, " %1[Cc] %f %f %f %f %f %f %n", command,
513 &x1, &y1, &x2, &y2, &x, &y, &n) == 7) {
514 /*LOG(("curveto"));*/
515 do {
516 p[i++] = svgtiny_PATH_BEZIER;
517 if (*command == 'c') {
518 x1 += last_x;
519 y1 += last_y;
520 x2 += last_x;
521 y2 += last_y;
522 x += last_x;
523 y += last_y;
524 }
525 p[i++] = x1;
526 p[i++] = y1;
527 p[i++] = last_cubic_x = x2;
528 p[i++] = last_cubic_y = y2;
529 p[i++] = last_quad_x = last_x = x;
530 p[i++] = last_quad_y = last_y = y;
531 s += n;
532 } while (sscanf(s, "%f %f %f %f %f %f %n",
533 &x1, &y1, &x2, &y2, &x, &y, &n) == 6);
534
535 /* shorthand/smooth curveto (S, s) (4 arguments) */
536 } else if (sscanf(s, " %1[Ss] %f %f %f %f %n", command,
537 &x2, &y2, &x, &y, &n) == 5) {
538 /*LOG(("shorthand/smooth curveto"));*/
539 do {
540 p[i++] = svgtiny_PATH_BEZIER;
541 x1 = last_x + (last_x - last_cubic_x);
542 y1 = last_y + (last_y - last_cubic_y);
543 if (*command == 's') {
544 x2 += last_x;
545 y2 += last_y;
546 x += last_x;
547 y += last_y;
548 }
549 p[i++] = x1;
550 p[i++] = y1;
551 p[i++] = last_cubic_x = x2;
552 p[i++] = last_cubic_y = y2;
553 p[i++] = last_quad_x = last_x = x;
554 p[i++] = last_quad_y = last_y = y;
555 s += n;
556 } while (sscanf(s, "%f %f %f %f %n",
557 &x2, &y2, &x, &y, &n) == 4);
558
559 /* quadratic Bezier curveto (Q, q) (4 arguments) */
560 } else if (sscanf(s, " %1[Qq] %f %f %f %f %n", command,
561 &x1, &y1, &x, &y, &n) == 5) {
562 /*LOG(("quadratic Bezier curveto"));*/
563 do {
564 p[i++] = svgtiny_PATH_BEZIER;
565 last_quad_x = x1;
566 last_quad_y = y1;
567 if (*command == 'q') {
568 x1 += last_x;
569 y1 += last_y;
570 x += last_x;
571 y += last_y;
572 }
573 p[i++] = 1./3 * last_x + 2./3 * x1;
574 p[i++] = 1./3 * last_y + 2./3 * y1;
575 p[i++] = 2./3 * x1 + 1./3 * x;
576 p[i++] = 2./3 * y1 + 1./3 * y;
577 p[i++] = last_cubic_x = last_x = x;
578 p[i++] = last_cubic_y = last_y = y;
579 s += n;
580 } while (sscanf(s, "%f %f %f %f %n",
581 &x1, &y1, &x, &y, &n) == 4);
582
583 /* shorthand/smooth quadratic Bezier curveto (T, t)
584 (2 arguments) */
585 } else if (sscanf(s, " %1[Tt] %f %f %n", command,
586 &x, &y, &n) == 3) {
587 /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
588 do {
589 p[i++] = svgtiny_PATH_BEZIER;
590 x1 = last_x + (last_x - last_quad_x);
591 y1 = last_y + (last_y - last_quad_y);
592 last_quad_x = x1;
593 last_quad_y = y1;
594 if (*command == 't') {
595 x1 += last_x;
596 y1 += last_y;
597 x += last_x;
598 y += last_y;
599 }
600 p[i++] = 1./3 * last_x + 2./3 * x1;
601 p[i++] = 1./3 * last_y + 2./3 * y1;
602 p[i++] = 2./3 * x1 + 1./3 * x;
603 p[i++] = 2./3 * y1 + 1./3 * y;
604 p[i++] = last_cubic_x = last_x = x;
605 p[i++] = last_cubic_y = last_y = y;
606 s += n;
607 } while (sscanf(s, "%f %f %n",
608 &x, &y, &n) == 2);
609
610 /* elliptical arc (A, a) (7 arguments) */
611 } else if (sscanf(s, " %1[Aa] %f %f %f %f %f %f %f %n", command,
612 &rx, &ry, &rotation, &large_arc, &sweep,
613 &x, &y, &n) == 8) {
614 do {
615 p[i++] = svgtiny_PATH_LINE;
616 if (*command == 'a') {
617 x += last_x;
618 y += last_y;
619 }
620 p[i++] = last_cubic_x = last_quad_x = last_x
621 = x;
622 p[i++] = last_cubic_y = last_quad_y = last_y
623 = y;
624 s += n;
625 } while (sscanf(s, "%f %f %f %f %f %f %f %n",
626 &rx, &ry, &rotation, &large_arc, &sweep,
627 &x, &y, &n) == 7);
628
629 } else {
630 fprintf(stderr, "parse failed at \"%s\"\n", s);
631 break;
632 }
633 }
634
635 free(path_d);
636
637 if (i <= 4) {
638 /* no real segments in path */
639 free(p);
640 svgtiny_cleanup_state_local(&state);
641 return svgtiny_OK;
642 }
643
644 err = svgtiny_add_path(p, i, &state);
645
646 svgtiny_cleanup_state_local(&state);
647
648 return err;
649 }
650
651
652 /**
653 * Parse a <rect> element node.
654 *
655 * http://www.w3.org/TR/SVG11/shapes#RectElement
656 */
657
658 svgtiny_code svgtiny_parse_rect(dom_element *rect,
659 struct svgtiny_parse_state state)
660 {
661 svgtiny_code err;
662 float x, y, width, height;
663 float *p;
664
665 svgtiny_setup_state_local(&state);
666
667 svgtiny_parse_position_attributes(rect, state,
668 &x, &y, &width, &height);
669 svgtiny_parse_paint_attributes(rect, &state);
670 svgtiny_parse_transform_attributes(rect, &state);
671
672 p = malloc(13 * sizeof p[0]);
673 if (!p) {
674 svgtiny_cleanup_state_local(&state);
675 return svgtiny_OUT_OF_MEMORY;
676 }
677
678 p[0] = svgtiny_PATH_MOVE;
679 p[1] = x;
680 p[2] = y;
681 p[3] = svgtiny_PATH_LINE;
682 p[4] = x + width;
683 p[5] = y;
684 p[6] = svgtiny_PATH_LINE;
685 p[7] = x + width;
686 p[8] = y + height;
687 p[9] = svgtiny_PATH_LINE;
688 p[10] = x;
689 p[11] = y + height;
690 p[12] = svgtiny_PATH_CLOSE;
691
692 err = svgtiny_add_path(p, 13, &state);
693
694 svgtiny_cleanup_state_local(&state);
695
696 return err;
697 }
698
699
700 /**
701 * Parse a <circle> element node.
702 */
703
704 svgtiny_code svgtiny_parse_circle(dom_element *circle,
705 struct svgtiny_parse_state state)
706 {
707 svgtiny_code err;
708 float x = 0, y = 0, r = -1;
709 float *p;
710 dom_string *attr;
711 dom_exception exc;
712
713 svgtiny_setup_state_local(&state);
714
715 exc = dom_element_get_attribute(circle, state.interned_cx, &attr);
716 if (exc != DOM_NO_ERR) {
717 svgtiny_cleanup_state_local(&state);
718 return svgtiny_LIBDOM_ERROR;
719 }
720 if (attr != NULL) {
721 x = svgtiny_parse_length(attr, state.viewport_width, state);
722 }
723 dom_string_unref(attr);
724
725 exc = dom_element_get_attribute(circle, state.interned_cy, &attr);
726 if (exc != DOM_NO_ERR) {
727 svgtiny_cleanup_state_local(&state);
728 return svgtiny_LIBDOM_ERROR;
729 }
730 if (attr != NULL) {
731 y = svgtiny_parse_length(attr, state.viewport_height, state);
732 }
733 dom_string_unref(attr);
734
735 exc = dom_element_get_attribute(circle, state.interned_r, &attr);
736 if (exc != DOM_NO_ERR) {
737 svgtiny_cleanup_state_local(&state);
738 return svgtiny_LIBDOM_ERROR;
739 }
740 if (attr != NULL) {
741 r = svgtiny_parse_length(attr, state.viewport_width, state);
742 }
743 dom_string_unref(attr);
744
745 svgtiny_parse_paint_attributes(circle, &state);
746 svgtiny_parse_transform_attributes(circle, &state);
747
748 if (r < 0) {
749 state.diagram->error_line = -1; /* circle->line; */
750 state.diagram->error_message = "circle: r missing or negative";
751 svgtiny_cleanup_state_local(&state);
752 return svgtiny_SVG_ERROR;
753 }
754 if (r == 0) {
755 svgtiny_cleanup_state_local(&state);
756 return svgtiny_OK;
757 }
758
759 p = malloc(32 * sizeof p[0]);
760 if (!p) {
761 svgtiny_cleanup_state_local(&state);
762 return svgtiny_OUT_OF_MEMORY;
763 }
764
765 p[0] = svgtiny_PATH_MOVE;
766 p[1] = x + r;
767 p[2] = y;
768 p[3] = svgtiny_PATH_BEZIER;
769 p[4] = x + r;
770 p[5] = y + r * KAPPA;
771 p[6] = x + r * KAPPA;
772 p[7] = y + r;
773 p[8] = x;
774 p[9] = y + r;
775 p[10] = svgtiny_PATH_BEZIER;
776 p[11] = x - r * KAPPA;
777 p[12] = y + r;
778 p[13] = x - r;
779 p[14] = y + r * KAPPA;
780 p[15] = x - r;
781 p[16] = y;
782 p[17] = svgtiny_PATH_BEZIER;
783 p[18] = x - r;
784 p[19] = y - r * KAPPA;
785 p[20] = x - r * KAPPA;
786 p[21] = y - r;
787 p[22] = x;
788 p[23] = y - r;
789 p[24] = svgtiny_PATH_BEZIER;
790 p[25] = x + r * KAPPA;
791 p[26] = y - r;
792 p[27] = x + r;
793 p[28] = y - r * KAPPA;
794 p[29] = x + r;
795 p[30] = y;
796 p[31] = svgtiny_PATH_CLOSE;
797
798 err = svgtiny_add_path(p, 32, &state);
799
800 svgtiny_cleanup_state_local(&state);
801
802 return err;
803 }
804
805
806 /**
807 * Parse an <ellipse> element node.
808 */
809
810 svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse,
811 struct svgtiny_parse_state state)
812 {
813 svgtiny_code err;
814 float x = 0, y = 0, rx = -1, ry = -1;
815 float *p;
816 dom_string *attr;
817 dom_exception exc;
818
819 svgtiny_setup_state_local(&state);
820
821 exc = dom_element_get_attribute(ellipse, state.interned_cx, &attr);
822 if (exc != DOM_NO_ERR) {
823 svgtiny_cleanup_state_local(&state);
824 return svgtiny_LIBDOM_ERROR;
825 }
826 if (attr != NULL) {
827 x = svgtiny_parse_length(attr, state.viewport_width, state);
828 }
829 dom_string_unref(attr);
830
831 exc = dom_element_get_attribute(ellipse, state.interned_cy, &attr);
832 if (exc != DOM_NO_ERR) {
833 svgtiny_cleanup_state_local(&state);
834 return svgtiny_LIBDOM_ERROR;
835 }
836 if (attr != NULL) {
837 y = svgtiny_parse_length(attr, state.viewport_height, state);
838 }
839 dom_string_unref(attr);
840
841 exc = dom_element_get_attribute(ellipse, state.interned_rx, &attr);
842 if (exc != DOM_NO_ERR) {
843 svgtiny_cleanup_state_local(&state);
844 return svgtiny_LIBDOM_ERROR;
845 }
846 if (attr != NULL) {
847 rx = svgtiny_parse_length(attr, state.viewport_width, state);
848 }
849 dom_string_unref(attr);
850
851 exc = dom_element_get_attribute(ellipse, state.interned_ry, &attr);
852 if (exc != DOM_NO_ERR) {
853 svgtiny_cleanup_state_local(&state);
854 return svgtiny_LIBDOM_ERROR;
855 }
856 if (attr != NULL) {
857 ry = svgtiny_parse_length(attr, state.viewport_width, state);
858 }
859 dom_string_unref(attr);
860
861 svgtiny_parse_paint_attributes(ellipse, &state);
862 svgtiny_parse_transform_attributes(ellipse, &state);
863
864 if (rx < 0 || ry < 0) {
865 state.diagram->error_line = -1; /* ellipse->line; */
866 state.diagram->error_message = "ellipse: rx or ry missing "
867 "or negative";
868 svgtiny_cleanup_state_local(&state);
869 return svgtiny_SVG_ERROR;
870 }
871 if (rx == 0 || ry == 0) {
872 svgtiny_cleanup_state_local(&state);
873 return svgtiny_OK;
874 }
875
876 p = malloc(32 * sizeof p[0]);
877 if (!p) {
878 svgtiny_cleanup_state_local(&state);
879 return svgtiny_OUT_OF_MEMORY;
880 }
881
882 p[0] = svgtiny_PATH_MOVE;
883 p[1] = x + rx;
884 p[2] = y;
885 p[3] = svgtiny_PATH_BEZIER;
886 p[4] = x + rx;
887 p[5] = y + ry * KAPPA;
888 p[6] = x + rx * KAPPA;
889 p[7] = y + ry;
890 p[8] = x;
891 p[9] = y + ry;
892 p[10] = svgtiny_PATH_BEZIER;
893 p[11] = x - rx * KAPPA;
894 p[12] = y + ry;
895 p[13] = x - rx;
896 p[14] = y + ry * KAPPA;
897 p[15] = x - rx;
898 p[16] = y;
899 p[17] = svgtiny_PATH_BEZIER;
900 p[18] = x - rx;
901 p[19] = y - ry * KAPPA;
902 p[20] = x - rx * KAPPA;
903 p[21] = y - ry;
904 p[22] = x;
905 p[23] = y - ry;
906 p[24] = svgtiny_PATH_BEZIER;
907 p[25] = x + rx * KAPPA;
908 p[26] = y - ry;
909 p[27] = x + rx;
910 p[28] = y - ry * KAPPA;
911 p[29] = x + rx;
912 p[30] = y;
913 p[31] = svgtiny_PATH_CLOSE;
914
915 err = svgtiny_add_path(p, 32, &state);
916
917 svgtiny_cleanup_state_local(&state);
918
919 return err;
920 }
921
922
923 /**
924 * Parse a <line> element node.
925 */
926
927 svgtiny_code svgtiny_parse_line(dom_element *line,
928 struct svgtiny_parse_state state)
929 {
930 svgtiny_code err;
931 float x1 = 0, y1 = 0, x2 = 0, y2 = 0;
932 float *p;
933 dom_string *attr;
934 dom_exception exc;
935
936 svgtiny_setup_state_local(&state);
937
938 exc = dom_element_get_attribute(line, state.interned_x1, &attr);
939 if (exc != DOM_NO_ERR) {
940 svgtiny_cleanup_state_local(&state);
941 return svgtiny_LIBDOM_ERROR;
942 }
943 if (attr != NULL) {
944 x1 = svgtiny_parse_length(attr, state.viewport_width, state);
945 }
946 dom_string_unref(attr);
947
948 exc = dom_element_get_attribute(line, state.interned_y1, &attr);
949 if (exc != DOM_NO_ERR) {
950 svgtiny_cleanup_state_local(&state);
951 return svgtiny_LIBDOM_ERROR;
952 }
953 if (attr != NULL) {
954 y1 = svgtiny_parse_length(attr, state.viewport_height, state);
955 }
956 dom_string_unref(attr);
957
958 exc = dom_element_get_attribute(line, state.interned_x2, &attr);
959 if (exc != DOM_NO_ERR) {
960 svgtiny_cleanup_state_local(&state);
961 return svgtiny_LIBDOM_ERROR;
962 }
963 if (attr != NULL) {
964 x2 = svgtiny_parse_length(attr, state.viewport_width, state);
965 }
966 dom_string_unref(attr);
967
968 exc = dom_element_get_attribute(line, state.interned_y2, &attr);
969 if (exc != DOM_NO_ERR) {
970 svgtiny_cleanup_state_local(&state);
971 return svgtiny_LIBDOM_ERROR;
972 }
973 if (attr != NULL) {
974 y2 = svgtiny_parse_length(attr, state.viewport_height, state);
975 }
976 dom_string_unref(attr);
977
978 svgtiny_parse_paint_attributes(line, &state);
979 svgtiny_parse_transform_attributes(line, &state);
980
981 p = malloc(7 * sizeof p[0]);
982 if (!p) {
983 svgtiny_cleanup_state_local(&state);
984 return svgtiny_OUT_OF_MEMORY;
985 }
986
987 p[0] = svgtiny_PATH_MOVE;
988 p[1] = x1;
989 p[2] = y1;
990 p[3] = svgtiny_PATH_LINE;
991 p[4] = x2;
992 p[5] = y2;
993 p[6] = svgtiny_PATH_CLOSE;
994
995 err = svgtiny_add_path(p, 7, &state);
996
997 svgtiny_cleanup_state_local(&state);
998
999 return err;
1000 }
1001
1002
1003 /**
1004 * Parse a <polyline> or <polygon> element node.
1005 *
1006 * http://www.w3.org/TR/SVG11/shapes#PolylineElement
1007 * http://www.w3.org/TR/SVG11/shapes#PolygonElement
1008 */
1009
1010 svgtiny_code svgtiny_parse_poly(dom_element *poly,
1011 struct svgtiny_parse_state state, bool polygon)
1012 {
1013 svgtiny_code err;
1014 dom_string *points_str;
1015 dom_exception exc;
1016 char *s, *points;
1017 float *p;
1018 unsigned int i;
1019
1020 svgtiny_setup_state_local(&state);
1021
1022 svgtiny_parse_paint_attributes(poly, &state);
1023 svgtiny_parse_transform_attributes(poly, &state);
1024
1025 exc = dom_element_get_attribute(poly, state.interned_points,
1026 &points_str);
1027 if (exc != DOM_NO_ERR) {
1028 svgtiny_cleanup_state_local(&state);
1029 return svgtiny_LIBDOM_ERROR;
1030 }
1031
1032 if (points_str == NULL) {
1033 state.diagram->error_line = -1; /* poly->line; */
1034 state.diagram->error_message =
1035 "polyline/polygon: missing points attribute";
1036 svgtiny_cleanup_state_local(&state);
1037 return svgtiny_SVG_ERROR;
1038 }
1039
1040 s = points = strndup(dom_string_data(points_str),
1041 dom_string_byte_length(points_str));
1042 dom_string_unref(points_str);
1043 /* read points attribute */
1044 if (s == NULL) {
1045 svgtiny_cleanup_state_local(&state);
1046 return svgtiny_OUT_OF_MEMORY;
1047 }
1048 /* allocate space for path: it will never have more elements than s */
1049 p = malloc(sizeof p[0] * strlen(s));
1050 if (!p) {
1051 free(points);
1052 svgtiny_cleanup_state_local(&state);
1053 return svgtiny_OUT_OF_MEMORY;
1054 }
1055
1056 /* parse s and build path */
1057 for (i = 0; s[i]; i++)
1058 if (s[i] == ',')
1059 s[i] = ' ';
1060 i = 0;
1061 while (*s) {
1062 float x, y;
1063 int n;
1064
1065 if (sscanf(s, "%f %f %n", &x, &y, &n) == 2) {
1066 if (i == 0)
1067 p[i++] = svgtiny_PATH_MOVE;
1068 else
1069 p[i++] = svgtiny_PATH_LINE;
1070 p[i++] = x;
1071 p[i++] = y;
1072 s += n;
1073 } else {
1074 break;
1075 }
1076 }
1077 if (polygon)
1078 p[i++] = svgtiny_PATH_CLOSE;
1079
1080 free(points);
1081
1082 err = svgtiny_add_path(p, i, &state);
1083
1084 svgtiny_cleanup_state_local(&state);
1085
1086 return err;
1087 }
1088
1089
1090 /**
1091 * Parse a <text> or <tspan> element node.
1092 */
1093
1094 svgtiny_code svgtiny_parse_text(dom_element *text,
1095 struct svgtiny_parse_state state)
1096 {
1097 float x, y, width, height;
1098 float px, py;
1099 dom_node *child;
1100 dom_exception exc;
1101
1102 svgtiny_setup_state_local(&state);
1103
1104 svgtiny_parse_position_attributes(text, state,
1105 &x, &y, &width, &height);
1106 svgtiny_parse_font_attributes(text, &state);
1107 svgtiny_parse_transform_attributes(text, &state);
1108
1109 px = state.ctm.a * x + state.ctm.c * y + state.ctm.e;
1110 py = state.ctm.b * x + state.ctm.d * y + state.ctm.f;
1111 /* state.ctm.e = px - state.origin_x; */
1112 /* state.ctm.f = py - state.origin_y; */
1113
1114 /*struct css_style style = state.style;
1115 style.font_size.value.length.value *= state.ctm.a;*/
1116
1117 exc = dom_node_get_first_child(text, &child);
1118 if (exc != DOM_NO_ERR) {
1119 return svgtiny_LIBDOM_ERROR;
1120 svgtiny_cleanup_state_local(&state);
1121 }
1122 while (child != NULL) {
1123 dom_node *next;
1124 dom_node_type nodetype;
1125 svgtiny_code code = svgtiny_OK;
1126
1127 exc = dom_node_get_node_type(child, &nodetype);
1128 if (exc != DOM_NO_ERR) {
1129 dom_node_unref(child);
1130 svgtiny_cleanup_state_local(&state);
1131 return svgtiny_LIBDOM_ERROR;
1132 }
1133 if (nodetype == DOM_ELEMENT_NODE) {
1134 dom_string *nodename;
1135 exc = dom_node_get_node_name(child, &nodename);
1136 if (exc != DOM_NO_ERR) {
1137 dom_node_unref(child);
1138 svgtiny_cleanup_state_local(&state);
1139 return svgtiny_LIBDOM_ERROR;
1140 }
1141 if (dom_string_caseless_isequal(nodename,
1142 state.interned_tspan))
1143 code = svgtiny_parse_text((dom_element *)child,
1144 state);
1145 dom_string_unref(nodename);
1146 } else if (nodetype == DOM_TEXT_NODE) {
1147 struct svgtiny_shape *shape = svgtiny_add_shape(&state);
1148 dom_string *content;
1149 if (shape == NULL) {
1150 dom_node_unref(child);
1151 svgtiny_cleanup_state_local(&state);
1152 return svgtiny_OUT_OF_MEMORY;
1153 }
1154 exc = dom_text_get_whole_text(child, &content);
1155 if (exc != DOM_NO_ERR) {
1156 dom_node_unref(child);
1157 svgtiny_cleanup_state_local(&state);
1158 return svgtiny_LIBDOM_ERROR;
1159 }
1160 if (content != NULL) {
1161 shape->text = strndup(dom_string_data(content),
1162 dom_string_byte_length(content));
1163 dom_string_unref(content);
1164 } else {
1165 shape->text = strdup("");
1166 }
1167 shape->text_x = px;
1168 shape->text_y = py;
1169 state.diagram->shape_count++;
1170 }
1171
1172 if (code != svgtiny_OK) {
1173 dom_node_unref(child);
1174 svgtiny_cleanup_state_local(&state);
1175 return code;
1176 }
1177 exc = dom_node_get_next_sibling(child, &next);
1178 dom_node_unref(child);
1179 if (exc != DOM_NO_ERR) {
1180 svgtiny_cleanup_state_local(&state);
1181 return svgtiny_LIBDOM_ERROR;
1182 }
1183 child = next;
1184 }
1185
1186 svgtiny_cleanup_state_local(&state);
1187
1188 return svgtiny_OK;
1189 }
1190
1191
1192 /**
1193 * Parse x, y, width, and height attributes, if present.
1194 */
1195
1196 void svgtiny_parse_position_attributes(dom_element *node,
1197 const struct svgtiny_parse_state state,
1198 float *x, float *y, float *width, float *height)
1199 {
1200 dom_string *attr;
1201 dom_exception exc;
1202
1203 *x = 0;
1204 *y = 0;
1205 *width = state.viewport_width;
1206 *height = state.viewport_height;
1207
1208 exc = dom_element_get_attribute(node, state.interned_x, &attr);
1209 if (exc == DOM_NO_ERR && attr != NULL) {
1210 *x = svgtiny_parse_length(attr, state.viewport_width, state);
1211 dom_string_unref(attr);
1212 }
1213
1214 exc = dom_element_get_attribute(node, state.interned_y, &attr);
1215 if (exc == DOM_NO_ERR && attr != NULL) {
1216 *y = svgtiny_parse_length(attr, state.viewport_height, state);
1217 dom_string_unref(attr);
1218 }
1219
1220 exc = dom_element_get_attribute(node, state.interned_width, &attr);
1221 if (exc == DOM_NO_ERR && attr != NULL) {
1222 *width = svgtiny_parse_length(attr, state.viewport_width,
1223 state);
1224 dom_string_unref(attr);
1225 }
1226
1227 exc = dom_element_get_attribute(node, state.interned_height, &attr);
1228 if (exc == DOM_NO_ERR && attr != NULL) {
1229 *height = svgtiny_parse_length(attr, state.viewport_height,
1230 state);
1231 dom_string_unref(attr);
1232 }
1233 }
1234
1235
1236 /**
1237 * Parse a length as a number of pixels.
1238 */
1239
1240 static float _svgtiny_parse_length(const char *s, int viewport_size,
1241 const struct svgtiny_parse_state state)
1242 {
1243 int num_length = strspn(s, "0123456789+-.");
1244 const char *unit = s + num_length;
1245 float n = atof((const char *) s);
1246 float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/
1247
1248 UNUSED(state);
1249
1250 if (unit[0] == 0) {
1251 return n;
1252 } else if (unit[0] == '%') {
1253 return n / 100.0 * viewport_size;
1254 } else if (unit[0] == 'e' && unit[1] == 'm') {
1255 return n * font_size;
1256 } else if (unit[0] == 'e' && unit[1] == 'x') {
1257 return n / 2.0 * font_size;
1258 } else if (unit[0] == 'p' && unit[1] == 'x') {
1259 return n;
1260 } else if (unit[0] == 'p' && unit[1] == 't') {
1261 return n * 1.25;
1262 } else if (unit[0] == 'p' && unit[1] == 'c') {
1263 return n * 15.0;
1264 } else if (unit[0] == 'm' && unit[1] == 'm') {
1265 return n * 3.543307;
1266 } else if (unit[0] == 'c' && unit[1] == 'm') {
1267 return n * 35.43307;
1268 } else if (unit[0] == 'i' && unit[1] == 'n') {
1269 return n * 90;
1270 }
1271
1272 return 0;
1273 }
1274
1275 float svgtiny_parse_length(dom_string *s, int viewport_size,
1276 const struct svgtiny_parse_state state)
1277 {
1278 char *ss = strndup(dom_string_data(s), dom_string_byte_length(s));
1279 float ret = _svgtiny_parse_length(ss, viewport_size, state);
1280 free(ss);
1281 return ret;
1282 }
1283
1284 /**
1285 * Parse paint attributes, if present.
1286 */
1287
1288 void svgtiny_parse_paint_attributes(dom_element *node,
1289 struct svgtiny_parse_state *state)
1290 {
1291 dom_string *attr;
1292 dom_exception exc;
1293
1294 exc = dom_element_get_attribute(node, state->interned_fill, &attr);
1295 if (exc == DOM_NO_ERR && attr != NULL) {
1296 svgtiny_parse_color(attr, &state->fill, state);
1297 dom_string_unref(attr);
1298 }
1299
1300 exc = dom_element_get_attribute(node, state->interned_stroke, &attr);
1301 if (exc == DOM_NO_ERR && attr != NULL) {
1302 svgtiny_parse_color(attr, &state->stroke, state);
1303 dom_string_unref(attr);
1304 }
1305
1306 exc = dom_element_get_attribute(node, state->interned_stroke_width, &attr);
1307 if (exc == DOM_NO_ERR && attr != NULL) {
1308 state->stroke_width = svgtiny_parse_length(attr,
1309 state->viewport_width, *state);
1310 dom_string_unref(attr);
1311 }
1312
1313 exc = dom_element_get_attribute(node, state->interned_style, &attr);
1314 if (exc == DOM_NO_ERR && attr != NULL) {
1315 char *style = strndup(dom_string_data(attr),
1316 dom_string_byte_length(attr));
1317 const char *s;
1318 char *value;
1319 if ((s = strstr(style, "fill:"))) {
1320 s += 5;
1321 while (*s == ' ')
1322 s++;
1323 value = strndup(s, strcspn(s, "; "));
1324 _svgtiny_parse_color(value, &state->fill, state);
1325 free(value);
1326 }
1327 if ((s = strstr(style, "stroke:"))) {
1328 s += 7;
1329 while (*s == ' ')
1330 s++;
1331 value = strndup(s, strcspn(s, "; "));
1332 _svgtiny_parse_color(value, &state->stroke, state);
1333 free(value);
1334 }
1335 if ((s = strstr(style, "stroke-width:"))) {
1336 s += 13;
1337 while (*s == ' ')
1338 s++;
1339 value = strndup(s, strcspn(s, "; "));
1340 state->stroke_width = _svgtiny_parse_length(value,
1341 state->viewport_width, *state);
1342 free(value);
1343 }
1344 free(style);
1345 dom_string_unref(attr);
1346 }
1347 }
1348
1349
1350 /**
1351 * Parse a colour.
1352 */
1353
1354 static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
1355 struct svgtiny_parse_state *state)
1356 {
1357 unsigned int r, g, b;
1358 float rf, gf, bf;
1359 size_t len = strlen(s);
1360 char *id = 0, *rparen;
1361
1362 if (len == 4 && s[0] == '#') {
1363 if (sscanf(s + 1, "%1x%1x%1x", &r, &g, &b) == 3)
1364 *c = svgtiny_RGB(r | r << 4, g | g << 4, b | b << 4);
1365
1366 } else if (len == 7 && s[0] == '#') {
1367 if (sscanf(s + 1, "%2x%2x%2x", &r, &g, &b) == 3)
1368 *c = svgtiny_RGB(r, g, b);
1369
1370 } else if (10 <= len && s[0] == 'r' && s[1] == 'g' && s[2] == 'b' &&
1371 s[3] == '(' && s[len - 1] == ')') {
1372 if (sscanf(s + 4, "%u,%u,%u", &r, &g, &b) == 3)
1373 *c = svgtiny_RGB(r, g, b);
1374 else if (sscanf(s + 4, "%f%%,%f%%,%f%%", &rf, &gf, &bf) == 3) {
1375 b = bf * 255 / 100;
1376 g = gf * 255 / 100;
1377 r = rf * 255 / 100;
1378 *c = svgtiny_RGB(r, g, b);
1379 }
1380
1381 } else if (len == 4 && strcmp(s, "none") == 0) {
1382 *c = svgtiny_TRANSPARENT;
1383
1384 } else if (5 < len && s[0] == 'u' && s[1] == 'r' && s[2] == 'l' &&
1385 s[3] == '(') {
1386 if (s[4] == '#') {
1387 id = strdup(s + 5);
1388 if (!id)
1389 return;
1390 rparen = strchr(id, ')');
1391 if (rparen)
1392 *rparen = 0;
1393 svgtiny_find_gradient(id, state);
1394 free(id);
1395 fprintf(stderr, "linear_gradient_stop_count %i\n",
1396 state->linear_gradient_stop_count);
1397 if (state->linear_gradient_stop_count == 0)
1398 *c = svgtiny_TRANSPARENT;
1399 else if (state->linear_gradient_stop_count == 1)
1400 *c = state->gradient_stop[0].color;
1401 else
1402 *c = svgtiny_LINEAR_GRADIENT;
1403 }
1404
1405 } else {
1406 const struct svgtiny_named_color *named_color;
1407 named_color = svgtiny_color_lookup(s, strlen(s));
1408 if (named_color)
1409 *c = named_color->color;
1410 }
1411 }
1412
1413 void svgtiny_parse_color(dom_string *s, svgtiny_colour *c,
1414 struct svgtiny_parse_state *state)
1415 {
1416 char *ss = strndup(dom_string_data(s), dom_string_byte_length(s));
1417 _svgtiny_parse_color(ss, c, state);
1418 free(ss);
1419 }
1420
1421 /**
1422 * Parse font attributes, if present.
1423 */
1424
1425 void svgtiny_parse_font_attributes(dom_element *node,
1426 struct svgtiny_parse_state *state)
1427 {
1428 /* TODO: Implement this, it never used to be */
1429 UNUSED(node);
1430 UNUSED(state);
1431 #ifdef WRITTEN_THIS_PROPERLY
1432 const xmlAttr *attr;
1433
1434 UNUSED(state);
1435
1436 for (attr = node->properties; attr; attr = attr->next) {
1437 if (strcmp((const char *) attr->name, "font-size") == 0) {
1438 /*if (css_parse_length(
1439 (const char *) attr->children->content,
1440 &state->style.font_size.value.length,
1441 true, true)) {
1442 state->style.font_size.size =
1443 CSS_FONT_SIZE_LENGTH;
1444 }*/
1445 }
1446 }
1447 #endif
1448 }
1449
1450
1451 /**
1452 * Parse transform attributes, if present.
1453 *
1454 * http://www.w3.org/TR/SVG11/coords#TransformAttribute
1455 */
1456
1457 void svgtiny_parse_transform_attributes(dom_element *node,
1458 struct svgtiny_parse_state *state)
1459 {
1460 char *transform;
1461 dom_string *attr;
1462 dom_exception exc;
1463
1464 exc = dom_element_get_attribute(node, state->interned_transform,
1465 &attr);
1466 if (exc == DOM_NO_ERR && attr != NULL) {
1467 transform = strndup(dom_string_data(attr),
1468 dom_string_byte_length(attr));
1469 svgtiny_parse_transform(transform, &state->ctm.a, &state->ctm.b,
1470 &state->ctm.c, &state->ctm.d,
1471 &state->ctm.e, &state->ctm.f);
1472 free(transform);
1473 dom_string_unref(attr);
1474 }
1475 }
1476
1477
1478 /**
1479 * Parse a transform string.
1480 */
1481
1482 void svgtiny_parse_transform(char *s, float *ma, float *mb,
1483 float *mc, float *md, float *me, float *mf)
1484 {
1485 float a, b, c, d, e, f;
1486 float za, zb, zc, zd, ze, zf;
1487 float angle, x, y;
1488 int n;
1489 unsigned int i;
1490
1491 for (i = 0; s[i]; i++)
1492 if (s[i] == ',')
1493 s[i] = ' ';
1494
1495 while (*s) {
1496 a = d = 1;
1497 b = c = 0;
1498 e = f = 0;
1499 if (sscanf(s, "matrix (%f %f %f %f %f %f) %n",
1500 &a, &b, &c, &d, &e, &f, &n) == 6)
1501 ;
1502 else if (sscanf(s, "translate (%f %f) %n",
1503 &e, &f, &n) == 2)
1504 ;
1505 else if (sscanf(s, "translate (%f) %n",
1506 &e, &n) == 1)
1507 ;
1508 else if (sscanf(s, "scale (%f %f) %n",
1509 &a, &d, &n) == 2)
1510 ;
1511 else if (sscanf(s, "scale (%f) %n",
1512 &a, &n) == 1)
1513 d = a;
1514 else if (sscanf(s, "rotate (%f %f %f) %n",
1515 &angle, &x, &y, &n) == 3) {
1516 angle = angle / 180 * M_PI;
1517 a = cos(angle);
1518 b = sin(angle);
1519 c = -sin(angle);
1520 d = cos(angle);
1521 e = -x * cos(angle) + y * sin(angle) + x;
1522 f = -x * sin(angle) - y * cos(angle) + y;
1523 } else if (sscanf(s, "rotate (%f) %n",
1524 &angle, &n) == 1) {
1525 angle = angle / 180 * M_PI;
1526 a = cos(angle);
1527 b = sin(angle);
1528 c = -sin(angle);
1529 d = cos(angle);
1530 } else if (sscanf(s, "skewX (%f) %n",
1531 &angle, &n) == 1) {
1532 angle = angle / 180 * M_PI;
1533 c = tan(angle);
1534 } else if (sscanf(s, "skewY (%f) %n",
1535 &angle, &n) == 1) {
1536 angle = angle / 180 * M_PI;
1537 b = tan(angle);
1538 } else
1539 break;
1540 za = *ma * a + *mc * b;
1541 zb = *mb * a + *md * b;
1542 zc = *ma * c + *mc * d;
1543 zd = *mb * c + *md * d;
1544 ze = *ma * e + *mc * f + *me;
1545 zf = *mb * e + *md * f + *mf;
1546 *ma = za;
1547 *mb = zb;
1548 *mc = zc;
1549 *md = zd;
1550 *me = ze;
1551 *mf = zf;
1552 s += n;
1553 }
1554 }
1555
1556
1557 /**
1558 * Add a path to the svgtiny_diagram.
1559 */
1560
1561 svgtiny_code svgtiny_add_path(float *p, unsigned int n,
1562 struct svgtiny_parse_state *state)
1563 {
1564 struct svgtiny_shape *shape;
1565
1566 if (state->fill == svgtiny_LINEAR_GRADIENT)
1567 return svgtiny_add_path_linear_gradient(p, n, state);
1568
1569 svgtiny_transform_path(p, n, state);
1570
1571 shape = svgtiny_add_shape(state);
1572 if (!shape) {
1573 free(p);
1574 return svgtiny_OUT_OF_MEMORY;
1575 }
1576 shape->path = p;
1577 shape->path_length = n;
1578 state->diagram->shape_count++;
1579
1580 return svgtiny_OK;
1581 }
1582
1583
1584 /**
1585 * Add a svgtiny_shape to the svgtiny_diagram.
1586 */
1587
1588 struct svgtiny_shape *svgtiny_add_shape(struct svgtiny_parse_state *state)
1589 {
1590 struct svgtiny_shape *shape = realloc(state->diagram->shape,
1591 (state->diagram->shape_count + 1) *
1592 sizeof (state->diagram->shape[0]));
1593 if (!shape)
1594 return 0;
1595 state->diagram->shape = shape;
1596
1597 shape += state->diagram->shape_count;
1598 shape->path = 0;
1599 shape->path_length = 0;
1600 shape->text = 0;
1601 shape->fill = state->fill;
1602 shape->stroke = state->stroke;
1603 shape->stroke_width = lroundf((float) state->stroke_width *
1604 (state->ctm.a + state->ctm.d) / 2.0);
1605 if (0 < state->stroke_width && shape->stroke_width == 0)
1606 shape->stroke_width = 1;
1607
1608 return shape;
1609 }
1610
1611
1612 /**
1613 * Apply the current transformation matrix to a path.
1614 */
1615
1616 void svgtiny_transform_path(float *p, unsigned int n,
1617 struct svgtiny_parse_state *state)
1618 {
1619 unsigned int j;
1620
1621 for (j = 0; j != n; ) {
1622 unsigned int points = 0;
1623 unsigned int k;
1624 switch ((int) p[j]) {
1625 case svgtiny_PATH_MOVE:
1626 case svgtiny_PATH_LINE:
1627 points = 1;
1628 break;
1629 case svgtiny_PATH_CLOSE:
1630 points = 0;
1631 break;
1632 case svgtiny_PATH_BEZIER:
1633 points = 3;
1634 break;
1635 default:
1636 assert(0);
1637 }
1638 j++;
1639 for (k = 0; k != points; k++) {
1640 float x0 = p[j], y0 = p[j + 1];
1641 float x = state->ctm.a * x0 + state->ctm.c * y0 +
1642 state->ctm.e;
1643 float y = state->ctm.b * x0 + state->ctm.d * y0 +
1644 state->ctm.f;
1645 p[j] = x;
1646 p[j + 1] = y;
1647 j += 2;
1648 }
1649 }
1650 }
1651
1652
1653 /**
1654 * Free all memory used by a diagram.
1655 */
1656
1657 void svgtiny_free(struct svgtiny_diagram *svg)
1658 {
1659 unsigned int i;
1660 assert(svg);
1661
1662 for (i = 0; i != svg->shape_count; i++) {
1663 free(svg->shape[i].path);
1664 free(svg->shape[i].text);
1665 }
1666
1667 free(svg->shape);
1668
1669 free(svg);
1670 }
1671
1672 #ifndef HAVE_STRNDUP
1673 char *svgtiny_strndup(const char *s, size_t n)
1674 {
1675 size_t len;
1676 char *s2;
1677
1678 for (len = 0; len != n && s[len]; len++)
1679 continue;
1680
1681 s2 = malloc(len + 1);
1682 if (s2 == NULL)
1683 return NULL;
1684
1685 memcpy(s2, s, len);
1686 s2[len] = '\0';
1687
1688 return s2;
1689 }
1690 #endif
1691