| 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); | |||
| 192 | ||||
| 193 | fprintf(stderrstderr, "%"PRIu32"u"": ", severity); | |||
| 194 | vfprintf(stderrstderr, msg, l); | |||
| 195 | fprintf(stderrstderr, "\n"); | |||
| 196 | ||||
| 197 | 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); | |||
| ||||
| 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 | } |