From 40cd0e22d680c93a8015f50968942d0cf118e5c5 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sat, 15 Jun 2024 15:41:07 +0100 Subject: [PATCH] Cleanup length parsing remove the incorrect use of atof and use an API that does not require the string to be copied to terminate with a null --- src/svgtiny.c | 146 ++++++++++++++++++++--------------------- src/svgtiny_gradient.c | 86 ++++++++++++++---------- src/svgtiny_internal.h | 4 +- src/svgtiny_parse.c | 76 +++++++++++++++++++++ 4 files changed, 202 insertions(+), 110 deletions(-) diff --git a/src/svgtiny.c b/src/svgtiny.c index a450985..61909a2 100644 --- a/src/svgtiny.c +++ b/src/svgtiny.c @@ -1294,7 +1294,10 @@ svgtiny_code svgtiny_parse_circle(dom_element *circle, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - x = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + &x); } dom_string_unref(attr); @@ -1304,7 +1307,10 @@ svgtiny_code svgtiny_parse_circle(dom_element *circle, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - y = svgtiny_parse_length(attr, state.viewport_height, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_height, + &y); } dom_string_unref(attr); @@ -1314,7 +1320,10 @@ svgtiny_code svgtiny_parse_circle(dom_element *circle, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - r = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + &r); } dom_string_unref(attr); @@ -1400,7 +1409,10 @@ svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - x = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + &x); } dom_string_unref(attr); @@ -1410,7 +1422,10 @@ svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - y = svgtiny_parse_length(attr, state.viewport_height, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_height, + &y); } dom_string_unref(attr); @@ -1420,7 +1435,10 @@ svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - rx = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + &rx); } dom_string_unref(attr); @@ -1430,7 +1448,10 @@ svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - ry = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + &ry); } dom_string_unref(attr); @@ -1517,7 +1538,10 @@ svgtiny_code svgtiny_parse_line(dom_element *line, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - x1 = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + &x1); } dom_string_unref(attr); @@ -1527,7 +1551,10 @@ svgtiny_code svgtiny_parse_line(dom_element *line, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - y1 = svgtiny_parse_length(attr, state.viewport_height, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_height, + &y1); } dom_string_unref(attr); @@ -1537,7 +1564,10 @@ svgtiny_code svgtiny_parse_line(dom_element *line, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - x2 = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + &x2); } dom_string_unref(attr); @@ -1547,7 +1577,10 @@ svgtiny_code svgtiny_parse_line(dom_element *line, return svgtiny_LIBDOM_ERROR; } if (attr != NULL) { - y2 = svgtiny_parse_length(attr, state.viewport_height, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_height, + &y2); } dom_string_unref(attr); @@ -1769,84 +1802,45 @@ void svgtiny_parse_position_attributes(dom_element *node, exc = dom_element_get_attribute(node, state.interned_x, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - *x = svgtiny_parse_length(attr, state.viewport_width, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + x); dom_string_unref(attr); } exc = dom_element_get_attribute(node, state.interned_y, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - *y = svgtiny_parse_length(attr, state.viewport_height, state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_height, + y); dom_string_unref(attr); } exc = dom_element_get_attribute(node, state.interned_width, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - *width = svgtiny_parse_length(attr, state.viewport_width, - state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_width, + width); dom_string_unref(attr); } exc = dom_element_get_attribute(node, state.interned_height, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - *height = svgtiny_parse_length(attr, state.viewport_height, - state); + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state.viewport_height, + height); dom_string_unref(attr); } } -/** - * Parse a length as a number of pixels. - */ - -static float _svgtiny_parse_length(const char *s, int viewport_size, - const struct svgtiny_parse_state state) -{ - int num_length = strspn(s, "0123456789+-."); - const char *unit = s + num_length; - float n = atof((const char *) s); - float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/ - - UNUSED(state); - - if (unit[0] == 0) { - return n; - } else if (unit[0] == '%') { - return n / 100.0 * viewport_size; - } else if (unit[0] == 'e' && unit[1] == 'm') { - return n * font_size; - } else if (unit[0] == 'e' && unit[1] == 'x') { - return n / 2.0 * font_size; - } else if (unit[0] == 'p' && unit[1] == 'x') { - return n; - } else if (unit[0] == 'p' && unit[1] == 't') { - return n * 1.25; - } else if (unit[0] == 'p' && unit[1] == 'c') { - return n * 15.0; - } else if (unit[0] == 'm' && unit[1] == 'm') { - return n * 3.543307; - } else if (unit[0] == 'c' && unit[1] == 'm') { - return n * 35.43307; - } else if (unit[0] == 'i' && unit[1] == 'n') { - return n * 90; - } - - return 0; -} - -float svgtiny_parse_length(dom_string *s, int viewport_size, - const struct svgtiny_parse_state state) -{ - char *ss = strndup(dom_string_data(s), dom_string_byte_length(s)); - float ret = _svgtiny_parse_length(ss, viewport_size, state); - free(ss); - return ret; -} - /** * Parse paint attributes, if present. */ - void svgtiny_parse_paint_attributes(dom_element *node, struct svgtiny_parse_state *state) { @@ -1867,8 +1861,12 @@ void svgtiny_parse_paint_attributes(dom_element *node, exc = dom_element_get_attribute(node, state->interned_stroke_width, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - state->stroke_width = svgtiny_parse_length(attr, - state->viewport_width, *state); + float stroke_width; + svgtiny_parse_length(dom_string_data(attr), + dom_string_byte_length(attr), + state->viewport_width, + &stroke_width); + state->stroke_width = stroke_width; dom_string_unref(attr); } @@ -1896,12 +1894,12 @@ void svgtiny_parse_paint_attributes(dom_element *node, } if ((s = strstr(style, "stroke-width:"))) { s += 13; - while (*s == ' ') - s++; - value = strndup(s, strcspn(s, "; ")); - state->stroke_width = _svgtiny_parse_length(value, - state->viewport_width, *state); - free(value); + float stroke_width; + svgtiny_parse_length(s, + strcspn(s, "; "), + state->viewport_width, + &stroke_width); + state->stroke_width = stroke_width; } free(style); dom_string_unref(attr); diff --git a/src/svgtiny_gradient.c b/src/svgtiny_gradient.c index 4b327dd..e7b1222 100644 --- a/src/svgtiny_gradient.c +++ b/src/svgtiny_gradient.c @@ -61,12 +61,12 @@ void svgtiny_find_gradient(const char *id, grad->gradient_transform.d = 1; grad->gradient_transform.e = 0; grad->gradient_transform.f = 0; - + exc = dom_string_create_interned((const uint8_t *) id, strlen(id), &id_str); if (exc != DOM_NO_ERR) return; - + exc = dom_document_get_element_by_id(state->document, id_str, &gradient); dom_string_unref(id_str); @@ -76,16 +76,16 @@ void svgtiny_find_gradient(const char *id, #endif return; } - + exc = dom_node_get_node_name(gradient, &name); if (exc != DOM_NO_ERR) { dom_node_unref(gradient); return; } - + if (dom_string_isequal(name, state->interned_linearGradient)) svgtiny_parse_linear_gradient(gradient, grad, state); - + dom_node_unref(gradient); dom_string_unref(name); @@ -110,7 +110,7 @@ svgtiny_code svgtiny_parse_linear_gradient(dom_element *linear, dom_string *attr; dom_exception exc; dom_nodelist *stops; - + exc = dom_element_get_attribute(linear, state->interned_href, &attr); if (exc == DOM_NO_ERR && attr != NULL) { if (dom_string_data(attr)[0] == (uint8_t) '#') { @@ -149,16 +149,16 @@ svgtiny_code svgtiny_parse_linear_gradient(dom_element *linear, grad->gradient_y2 = attr; attr = NULL; } - + exc = dom_element_get_attribute(linear, state->interned_gradientUnits, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - grad->gradient_user_space_on_use = + grad->gradient_user_space_on_use = dom_string_isequal(attr, state->interned_userSpaceOnUse); dom_string_unref(attr); } - + exc = dom_element_get_attribute(linear, state->interned_gradientTransform, &attr); @@ -184,7 +184,7 @@ svgtiny_code svgtiny_parse_linear_gradient(dom_element *linear, grad->gradient_transform.f = f; dom_string_unref(attr); } - + exc = dom_element_get_elements_by_tag_name(linear, state->interned_stop, &stops); @@ -195,7 +195,7 @@ svgtiny_code svgtiny_parse_linear_gradient(dom_element *linear, dom_nodelist_unref(stops); goto no_more_stops; } - + for (stopnr = 0; stopnr < listlen; ++stopnr) { dom_element *stop; float offset = -1; @@ -261,7 +261,7 @@ svgtiny_code svgtiny_parse_linear_gradient(dom_element *linear, if (i == svgtiny_MAX_STOPS) break; } - + dom_nodelist_unref(stops); } no_more_stops: @@ -336,27 +336,46 @@ svgtiny_code svgtiny_add_path_linear_gradient(float *p, unsigned int n, #endif if (!grad->gradient_user_space_on_use) { - gradient_x0 = object_x0 + - svgtiny_parse_length(grad->gradient_x1, - object_x1 - object_x0, *state); - gradient_y0 = object_y0 + - svgtiny_parse_length(grad->gradient_y1, - object_y1 - object_y0, *state); - gradient_x1 = object_x0 + - svgtiny_parse_length(grad->gradient_x2, - object_x1 - object_x0, *state); - gradient_y1 = object_y0 + - svgtiny_parse_length(grad->gradient_y2, - object_y1 - object_y0, *state); + svgtiny_parse_length(dom_string_data(grad->gradient_x1), + dom_string_byte_length(grad->gradient_x1), + object_x1 - object_x0, + &gradient_x0); + gradient_x0 += object_x0; + + svgtiny_parse_length(dom_string_data(grad->gradient_y1), + dom_string_byte_length(grad->gradient_y1), + object_y1 - object_y0, + &gradient_y0); + gradient_y0 += object_y0; + + svgtiny_parse_length(dom_string_data(grad->gradient_x2), + dom_string_byte_length(grad->gradient_x2), + object_x1 - object_x0, + &gradient_x1); + gradient_x1 += object_x0; + + svgtiny_parse_length(dom_string_data(grad->gradient_y2), + dom_string_byte_length(grad->gradient_y2), + object_y1 - object_y0, + &gradient_y1); + gradient_y1 += object_y0; } else { - gradient_x0 = svgtiny_parse_length(grad->gradient_x1, - state->viewport_width, *state); - gradient_y0 = svgtiny_parse_length(grad->gradient_y1, - state->viewport_height, *state); - gradient_x1 = svgtiny_parse_length(grad->gradient_x2, - state->viewport_width, *state); - gradient_y1 = svgtiny_parse_length(grad->gradient_y2, - state->viewport_height, *state); + svgtiny_parse_length(dom_string_data(grad->gradient_x1), + dom_string_byte_length(grad->gradient_x1), + state->viewport_width, + &gradient_x0); + svgtiny_parse_length(dom_string_data(grad->gradient_y1), + dom_string_byte_length(grad->gradient_y1), + state->viewport_height, + &gradient_y0); + svgtiny_parse_length(dom_string_data(grad->gradient_x2), + dom_string_byte_length(grad->gradient_x2), + state->viewport_width, + &gradient_x1); + svgtiny_parse_length(dom_string_data(grad->gradient_y2), + dom_string_byte_length(grad->gradient_y2), + state->viewport_height, + &gradient_y1); } gradient_dx = gradient_x1 - gradient_x0; gradient_dy = gradient_y1 - gradient_y0; @@ -559,7 +578,7 @@ svgtiny_code svgtiny_add_path_linear_gradient(float *p, unsigned int n, return svgtiny_OK; } - + /* render triangles */ stop_count = grad->linear_gradient_stop_count; assert(2 <= stop_count); @@ -780,4 +799,3 @@ void svgtiny_invert_matrix(float *m, float *inv) inv[4] = (m[2]*m[5] - m[3]*m[4]) / determinant; inv[5] = (m[1]*m[4] - m[0]*m[5]) / determinant; } - diff --git a/src/svgtiny_internal.h b/src/svgtiny_internal.h index c04ae66..1f01f1f 100644 --- a/src/svgtiny_internal.h +++ b/src/svgtiny_internal.h @@ -67,8 +67,6 @@ struct svgtiny_parse_state { struct svgtiny_list; /* svgtiny.c */ -float svgtiny_parse_length(dom_string *s, int viewport_size, - const struct svgtiny_parse_state state); void svgtiny_parse_color(dom_string *s, svgtiny_colour *c, struct svgtiny_parse_state_gradient *grad, struct svgtiny_parse_state *state); @@ -88,6 +86,8 @@ char *svgtiny_strndup(const char *s, size_t n); /* svgtiny_parse.c */ svgtiny_code svgtiny_parse_poly_points(const char *data, size_t datalen, float *pointv, unsigned int *pointc); +svgtiny_code svgtiny_parse_length(const char *text, size_t textlen, + int viewport_size, float *length); /* svgtiny_gradient.c */ void svgtiny_find_gradient(const char *id, diff --git a/src/svgtiny_parse.c b/src/svgtiny_parse.c index 890ee1b..49cbf42 100644 --- a/src/svgtiny_parse.c +++ b/src/svgtiny_parse.c @@ -343,3 +343,79 @@ svgtiny_parse_poly_points(const char *text, return svgtiny_OK; } + + +/** + * Parse a length as a number of pixels. + */ +svgtiny_code +svgtiny_parse_length(const char *text, + size_t textlen, + int viewport_size, + float *length) +{ + svgtiny_code err; + float number; + const char *unit = NULL; + int unitlen; + float font_size = 20; /*css_len2px(&state.style.font_size.value.length, 0);*/ + + err = svgtiny_parse_number(text, textlen, &unit, &number); + if (err != svgtiny_OK) { + unitlen = -1; + } else { + unitlen = (text + textlen) - unit; + } + + /* discount whitespace on the end of the unit */ + while(unitlen > 0) { + if ((unit[unitlen - 1] != 0x20) && + (unit[unitlen - 1] != 0x09) && + (unit[unitlen - 1] != 0x0A) && + (unit[unitlen - 1] != 0x0D)) { + break; + } + unitlen--; + } + + /* decode the unit */ + *length = 0; + switch (unitlen) { + case 0: + /* no unit, assume pixels */ + *length = number; + break; + case 1: + if (unit[0] == '%') { + /* percentage of viewport */ + *length = number / 100.0 * viewport_size; + } + break; + + case 2: + if (unit[0] == 'e' && unit[1] == 'm') { + *length = number * font_size; + } else if (unit[0] == 'e' && unit[1] == 'x') { + *length = number / 2.0 * font_size; + } else if (unit[0] == 'p' && unit[1] == 'x') { + *length = number; + } else if (unit[0] == 'p' && unit[1] == 't') { + *length = number * 1.25; + } else if (unit[0] == 'p' && unit[1] == 'c') { + *length = number * 15.0; + } else if (unit[0] == 'm' && unit[1] == 'm') { + *length = number * 3.543307; + } else if (unit[0] == 'c' && unit[1] == 'm') { + *length = number * 35.43307; + } else if (unit[0] == 'i' && unit[1] == 'n') { + *length = number * 90; + } + break; + + default: + /* unknown unit */ + break; + } + + return svgtiny_OK; +} -- 2.49.0