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