File: | svgtiny.c |
Warning: | line 954, column 4 Value stored to 'res' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | * Copyright 2024 Vincent Sanders <vince@netsurf-browser.org> |
8 | */ |
9 | |
10 | #include <assert.h> |
11 | #include <math.h> |
12 | #include <setjmp.h> |
13 | #include <stdbool.h> |
14 | #include <stdio.h> |
15 | #include <stdlib.h> |
16 | #include <string.h> |
17 | |
18 | #include <dom/dom.h> |
19 | #include <dom/bindings/xml/xmlparser.h> |
20 | |
21 | #include "svgtiny.h" |
22 | #include "svgtiny_internal.h" |
23 | |
24 | |
25 | /* circles are approximated with four bezier curves |
26 | * |
27 | * The optimal distance to the control points is the constant (4/3)*tan(pi/(2n)) |
28 | * (where n is 4) |
29 | */ |
30 | #define KAPPA0.5522847498 0.5522847498 |
31 | |
32 | /* debug flag which enables printing of libdom parse messages to stderr */ |
33 | #undef PRINT_XML_PARSE_MSG |
34 | |
35 | #if (defined(_GNU_SOURCE) && !defined(__APPLE__) || defined(__amigaos4__) || defined(__HAIKU__) || (defined(_POSIX_C_SOURCE200809L) && ((_POSIX_C_SOURCE200809L - 0) >= 200809L))) |
36 | #define HAVE_STRNDUP |
37 | #else |
38 | #undef HAVE_STRNDUP |
39 | char *svgtiny_strndup(const char *s, size_t n); |
40 | #define strndup svgtiny_strndup |
41 | #endif |
42 | |
43 | static svgtiny_code parse_element(dom_element *element, struct svgtiny_parse_state *state); |
44 | |
45 | #ifndef HAVE_STRNDUP |
46 | char *svgtiny_strndup(const char *s, size_t n) |
47 | { |
48 | size_t len; |
49 | char *s2; |
50 | |
51 | for (len = 0; len != n && s[len]; len++) |
52 | continue; |
53 | |
54 | s2 = malloc(len + 1); |
55 | if (s2 == NULL((void*)0)) |
56 | return NULL((void*)0); |
57 | |
58 | memcpy(s2, s, len); |
59 | s2[len] = '\0'; |
60 | |
61 | return s2; |
62 | } |
63 | #endif |
64 | |
65 | |
66 | /** |
67 | * Parse x, y, width, and height attributes, if present. |
68 | */ |
69 | static void |
70 | svgtiny_parse_position_attributes(dom_element *node, |
71 | struct svgtiny_parse_state state, |
72 | float *x, float *y, |
73 | float *width, float *height) |
74 | { |
75 | struct svgtiny_parse_internal_operation styles[] = { |
76 | { |
77 | /* x */ |
78 | state.interned_x, |
79 | SVGTIOP_LENGTH, |
80 | &state.viewport_width, |
81 | x |
82 | },{ |
83 | /* y */ |
84 | state.interned_y, |
85 | SVGTIOP_LENGTH, |
86 | &state.viewport_height, |
87 | y |
88 | },{ |
89 | /* width */ |
90 | state.interned_width, |
91 | SVGTIOP_LENGTH, |
92 | &state.viewport_width, |
93 | width |
94 | },{ |
95 | /* height */ |
96 | state.interned_height, |
97 | SVGTIOP_LENGTH, |
98 | &state.viewport_height, |
99 | height |
100 | },{ |
101 | NULL((void*)0), SVGTIOP_NONE, NULL((void*)0), NULL((void*)0) |
102 | }, |
103 | }; |
104 | |
105 | *x = 0; |
106 | *y = 0; |
107 | *width = state.viewport_width; |
108 | *height = state.viewport_height; |
109 | |
110 | svgtiny_parse_attributes(node, &state, styles); |
111 | } |
112 | |
113 | |
114 | /** |
115 | * Call this to ref the strings in a gradient state. |
116 | */ |
117 | static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient *grad) |
118 | { |
119 | if (grad->gradient_x1 != NULL((void*)0)) { |
120 | dom_string_ref(grad->gradient_x1); |
121 | } |
122 | if (grad->gradient_y1 != NULL((void*)0)) { |
123 | dom_string_ref(grad->gradient_y1); |
124 | } |
125 | if (grad->gradient_x2 != NULL((void*)0)) { |
126 | dom_string_ref(grad->gradient_x2); |
127 | } |
128 | if (grad->gradient_y2 != NULL((void*)0)) { |
129 | dom_string_ref(grad->gradient_y2); |
130 | } |
131 | } |
132 | |
133 | |
134 | /** |
135 | * Call this to clean up the strings in a gradient state. |
136 | */ |
137 | static void svgtiny_grad_string_cleanup( |
138 | struct svgtiny_parse_state_gradient *grad) |
139 | { |
140 | if (grad->gradient_x1 != NULL((void*)0)) { |
141 | dom_string_unref(grad->gradient_x1); |
142 | grad->gradient_x1 = NULL((void*)0); |
143 | } |
144 | if (grad->gradient_y1 != NULL((void*)0)) { |
145 | dom_string_unref(grad->gradient_y1); |
146 | grad->gradient_y1 = NULL((void*)0); |
147 | } |
148 | if (grad->gradient_x2 != NULL((void*)0)) { |
149 | dom_string_unref(grad->gradient_x2); |
150 | grad->gradient_x2 = NULL((void*)0); |
151 | } |
152 | if (grad->gradient_y2 != NULL((void*)0)) { |
153 | dom_string_unref(grad->gradient_y2); |
154 | grad->gradient_y2 = NULL((void*)0); |
155 | } |
156 | } |
157 | |
158 | |
159 | /** |
160 | * Set the local externally-stored parts of a parse state. |
161 | * Call this in functions that made a new state on the stack. |
162 | * Doesn't make own copy of global state, such as the interned string list. |
163 | */ |
164 | static void svgtiny_setup_state_local(struct svgtiny_parse_state *state) |
165 | { |
166 | svgtiny_grad_string_ref(&(state->fill_grad)); |
167 | svgtiny_grad_string_ref(&(state->stroke_grad)); |
168 | } |
169 | |
170 | |
171 | /** |
172 | * Cleanup the local externally-stored parts of a parse state. |
173 | * Call this in functions that made a new state on the stack. |
174 | * Doesn't cleanup global state, such as the interned string list. |
175 | */ |
176 | static void svgtiny_cleanup_state_local(struct svgtiny_parse_state *state) |
177 | { |
178 | svgtiny_grad_string_cleanup(&(state->fill_grad)); |
179 | svgtiny_grad_string_cleanup(&(state->stroke_grad)); |
180 | } |
181 | |
182 | |
183 | static void ignore_msg(uint32_t severity, void *ctx, const char *msg, ...) |
184 | { |
185 | #ifdef PRINT_XML_PARSE_MSG |
186 | #include <stdarg.h> |
187 | va_list l; |
188 | |
189 | UNUSED(ctx)((void) (ctx)); |
190 | |
191 | va_start(l, msg)__builtin_va_start(l, msg); |
192 | |
193 | fprintf(stderrstderr, "%"PRIu32"u"": ", severity); |
194 | vfprintf(stderrstderr, msg, l); |
195 | fprintf(stderrstderr, "\n"); |
196 | |
197 | va_end(l)__builtin_va_end(l); |
198 | #else |
199 | UNUSED(severity)((void) (severity)); |
200 | UNUSED(ctx)((void) (ctx)); |
201 | UNUSED(msg)((void) (msg)); |
202 | #endif |
203 | } |
204 | |
205 | |
206 | /** |
207 | * Parse paint attributes, if present. |
208 | */ |
209 | static void |
210 | svgtiny_parse_paint_attributes(dom_element *node, |
211 | struct svgtiny_parse_state *state) |
212 | { |
213 | struct svgtiny_parse_internal_operation ops[] = { |
214 | { |
215 | /* fill color */ |
216 | state->interned_fill, |
217 | SVGTIOP_PAINT, |
218 | &state->fill_grad, |
219 | &state->fill |
220 | }, { |
221 | /* stroke color */ |
222 | state->interned_stroke, |
223 | SVGTIOP_PAINT, |
224 | &state->stroke_grad, |
225 | &state->stroke |
226 | }, { |
227 | /* stroke width */ |
228 | state->interned_stroke_width, |
229 | SVGTIOP_INTLENGTH, |
230 | &state->viewport_width, |
231 | &state->stroke_width |
232 | },{ |
233 | NULL((void*)0), SVGTIOP_NONE, NULL((void*)0), NULL((void*)0) |
234 | }, |
235 | }; |
236 | |
237 | svgtiny_parse_attributes(node, state, ops); |
238 | svgtiny_parse_inline_style(node, state, ops); |
239 | } |
240 | |
241 | |
242 | /** |
243 | * Parse font attributes, if present. |
244 | */ |
245 | static void |
246 | svgtiny_parse_font_attributes(dom_element *node, |
247 | struct svgtiny_parse_state *state) |
248 | { |
249 | /* TODO: Implement this, it never used to be */ |
250 | UNUSED(node)((void) (node)); |
251 | UNUSED(state)((void) (state)); |
252 | #ifdef WRITTEN_THIS_PROPERLY |
253 | const xmlAttr *attr; |
254 | |
255 | UNUSED(state)((void) (state)); |
256 | |
257 | for (attr = node->properties; attr; attr = attr->next) { |
258 | if (strcmp((const char *) attr->name, "font-size") == 0) { |
259 | /*if (css_parse_length( |
260 | (const char *) attr->children->content, |
261 | &state->style.font_size.value.length, |
262 | true, true)) { |
263 | state->style.font_size.size = |
264 | CSS_FONT_SIZE_LENGTH; |
265 | }*/ |
266 | } |
267 | } |
268 | #endif |
269 | } |
270 | |
271 | |
272 | /** |
273 | * Parse transform attributes, if present. |
274 | * |
275 | * http://www.w3.org/TR/SVG11/coords#TransformAttribute |
276 | */ |
277 | static void |
278 | svgtiny_parse_transform_attributes(dom_element *node, |
279 | struct svgtiny_parse_state *state) |
280 | { |
281 | dom_string *attr; |
282 | dom_exception exc; |
283 | |
284 | exc = dom_element_get_attribute(node, state->interned_transform, &attr)dom_element_get_attribute( (dom_element *) (node), (state-> interned_transform), (&attr)); |
285 | if (exc == DOM_NO_ERR && attr != NULL((void*)0)) { |
286 | svgtiny_parse_transform(dom_string_data(attr), |
287 | dom_string_byte_length(attr), |
288 | &state->ctm); |
289 | |
290 | dom_string_unref(attr); |
291 | } |
292 | } |
293 | |
294 | |
295 | /** |
296 | * Add a path to the svgtiny_diagram. |
297 | */ |
298 | static svgtiny_code |
299 | svgtiny_add_path(float *p, unsigned int n, struct svgtiny_parse_state *state) |
300 | { |
301 | struct svgtiny_shape *shape; |
302 | svgtiny_code res = svgtiny_OK; |
303 | |
304 | if (state->fill == svgtiny_LINEAR_GRADIENT0x2000000) { |
305 | /* adds a shape to fill the path with a linear gradient */ |
306 | res = svgtiny_gradient_add_fill_path(p, n, state); |
307 | } |
308 | if (res != svgtiny_OK) { |
309 | free(p); |
310 | return res; |
311 | } |
312 | |
313 | if (state->stroke == svgtiny_LINEAR_GRADIENT0x2000000) { |
314 | /* adds a shape to stroke the path with a linear gradient */ |
315 | res = svgtiny_gradient_add_stroke_path(p, n, state); |
316 | } |
317 | if (res != svgtiny_OK) { |
318 | free(p); |
319 | return res; |
320 | } |
321 | |
322 | /* if stroke and fill are transparent do not add a shape */ |
323 | if ((state->fill == svgtiny_TRANSPARENT0x1000000) && |
324 | (state->stroke == svgtiny_TRANSPARENT0x1000000)) { |
325 | free(p); |
326 | return res; |
327 | } |
328 | |
329 | svgtiny_transform_path(p, n, state); |
330 | |
331 | shape = svgtiny_add_shape(state); |
332 | if (shape == NULL((void*)0)) { |
333 | free(p); |
334 | return svgtiny_OUT_OF_MEMORY; |
335 | } |
336 | shape->path = p; |
337 | shape->path_length = n; |
338 | state->diagram->shape_count++; |
339 | |
340 | |
341 | return svgtiny_OK; |
342 | } |
343 | |
344 | |
345 | /** |
346 | * return svgtiny_OK if source is an ancestor of target else svgtiny_LIBDOM_ERROR |
347 | */ |
348 | static svgtiny_code is_ancestor_node(dom_node *source, dom_node *target) |
349 | { |
350 | dom_node *parent; |
351 | dom_exception exc; |
352 | |
353 | parent = dom_node_ref(target)dom_node_ref((dom_node *) (target)); |
354 | while (parent != NULL((void*)0)) { |
355 | dom_node *next = NULL((void*)0); |
356 | if (parent == source) { |
357 | dom_node_unref(parent)dom_node_unref((dom_node *) (parent)); |
358 | return svgtiny_OK; |
359 | } |
360 | exc = dom_node_get_parent_node(parent, &next)dom_node_get_parent_node( (dom_node *) (parent), (dom_node ** ) (&next)); |
361 | dom_node_unref(parent)dom_node_unref((dom_node *) (parent)); |
362 | if (exc != DOM_NO_ERR) { |
363 | break; |
364 | } |
365 | |
366 | parent = next; |
367 | } |
368 | return svgtiny_LIBDOM_ERROR; |
369 | } |
370 | |
371 | |
372 | /** |
373 | * Parse a <path> element node. |
374 | * |
375 | * https://svgwg.org/svg2-draft/paths.html#PathElement |
376 | */ |
377 | static svgtiny_code |
378 | svgtiny_parse_path(dom_element *path, struct svgtiny_parse_state state) |
379 | { |
380 | svgtiny_code res; |
381 | dom_string *path_d_str; |
382 | dom_exception exc; |
383 | float *p; /* path elemets */ |
384 | unsigned int i; |
385 | |
386 | svgtiny_setup_state_local(&state); |
387 | |
388 | svgtiny_parse_paint_attributes(path, &state); |
389 | svgtiny_parse_transform_attributes(path, &state); |
390 | |
391 | /* read d attribute */ |
392 | exc = dom_element_get_attribute(path, state.interned_d, &path_d_str)dom_element_get_attribute( (dom_element *) (path), (state.interned_d ), (&path_d_str)); |
393 | if (exc != DOM_NO_ERR) { |
394 | state.diagram->error_line = -1; /* path->line; */ |
395 | state.diagram->error_message = "path: error retrieving d attribute"; |
396 | svgtiny_cleanup_state_local(&state); |
397 | return svgtiny_SVG_ERROR; |
398 | } |
399 | |
400 | if (path_d_str == NULL((void*)0)) { |
401 | state.diagram->error_line = -1; /* path->line; */ |
402 | state.diagram->error_message = "path: missing d attribute"; |
403 | svgtiny_cleanup_state_local(&state); |
404 | return svgtiny_SVG_ERROR; |
405 | } |
406 | |
407 | res = svgtiny_parse_path_data(dom_string_data(path_d_str), |
408 | dom_string_byte_length(path_d_str), |
409 | &p, |
410 | &i); |
411 | if (res != svgtiny_OK) { |
412 | svgtiny_cleanup_state_local(&state); |
413 | return res; |
414 | } |
415 | |
416 | if (i <= 4) { |
417 | /* insufficient segments in path treated as none */ |
418 | if (i > 0) { |
419 | free(p); |
420 | } |
421 | res = svgtiny_OK; |
422 | } else { |
423 | res = svgtiny_add_path(p, i, &state); |
424 | } |
425 | |
426 | svgtiny_cleanup_state_local(&state); |
427 | |
428 | return res; |
429 | } |
430 | |
431 | |
432 | /** |
433 | * Parse a <rect> element node. |
434 | * |
435 | * http://www.w3.org/TR/SVG11/shapes#RectElement |
436 | */ |
437 | static svgtiny_code |
438 | svgtiny_parse_rect(dom_element *rect, struct svgtiny_parse_state state) |
439 | { |
440 | svgtiny_code err; |
441 | float x, y, width, height; |
442 | float *p; |
443 | |
444 | svgtiny_setup_state_local(&state); |
445 | |
446 | svgtiny_parse_position_attributes(rect, state, |
447 | &x, &y, &width, &height); |
448 | svgtiny_parse_paint_attributes(rect, &state); |
449 | svgtiny_parse_transform_attributes(rect, &state); |
450 | |
451 | p = malloc(13 * sizeof p[0]); |
452 | if (!p) { |
453 | svgtiny_cleanup_state_local(&state); |
454 | return svgtiny_OUT_OF_MEMORY; |
455 | } |
456 | |
457 | p[0] = svgtiny_PATH_MOVE; |
458 | p[1] = x; |
459 | p[2] = y; |
460 | p[3] = svgtiny_PATH_LINE; |
461 | p[4] = x + width; |
462 | p[5] = y; |
463 | p[6] = svgtiny_PATH_LINE; |
464 | p[7] = x + width; |
465 | p[8] = y + height; |
466 | p[9] = svgtiny_PATH_LINE; |
467 | p[10] = x; |
468 | p[11] = y + height; |
469 | p[12] = svgtiny_PATH_CLOSE; |
470 | |
471 | err = svgtiny_add_path(p, 13, &state); |
472 | |
473 | svgtiny_cleanup_state_local(&state); |
474 | |
475 | return err; |
476 | } |
477 | |
478 | |
479 | /** |
480 | * Parse a <circle> element node. |
481 | */ |
482 | static svgtiny_code |
483 | svgtiny_parse_circle(dom_element *circle, struct svgtiny_parse_state state) |
484 | { |
485 | svgtiny_code err; |
486 | float x = 0, y = 0, r = -1; |
487 | float *p; |
488 | struct svgtiny_parse_internal_operation ops[] = { |
489 | { |
490 | state.interned_cx, |
491 | SVGTIOP_LENGTH, |
492 | &state.viewport_width, |
493 | &x |
494 | }, { |
495 | state.interned_cy, |
496 | SVGTIOP_LENGTH, |
497 | &state.viewport_height, |
498 | &y |
499 | }, { |
500 | state.interned_r, |
501 | SVGTIOP_LENGTH, |
502 | &state.viewport_width, |
503 | &r |
504 | }, { |
505 | NULL((void*)0), SVGTIOP_NONE, NULL((void*)0), NULL((void*)0) |
506 | }, |
507 | }; |
508 | |
509 | svgtiny_setup_state_local(&state); |
510 | |
511 | err = svgtiny_parse_attributes(circle, &state, ops); |
512 | if (err != svgtiny_OK) { |
513 | svgtiny_cleanup_state_local(&state); |
514 | return err; |
515 | } |
516 | |
517 | svgtiny_parse_paint_attributes(circle, &state); |
518 | svgtiny_parse_transform_attributes(circle, &state); |
519 | |
520 | if (r < 0) { |
521 | state.diagram->error_line = -1; /* circle->line; */ |
522 | state.diagram->error_message = "circle: r missing or negative"; |
523 | svgtiny_cleanup_state_local(&state); |
524 | return svgtiny_SVG_ERROR; |
525 | } |
526 | if (r == 0) { |
527 | svgtiny_cleanup_state_local(&state); |
528 | return svgtiny_OK; |
529 | } |
530 | |
531 | p = malloc(32 * sizeof p[0]); |
532 | if (!p) { |
533 | svgtiny_cleanup_state_local(&state); |
534 | return svgtiny_OUT_OF_MEMORY; |
535 | } |
536 | |
537 | p[0] = svgtiny_PATH_MOVE; |
538 | p[1] = x + r; |
539 | p[2] = y; |
540 | p[3] = svgtiny_PATH_BEZIER; |
541 | p[4] = x + r; |
542 | p[5] = y + r * KAPPA0.5522847498; |
543 | p[6] = x + r * KAPPA0.5522847498; |
544 | p[7] = y + r; |
545 | p[8] = x; |
546 | p[9] = y + r; |
547 | p[10] = svgtiny_PATH_BEZIER; |
548 | p[11] = x - r * KAPPA0.5522847498; |
549 | p[12] = y + r; |
550 | p[13] = x - r; |
551 | p[14] = y + r * KAPPA0.5522847498; |
552 | p[15] = x - r; |
553 | p[16] = y; |
554 | p[17] = svgtiny_PATH_BEZIER; |
555 | p[18] = x - r; |
556 | p[19] = y - r * KAPPA0.5522847498; |
557 | p[20] = x - r * KAPPA0.5522847498; |
558 | p[21] = y - r; |
559 | p[22] = x; |
560 | p[23] = y - r; |
561 | p[24] = svgtiny_PATH_BEZIER; |
562 | p[25] = x + r * KAPPA0.5522847498; |
563 | p[26] = y - r; |
564 | p[27] = x + r; |
565 | p[28] = y - r * KAPPA0.5522847498; |
566 | p[29] = x + r; |
567 | p[30] = y; |
568 | p[31] = svgtiny_PATH_CLOSE; |
569 | |
570 | err = svgtiny_add_path(p, 32, &state); |
571 | |
572 | svgtiny_cleanup_state_local(&state); |
573 | |
574 | return err; |
575 | } |
576 | |
577 | |
578 | /** |
579 | * Parse an <ellipse> element node. |
580 | */ |
581 | static svgtiny_code |
582 | svgtiny_parse_ellipse(dom_element *ellipse, struct svgtiny_parse_state state) |
583 | { |
584 | svgtiny_code err; |
585 | float x = 0, y = 0, rx = -1, ry = -1; |
586 | float *p; |
587 | struct svgtiny_parse_internal_operation ops[] = { |
588 | { |
589 | state.interned_cx, |
590 | SVGTIOP_LENGTH, |
591 | &state.viewport_width, |
592 | &x |
593 | }, { |
594 | state.interned_cy, |
595 | SVGTIOP_LENGTH, |
596 | &state.viewport_height, |
597 | &y |
598 | }, { |
599 | state.interned_rx, |
600 | SVGTIOP_LENGTH, |
601 | &state.viewport_width, |
602 | &rx |
603 | }, { |
604 | state.interned_ry, |
605 | SVGTIOP_LENGTH, |
606 | &state.viewport_height, |
607 | &ry |
608 | }, { |
609 | NULL((void*)0), SVGTIOP_NONE, NULL((void*)0), NULL((void*)0) |
610 | }, |
611 | }; |
612 | |
613 | svgtiny_setup_state_local(&state); |
614 | |
615 | err = svgtiny_parse_attributes(ellipse, &state, ops); |
616 | if (err != svgtiny_OK) { |
617 | svgtiny_cleanup_state_local(&state); |
618 | return err; |
619 | } |
620 | |
621 | svgtiny_parse_paint_attributes(ellipse, &state); |
622 | svgtiny_parse_transform_attributes(ellipse, &state); |
623 | |
624 | if (rx < 0 || ry < 0) { |
625 | state.diagram->error_line = -1; /* ellipse->line; */ |
626 | state.diagram->error_message = "ellipse: rx or ry missing " |
627 | "or negative"; |
628 | svgtiny_cleanup_state_local(&state); |
629 | return svgtiny_SVG_ERROR; |
630 | } |
631 | if (rx == 0 || ry == 0) { |
632 | svgtiny_cleanup_state_local(&state); |
633 | return svgtiny_OK; |
634 | } |
635 | |
636 | p = malloc(32 * sizeof p[0]); |
637 | if (!p) { |
638 | svgtiny_cleanup_state_local(&state); |
639 | return svgtiny_OUT_OF_MEMORY; |
640 | } |
641 | |
642 | p[0] = svgtiny_PATH_MOVE; |
643 | p[1] = x + rx; |
644 | p[2] = y; |
645 | p[3] = svgtiny_PATH_BEZIER; |
646 | p[4] = x + rx; |
647 | p[5] = y + ry * KAPPA0.5522847498; |
648 | p[6] = x + rx * KAPPA0.5522847498; |
649 | p[7] = y + ry; |
650 | p[8] = x; |
651 | p[9] = y + ry; |
652 | p[10] = svgtiny_PATH_BEZIER; |
653 | p[11] = x - rx * KAPPA0.5522847498; |
654 | p[12] = y + ry; |
655 | p[13] = x - rx; |
656 | p[14] = y + ry * KAPPA0.5522847498; |
657 | p[15] = x - rx; |
658 | p[16] = y; |
659 | p[17] = svgtiny_PATH_BEZIER; |
660 | p[18] = x - rx; |
661 | p[19] = y - ry * KAPPA0.5522847498; |
662 | p[20] = x - rx * KAPPA0.5522847498; |
663 | p[21] = y - ry; |
664 | p[22] = x; |
665 | p[23] = y - ry; |
666 | p[24] = svgtiny_PATH_BEZIER; |
667 | p[25] = x + rx * KAPPA0.5522847498; |
668 | p[26] = y - ry; |
669 | p[27] = x + rx; |
670 | p[28] = y - ry * KAPPA0.5522847498; |
671 | p[29] = x + rx; |
672 | p[30] = y; |
673 | p[31] = svgtiny_PATH_CLOSE; |
674 | |
675 | err = svgtiny_add_path(p, 32, &state); |
676 | |
677 | svgtiny_cleanup_state_local(&state); |
678 | |
679 | return err; |
680 | } |
681 | |
682 | |
683 | /** |
684 | * Parse a <line> element node. |
685 | */ |
686 | static svgtiny_code |
687 | svgtiny_parse_line(dom_element *line, struct svgtiny_parse_state state) |
688 | { |
689 | svgtiny_code err; |
690 | float x1 = 0, y1 = 0, x2 = 0, y2 = 0; |
691 | float *p; |
692 | struct svgtiny_parse_internal_operation ops[] = { |
693 | { |
694 | state.interned_x1, |
695 | SVGTIOP_LENGTH, |
696 | &state.viewport_width, |
697 | &x1 |
698 | }, { |
699 | state.interned_y1, |
700 | SVGTIOP_LENGTH, |
701 | &state.viewport_height, |
702 | &y1 |
703 | }, { |
704 | state.interned_x2, |
705 | SVGTIOP_LENGTH, |
706 | &state.viewport_width, |
707 | &x2 |
708 | }, { |
709 | state.interned_y2, |
710 | SVGTIOP_LENGTH, |
711 | &state.viewport_height, |
712 | &y2 |
713 | }, { |
714 | NULL((void*)0), SVGTIOP_NONE, NULL((void*)0), NULL((void*)0) |
715 | }, |
716 | }; |
717 | |
718 | svgtiny_setup_state_local(&state); |
719 | |
720 | err = svgtiny_parse_attributes(line, &state, ops); |
721 | if (err != svgtiny_OK) { |
722 | svgtiny_cleanup_state_local(&state); |
723 | return err; |
724 | } |
725 | |
726 | svgtiny_parse_paint_attributes(line, &state); |
727 | svgtiny_parse_transform_attributes(line, &state); |
728 | |
729 | p = malloc(7 * sizeof p[0]); |
730 | if (!p) { |
731 | svgtiny_cleanup_state_local(&state); |
732 | return svgtiny_OUT_OF_MEMORY; |
733 | } |
734 | |
735 | p[0] = svgtiny_PATH_MOVE; |
736 | p[1] = x1; |
737 | p[2] = y1; |
738 | p[3] = svgtiny_PATH_LINE; |
739 | p[4] = x2; |
740 | p[5] = y2; |
741 | p[6] = svgtiny_PATH_CLOSE; |
742 | |
743 | err = svgtiny_add_path(p, 7, &state); |
744 | |
745 | svgtiny_cleanup_state_local(&state); |
746 | |
747 | return err; |
748 | } |
749 | |
750 | |
751 | /** |
752 | * Parse a <polyline> or <polygon> element node. |
753 | * |
754 | * http://www.w3.org/TR/SVG11/shapes#PolylineElement |
755 | * http://www.w3.org/TR/SVG11/shapes#PolygonElement |
756 | */ |
757 | static svgtiny_code |
758 | svgtiny_parse_poly(dom_element *poly, |
759 | struct svgtiny_parse_state state, |
760 | bool_Bool polygon) |
761 | { |
762 | svgtiny_code err; |
763 | dom_string *points_str; |
764 | dom_exception exc; |
765 | float *pointv; |
766 | unsigned int pointc; |
767 | |
768 | svgtiny_setup_state_local(&state); |
769 | |
770 | svgtiny_parse_paint_attributes(poly, &state); |
771 | svgtiny_parse_transform_attributes(poly, &state); |
772 | |
773 | exc = dom_element_get_attribute(poly, state.interned_points,dom_element_get_attribute( (dom_element *) (poly), (state.interned_points ), (&points_str)) |
774 | &points_str)dom_element_get_attribute( (dom_element *) (poly), (state.interned_points ), (&points_str)); |
775 | if (exc != DOM_NO_ERR) { |
776 | svgtiny_cleanup_state_local(&state); |
777 | return svgtiny_LIBDOM_ERROR; |
778 | } |
779 | |
780 | if (points_str == NULL((void*)0)) { |
781 | state.diagram->error_line = -1; /* poly->line; */ |
782 | state.diagram->error_message = |
783 | "polyline/polygon: missing points attribute"; |
784 | svgtiny_cleanup_state_local(&state); |
785 | return svgtiny_SVG_ERROR; |
786 | } |
787 | |
788 | /* allocate space for path: it will never have more elements than bytes |
789 | * in the string. |
790 | */ |
791 | pointc = dom_string_byte_length(points_str); |
792 | pointv = malloc(sizeof pointv[0] * pointc); |
793 | if (pointv == NULL((void*)0)) { |
794 | svgtiny_cleanup_state_local(&state); |
795 | return svgtiny_OUT_OF_MEMORY; |
796 | } |
797 | |
798 | err = svgtiny_parse_poly_points(dom_string_data(points_str), |
799 | dom_string_byte_length(points_str), |
800 | pointv, |
801 | &pointc); |
802 | dom_string_unref(points_str); |
803 | if (err != svgtiny_OK) { |
804 | free(pointv); |
805 | state.diagram->error_line = -1; /* poly->line; */ |
806 | state.diagram->error_message = |
807 | "polyline/polygon: failed to parse points"; |
808 | } else { |
809 | if (pointc > 0) { |
810 | pointv[0] = svgtiny_PATH_MOVE; |
811 | } |
812 | if (polygon) { |
813 | pointv[pointc++] = svgtiny_PATH_CLOSE; |
814 | } |
815 | |
816 | err = svgtiny_add_path(pointv, pointc, &state); |
817 | } |
818 | svgtiny_cleanup_state_local(&state); |
819 | |
820 | return err; |
821 | } |
822 | |
823 | |
824 | /** |
825 | * Parse a <text> or <tspan> element node. |
826 | */ |
827 | static svgtiny_code |
828 | svgtiny_parse_text(dom_element *text, struct svgtiny_parse_state state) |
829 | { |
830 | float x, y, width, height; |
831 | float px, py; |
832 | dom_node *child; |
833 | dom_exception exc; |
834 | |
835 | svgtiny_setup_state_local(&state); |
836 | |
837 | svgtiny_parse_position_attributes(text, state, |
838 | &x, &y, &width, &height); |
839 | svgtiny_parse_font_attributes(text, &state); |
840 | svgtiny_parse_transform_attributes(text, &state); |
841 | |
842 | px = state.ctm.a * x + state.ctm.c * y + state.ctm.e; |
843 | py = state.ctm.b * x + state.ctm.d * y + state.ctm.f; |
844 | /* state.ctm.e = px - state.origin_x; */ |
845 | /* state.ctm.f = py - state.origin_y; */ |
846 | |
847 | /*struct css_style style = state.style; |
848 | style.font_size.value.length.value *= state.ctm.a;*/ |
849 | |
850 | exc = dom_node_get_first_child(text, &child)dom_node_get_first_child( (dom_node *) (text), (dom_node **) ( &child)); |
851 | if (exc != DOM_NO_ERR) { |
852 | return svgtiny_LIBDOM_ERROR; |
853 | svgtiny_cleanup_state_local(&state); |
854 | } |
855 | while (child != NULL((void*)0)) { |
856 | dom_node *next; |
857 | dom_node_type nodetype; |
858 | svgtiny_code code = svgtiny_OK; |
859 | |
860 | exc = dom_node_get_node_type(child, &nodetype)dom_node_get_node_type( (dom_node *) (child), (dom_node_type * ) (&nodetype)); |
861 | if (exc != DOM_NO_ERR) { |
862 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
863 | svgtiny_cleanup_state_local(&state); |
864 | return svgtiny_LIBDOM_ERROR; |
865 | } |
866 | if (nodetype == DOM_ELEMENT_NODE) { |
867 | dom_string *nodename; |
868 | exc = dom_node_get_node_name(child, &nodename)dom_node_get_node_name((dom_node *) (child), (&nodename)); |
869 | if (exc != DOM_NO_ERR) { |
870 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
871 | svgtiny_cleanup_state_local(&state); |
872 | return svgtiny_LIBDOM_ERROR; |
873 | } |
874 | if (dom_string_caseless_isequal(nodename, |
875 | state.interned_tspan)) |
876 | code = svgtiny_parse_text((dom_element *)child, |
877 | state); |
878 | dom_string_unref(nodename); |
879 | } else if (nodetype == DOM_TEXT_NODE) { |
880 | struct svgtiny_shape *shape = svgtiny_add_shape(&state); |
881 | dom_string *content; |
882 | if (shape == NULL((void*)0)) { |
883 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
884 | svgtiny_cleanup_state_local(&state); |
885 | return svgtiny_OUT_OF_MEMORY; |
886 | } |
887 | exc = dom_text_get_whole_text(child, &content)dom_text_get_whole_text((dom_text *) (child), (&content)); |
888 | if (exc != DOM_NO_ERR) { |
889 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
890 | svgtiny_cleanup_state_local(&state); |
891 | return svgtiny_LIBDOM_ERROR; |
892 | } |
893 | if (content != NULL((void*)0)) { |
894 | shape->text = strndup(dom_string_data(content), |
895 | dom_string_byte_length(content)); |
896 | dom_string_unref(content); |
897 | } else { |
898 | shape->text = strdup(""); |
899 | } |
900 | shape->text_x = px; |
901 | shape->text_y = py; |
902 | state.diagram->shape_count++; |
903 | } |
904 | |
905 | if (code != svgtiny_OK) { |
906 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
907 | svgtiny_cleanup_state_local(&state); |
908 | return code; |
909 | } |
910 | exc = dom_node_get_next_sibling(child, &next)dom_node_get_next_sibling( (dom_node *) (child), (dom_node ** ) (&next)); |
911 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
912 | if (exc != DOM_NO_ERR) { |
913 | svgtiny_cleanup_state_local(&state); |
914 | return svgtiny_LIBDOM_ERROR; |
915 | } |
916 | child = next; |
917 | } |
918 | |
919 | svgtiny_cleanup_state_local(&state); |
920 | |
921 | return svgtiny_OK; |
922 | } |
923 | |
924 | |
925 | /** |
926 | * Parse a <use> element node. |
927 | * |
928 | * https://www.w3.org/TR/SVG2/struct.html#UseElement |
929 | */ |
930 | static svgtiny_code |
931 | svgtiny_parse_use(dom_element *use, struct svgtiny_parse_state state) |
932 | { |
933 | svgtiny_code res; |
934 | dom_element *ref; /* referenced element */ |
935 | |
936 | svgtiny_setup_state_local(&state); |
937 | |
938 | res = svgtiny_parse_element_from_href(use, &state, &ref); |
939 | if (res != svgtiny_OK) { |
940 | svgtiny_cleanup_state_local(&state); |
941 | return res; |
942 | } |
943 | |
944 | if (ref != NULL((void*)0)) { |
945 | /* found the reference */ |
946 | |
947 | /** |
948 | * If the referenced element is a ancestor of the ‘use’ element, |
949 | * then this is an invalid circular reference and the ‘use’ |
950 | * element is in error. |
951 | */ |
952 | res = is_ancestor_node((dom_node *)ref, (dom_node *)use); |
953 | if (res != svgtiny_OK) { |
954 | res = parse_element(ref, &state); |
Value stored to 'res' is never read | |
955 | } |
956 | dom_node_unref(ref)dom_node_unref((dom_node *) (ref)); |
957 | } |
958 | |
959 | svgtiny_cleanup_state_local(&state); |
960 | |
961 | return svgtiny_OK; |
962 | } |
963 | |
964 | |
965 | /** |
966 | * Parse a <svg> or <g> element node. |
967 | */ |
968 | static svgtiny_code |
969 | svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state) |
970 | { |
971 | float x, y, width, height; |
972 | dom_string *view_box; |
973 | dom_element *child; |
974 | dom_exception exc; |
975 | |
976 | svgtiny_setup_state_local(&state); |
977 | |
978 | svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height); |
979 | svgtiny_parse_paint_attributes(svg, &state); |
980 | svgtiny_parse_font_attributes(svg, &state); |
981 | |
982 | exc = dom_element_get_attribute(svg, state.interned_viewBox,dom_element_get_attribute( (dom_element *) (svg), (state.interned_viewBox ), (&view_box)) |
983 | &view_box)dom_element_get_attribute( (dom_element *) (svg), (state.interned_viewBox ), (&view_box)); |
984 | if (exc != DOM_NO_ERR) { |
985 | svgtiny_cleanup_state_local(&state); |
986 | return svgtiny_LIBDOM_ERROR; |
987 | } |
988 | |
989 | if (view_box) { |
990 | svgtiny_parse_viewbox(dom_string_data(view_box), |
991 | dom_string_byte_length(view_box), |
992 | state.viewport_width, |
993 | state.viewport_height, |
994 | &state.ctm); |
995 | dom_string_unref(view_box); |
996 | } |
997 | |
998 | svgtiny_parse_transform_attributes(svg, &state); |
999 | |
1000 | exc = dom_node_get_first_child(svg, (dom_node **) (void *) &child)dom_node_get_first_child( (dom_node *) (svg), (dom_node **) ( (dom_node **) (void *) &child)); |
1001 | if (exc != DOM_NO_ERR) { |
1002 | svgtiny_cleanup_state_local(&state); |
1003 | return svgtiny_LIBDOM_ERROR; |
1004 | } |
1005 | while (child != NULL((void*)0)) { |
1006 | dom_element *next; |
1007 | dom_node_type nodetype; |
1008 | svgtiny_code code = svgtiny_OK; |
1009 | |
1010 | exc = dom_node_get_node_type(child, &nodetype)dom_node_get_node_type( (dom_node *) (child), (dom_node_type * ) (&nodetype)); |
1011 | if (exc != DOM_NO_ERR) { |
1012 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
1013 | return svgtiny_LIBDOM_ERROR; |
1014 | } |
1015 | if (nodetype == DOM_ELEMENT_NODE) { |
1016 | code = parse_element(child, &state); |
1017 | } |
1018 | if (code != svgtiny_OK) { |
1019 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
1020 | svgtiny_cleanup_state_local(&state); |
1021 | return code; |
1022 | } |
1023 | exc = dom_node_get_next_sibling(child,dom_node_get_next_sibling( (dom_node *) (child), (dom_node ** ) ((dom_node **) (void *) &next)) |
1024 | (dom_node **) (void *) &next)dom_node_get_next_sibling( (dom_node *) (child), (dom_node ** ) ((dom_node **) (void *) &next)); |
1025 | dom_node_unref(child)dom_node_unref((dom_node *) (child)); |
1026 | if (exc != DOM_NO_ERR) { |
1027 | svgtiny_cleanup_state_local(&state); |
1028 | return svgtiny_LIBDOM_ERROR; |
1029 | } |
1030 | child = next; |
1031 | } |
1032 | |
1033 | svgtiny_cleanup_state_local(&state); |
1034 | return svgtiny_OK; |
1035 | } |
1036 | |
1037 | |
1038 | static svgtiny_code |
1039 | parse_element(dom_element *element, struct svgtiny_parse_state *state) |
1040 | { |
1041 | dom_exception exc; |
1042 | dom_string *nodename; |
1043 | svgtiny_code code = svgtiny_OK;; |
1044 | |
1045 | exc = dom_node_get_node_name(element, &nodename)dom_node_get_node_name((dom_node *) (element), (&nodename )); |
1046 | if (exc != DOM_NO_ERR) { |
1047 | return svgtiny_LIBDOM_ERROR; |
1048 | } |
1049 | |
1050 | if (dom_string_caseless_isequal(state->interned_svg, nodename)) { |
1051 | code = svgtiny_parse_svg(element, *state); |
1052 | } else if (dom_string_caseless_isequal(state->interned_g, nodename)) { |
1053 | code = svgtiny_parse_svg(element, *state); |
1054 | } else if (dom_string_caseless_isequal(state->interned_a, nodename)) { |
1055 | code = svgtiny_parse_svg(element, *state); |
1056 | } else if (dom_string_caseless_isequal(state->interned_path, nodename)) { |
1057 | code = svgtiny_parse_path(element, *state); |
1058 | } else if (dom_string_caseless_isequal(state->interned_rect, nodename)) { |
1059 | code = svgtiny_parse_rect(element, *state); |
1060 | } else if (dom_string_caseless_isequal(state->interned_circle, nodename)) { |
1061 | code = svgtiny_parse_circle(element, *state); |
1062 | } else if (dom_string_caseless_isequal(state->interned_ellipse, nodename)) { |
1063 | code = svgtiny_parse_ellipse(element, *state); |
1064 | } else if (dom_string_caseless_isequal(state->interned_line, nodename)) { |
1065 | code = svgtiny_parse_line(element, *state); |
1066 | } else if (dom_string_caseless_isequal(state->interned_polyline, nodename)) { |
1067 | code = svgtiny_parse_poly(element, *state, false0); |
1068 | } else if (dom_string_caseless_isequal(state->interned_polygon, nodename)) { |
1069 | code = svgtiny_parse_poly(element, *state, true1); |
1070 | } else if (dom_string_caseless_isequal(state->interned_text, nodename)) { |
1071 | code = svgtiny_parse_text(element, *state); |
1072 | } else if (dom_string_caseless_isequal(state->interned_use, nodename)) { |
1073 | code = svgtiny_parse_use(element, *state); |
1074 | } |
1075 | dom_string_unref(nodename); |
1076 | return code; |
1077 | } |
1078 | |
1079 | |
1080 | static svgtiny_code |
1081 | initialise_parse_state(struct svgtiny_parse_state *state, |
1082 | struct svgtiny_diagram *diagram, |
1083 | dom_document *document, |
1084 | dom_element *svg, |
1085 | int viewport_width, |
1086 | int viewport_height) |
1087 | { |
1088 | float x, y, width, height; |
1089 | |
1090 | memset(state, 0, sizeof(*state)); |
1091 | |
1092 | state->diagram = diagram; |
1093 | state->document = document; |
1094 | |
1095 | #define SVGTINY_STRING_ACTION2(s,n) \ |
1096 | if (dom_string_create_interned((const uint8_t *) #n, \ |
1097 | strlen(#n), \ |
1098 | &state->interned_##s) \ |
1099 | != DOM_NO_ERR) { \ |
1100 | return svgtiny_LIBDOM_ERROR; \ |
1101 | } |
1102 | #include "svgtiny_strings.h" |
1103 | #undef SVGTINY_STRING_ACTION2 |
1104 | |
1105 | /* get graphic dimensions */ |
1106 | state->viewport_width = viewport_width; |
1107 | state->viewport_height = viewport_height; |
1108 | svgtiny_parse_position_attributes(svg, *state, &x, &y, &width, &height); |
1109 | diagram->width = width; |
1110 | diagram->height = height; |
1111 | |
1112 | /* set up parsing state */ |
1113 | state->viewport_width = width; |
1114 | state->viewport_height = height; |
1115 | state->ctm.a = 1; /*(float) viewport_width / (float) width;*/ |
1116 | state->ctm.b = 0; |
1117 | state->ctm.c = 0; |
1118 | state->ctm.d = 1; /*(float) viewport_height / (float) height;*/ |
1119 | state->ctm.e = 0; /*x;*/ |
1120 | state->ctm.f = 0; /*y;*/ |
1121 | /*state->style = css_base_style; |
1122 | state->style.font_size.value.length.value = option_font_size * 0.1;*/ |
1123 | state->fill = 0x000000; |
1124 | state->stroke = svgtiny_TRANSPARENT0x1000000; |
1125 | state->stroke_width = 1; |
1126 | return svgtiny_OK; |
1127 | } |
1128 | |
1129 | |
1130 | static svgtiny_code finalise_parse_state(struct svgtiny_parse_state *state) |
1131 | { |
1132 | svgtiny_cleanup_state_local(state); |
1133 | |
1134 | #define SVGTINY_STRING_ACTION2(s,n) \ |
1135 | if (state->interned_##s != NULL((void*)0)) \ |
1136 | dom_string_unref(state->interned_##s); |
1137 | #include "svgtiny_strings.h" |
1138 | #undef SVGTINY_STRING_ACTION2 |
1139 | return svgtiny_OK; |
1140 | } |
1141 | |
1142 | |
1143 | static svgtiny_code get_svg_element(dom_document *document, dom_element **svg) |
1144 | { |
1145 | dom_exception exc; |
1146 | dom_string *svg_name; |
1147 | lwc_string *svg_name_lwc; |
1148 | |
1149 | /* find root <svg> element */ |
1150 | exc = dom_document_get_document_element(document, svg)dom_document_get_document_element((dom_document *) (document) , (struct dom_element **) (svg)); |
1151 | if (exc != DOM_NO_ERR) { |
1152 | return svgtiny_LIBDOM_ERROR; |
1153 | } |
1154 | if (svg == NULL((void*)0)) { |
1155 | /* no root element */ |
1156 | return svgtiny_SVG_ERROR; |
1157 | } |
1158 | |
1159 | /* ensure root element is <svg> */ |
1160 | exc = dom_node_get_node_name(*svg, &svg_name)dom_node_get_node_name((dom_node *) (*svg), (&svg_name)); |
1161 | if (exc != DOM_NO_ERR) { |
1162 | dom_node_unref(*svg)dom_node_unref((dom_node *) (*svg)); |
1163 | return svgtiny_LIBDOM_ERROR; |
1164 | } |
1165 | if (lwc_intern_string("svg", 3 /* SLEN("svg") */, |
1166 | &svg_name_lwc) != lwc_error_ok) { |
1167 | dom_string_unref(svg_name); |
1168 | dom_node_unref(*svg)dom_node_unref((dom_node *) (*svg)); |
1169 | return svgtiny_LIBDOM_ERROR; |
1170 | } |
1171 | if (!dom_string_caseless_lwc_isequal(svg_name, svg_name_lwc)) { |
1172 | lwc_string_unref(svg_name_lwc){ lwc_string *__lwc_s = (svg_name_lwc); ((__lwc_s != ((void*) 0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/svgtiny.c" , 1172, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; |
1173 | dom_string_unref(svg_name); |
1174 | dom_node_unref(*svg)dom_node_unref((dom_node *) (*svg)); |
1175 | return svgtiny_NOT_SVG; |
1176 | } |
1177 | |
1178 | lwc_string_unref(svg_name_lwc){ lwc_string *__lwc_s = (svg_name_lwc); ((__lwc_s != ((void*) 0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "src/svgtiny.c" , 1178, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt --; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1 ) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy (__lwc_s); }; |
1179 | dom_string_unref(svg_name); |
1180 | |
1181 | return svgtiny_OK; |
1182 | } |
1183 | |
1184 | |
1185 | static svgtiny_code |
1186 | svg_document_from_buffer(uint8_t *buffer, size_t size, dom_document **document) |
1187 | { |
1188 | dom_xml_parser *parser; |
1189 | dom_xml_error err; |
1190 | |
1191 | parser = dom_xml_parser_create(NULL((void*)0), NULL((void*)0), ignore_msg, NULL((void*)0), document); |
1192 | |
1193 | if (parser == NULL((void*)0)) |
1194 | return svgtiny_LIBDOM_ERROR; |
1195 | |
1196 | err = dom_xml_parser_parse_chunk(parser, buffer, size); |
1197 | if (err != DOM_XML_OK) { |
1198 | dom_node_unref(*document)dom_node_unref((dom_node *) (*document)); |
1199 | dom_xml_parser_destroy(parser); |
1200 | return svgtiny_LIBDOM_ERROR; |
1201 | } |
1202 | |
1203 | err = dom_xml_parser_completed(parser); |
1204 | if (err != DOM_XML_OK) { |
1205 | dom_node_unref(*document)dom_node_unref((dom_node *) (*document)); |
1206 | dom_xml_parser_destroy(parser); |
1207 | return svgtiny_LIBDOM_ERROR; |
1208 | } |
1209 | |
1210 | /* We're done parsing, drop the parser. |
1211 | * We now own the document entirely. |
1212 | */ |
1213 | dom_xml_parser_destroy(parser); |
1214 | |
1215 | return svgtiny_OK; |
1216 | } |
1217 | |
1218 | |
1219 | /** |
1220 | * Add a svgtiny_shape to the svgtiny_diagram. |
1221 | * |
1222 | * library internal interface |
1223 | */ |
1224 | struct svgtiny_shape *svgtiny_add_shape(struct svgtiny_parse_state *state) |
1225 | { |
1226 | struct svgtiny_shape *shape; |
1227 | |
1228 | shape = realloc(state->diagram->shape, |
1229 | (state->diagram->shape_count + 1) * |
1230 | sizeof (state->diagram->shape[0])); |
1231 | if (shape != NULL((void*)0)) { |
1232 | state->diagram->shape = shape; |
1233 | |
1234 | shape += state->diagram->shape_count; |
1235 | shape->path = 0; |
1236 | shape->path_length = 0; |
1237 | shape->text = 0; |
1238 | shape->fill = state->fill; |
1239 | shape->stroke = state->stroke; |
1240 | shape->stroke_width = lroundf((float) state->stroke_width * |
1241 | (state->ctm.a + state->ctm.d) / 2.0); |
1242 | if (0 < state->stroke_width && shape->stroke_width == 0) |
1243 | shape->stroke_width = 1; |
1244 | } |
1245 | return shape; |
1246 | } |
1247 | |
1248 | |
1249 | /** |
1250 | * Apply the current transformation matrix to a path. |
1251 | * |
1252 | * library internal interface |
1253 | */ |
1254 | void |
1255 | svgtiny_transform_path(float *p, |
1256 | unsigned int n, |
1257 | struct svgtiny_parse_state *state) |
1258 | { |
1259 | unsigned int j; |
1260 | |
1261 | for (j = 0; j != n; ) { |
1262 | unsigned int points = 0; |
1263 | unsigned int k; |
1264 | switch ((int) p[j]) { |
1265 | case svgtiny_PATH_MOVE: |
1266 | case svgtiny_PATH_LINE: |
1267 | points = 1; |
1268 | break; |
1269 | case svgtiny_PATH_CLOSE: |
1270 | points = 0; |
1271 | break; |
1272 | case svgtiny_PATH_BEZIER: |
1273 | points = 3; |
1274 | break; |
1275 | default: |
1276 | assert(0)((0) ? (void) (0) : __assert_fail ("0", "src/svgtiny.c", 1276 , __extension__ __PRETTY_FUNCTION__)); |
1277 | } |
1278 | j++; |
1279 | for (k = 0; k != points; k++) { |
1280 | float x0 = p[j], y0 = p[j + 1]; |
1281 | float x = state->ctm.a * x0 + state->ctm.c * y0 + |
1282 | state->ctm.e; |
1283 | float y = state->ctm.b * x0 + state->ctm.d * y0 + |
1284 | state->ctm.f; |
1285 | p[j] = x; |
1286 | p[j + 1] = y; |
1287 | j += 2; |
1288 | } |
1289 | } |
1290 | } |
1291 | |
1292 | |
1293 | /** |
1294 | * Create a new svgtiny_diagram structure. |
1295 | */ |
1296 | struct svgtiny_diagram *svgtiny_create(void) |
1297 | { |
1298 | struct svgtiny_diagram *diagram; |
1299 | |
1300 | diagram = calloc(1, sizeof(*diagram)); |
1301 | |
1302 | return diagram; |
1303 | } |
1304 | |
1305 | |
1306 | /** |
1307 | * Parse a block of memory into a svgtiny_diagram. |
1308 | */ |
1309 | svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram, |
1310 | const char *buffer, size_t size, const char *url, |
1311 | int viewport_width, int viewport_height) |
1312 | { |
1313 | dom_document *document; |
1314 | dom_element *svg; |
1315 | struct svgtiny_parse_state state; |
1316 | svgtiny_code code; |
1317 | |
1318 | assert(diagram)((diagram) ? (void) (0) : __assert_fail ("diagram", "src/svgtiny.c" , 1318, __extension__ __PRETTY_FUNCTION__)); |
1319 | assert(buffer)((buffer) ? (void) (0) : __assert_fail ("buffer", "src/svgtiny.c" , 1319, __extension__ __PRETTY_FUNCTION__)); |
1320 | assert(url)((url) ? (void) (0) : __assert_fail ("url", "src/svgtiny.c", 1320 , __extension__ __PRETTY_FUNCTION__)); |
1321 | |
1322 | UNUSED(url)((void) (url)); |
1323 | |
1324 | code = svg_document_from_buffer((uint8_t *)buffer, size, &document); |
1325 | if (code == svgtiny_OK) { |
1326 | code = get_svg_element(document, &svg); |
1327 | if (code == svgtiny_OK) { |
1328 | code = initialise_parse_state(&state, |
1329 | diagram, |
1330 | document, |
1331 | svg, |
1332 | viewport_width, |
1333 | viewport_height); |
1334 | if (code == svgtiny_OK) { |
1335 | code = svgtiny_parse_svg(svg, state); |
1336 | } |
1337 | |
1338 | finalise_parse_state(&state); |
1339 | |
1340 | dom_node_unref(svg)dom_node_unref((dom_node *) (svg)); |
1341 | } |
1342 | dom_node_unref(document)dom_node_unref((dom_node *) (document)); |
1343 | } |
1344 | |
1345 | return code; |
1346 | } |
1347 | |
1348 | |
1349 | /** |
1350 | * Free all memory used by a diagram. |
1351 | */ |
1352 | void svgtiny_free(struct svgtiny_diagram *svg) |
1353 | { |
1354 | unsigned int i; |
1355 | assert(svg)((svg) ? (void) (0) : __assert_fail ("svg", "src/svgtiny.c", 1355 , __extension__ __PRETTY_FUNCTION__)); |
1356 | |
1357 | for (i = 0; i != svg->shape_count; i++) { |
1358 | free(svg->shape[i].path); |
1359 | free(svg->shape[i].text); |
1360 | } |
1361 | |
1362 | free(svg->shape); |
1363 | |
1364 | free(svg); |
1365 | } |