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