From b3590b5856f55757525ea50f67d5a7d72e964d7a Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Wed, 3 Jul 2024 17:16:07 +0100 Subject: [PATCH] Reimplement style processing --- src/svgtiny.c | 54 ++++++++--------- src/svgtiny_gradient.c | 35 +++++------ src/svgtiny_internal.h | 21 +++++++ src/svgtiny_parse.c | 135 +++++++++++++++++++++++++++++++++++++++++ test/data/bad-grad.mvg | 42 +++++++++++++ test/data/bad-grad.svg | 2 +- 6 files changed, 238 insertions(+), 51 deletions(-) create mode 100644 test/data/bad-grad.mvg diff --git a/src/svgtiny.c b/src/svgtiny.c index 3813d7c..2e3d704 100644 --- a/src/svgtiny.c +++ b/src/svgtiny.c @@ -1870,37 +1870,33 @@ void svgtiny_parse_paint_attributes(dom_element *node, dom_string_unref(attr); } + /* style attribute */ exc = dom_element_get_attribute(node, state->interned_style, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - char *style = strndup(dom_string_data(attr), - dom_string_byte_length(attr)); - const char *s; - if ((s = strstr(style, "fill:"))) { - s += 5; - svgtiny_parse_paint(s, - strcspn(s, "; "), - &state->fill_grad, - state, - &state->fill); - } - if ((s = strstr(style, "stroke:"))) { - s += 7; - svgtiny_parse_paint(s, - strcspn(s, "; "), - &state->stroke_grad, - state, - &state->stroke); - } - if ((s = strstr(style, "stroke-width:"))) { - float stroke_width; - s += 13; - svgtiny_parse_length(s, - strcspn(s, "; "), - state->viewport_width, - &stroke_width); - state->stroke_width = stroke_width; - } - free(style); + struct svgtiny_parse_inline_style_op styles[]={ + { + state->interned_fill, + ISTYLEOP_PAINT, + &state->fill_grad, + &state->fill + }, { + state->interned_stroke, + ISTYLEOP_PAINT, + &state->stroke_grad, + &state->stroke + }, { + state->interned_stroke_width, + ISTYLEOP_INTLENGTH, + &state->viewport_width, + &state->stroke_width + },{ + NULL, ISTYLEOP_NONE, NULL, NULL + }, + }; + svgtiny_parse_inline_style(dom_string_data(attr), + dom_string_byte_length(attr), + state, + styles); dom_string_unref(attr); } } diff --git a/src/svgtiny_gradient.c b/src/svgtiny_gradient.c index b6b2084..ed20985 100644 --- a/src/svgtiny_gradient.c +++ b/src/svgtiny_gradient.c @@ -227,27 +227,20 @@ svgtiny_code svgtiny_parse_linear_gradient(dom_element *linear, state->interned_style, &attr); if (exc == DOM_NO_ERR && attr != NULL) { - char *content = strndup(dom_string_data(attr), - dom_string_byte_length(attr)); - const char *s; - dom_string *value; - if ((s = strstr(content, "stop-color:"))) { - s += 11; - while (*s == ' ') - s++; - exc = dom_string_create_interned( - (const uint8_t *) s, - strcspn(s, "; "), - &value); - if (exc == DOM_NO_ERR && - value != NULL) { - svgtiny_parse_color(dom_string_data(value), - dom_string_byte_length(value), - &color); - dom_string_unref(value); - } - } - free(content); + struct svgtiny_parse_inline_style_op styles[]={ + { + state->interned_stop_color, + ISTYLEOP_COLOR, + NULL, + &color + },{ + NULL, ISTYLEOP_NONE, NULL, NULL + }, + }; + svgtiny_parse_inline_style(dom_string_data(attr), + dom_string_byte_length(attr), + state, + styles); dom_string_unref(attr); } if (offset != -1 && color != svgtiny_TRANSPARENT) { diff --git a/src/svgtiny_internal.h b/src/svgtiny_internal.h index 519f65f..20680fb 100644 --- a/src/svgtiny_internal.h +++ b/src/svgtiny_internal.h @@ -73,6 +73,24 @@ struct svgtiny_parse_state { struct svgtiny_list; +enum svgtiny_parse_inline_style_operation { + ISTYLEOP_NONE, + ISTYLEOP_PAINT, + ISTYLEOP_COLOR, + ISTYLEOP_LENGTH, + ISTYLEOP_INTLENGTH, +}; + +/** + * control structure for inline style parse + */ +struct svgtiny_parse_inline_style_op { + dom_string *key; + enum svgtiny_parse_inline_style_operation operation; + void *param; + void *value; +}; + /* svgtiny.c */ struct svgtiny_shape *svgtiny_add_shape(struct svgtiny_parse_state *state); void svgtiny_transform_path(float *p, unsigned int n, @@ -101,6 +119,9 @@ svgtiny_code svgtiny_parse_color(const char *text, size_t textlen, svgtiny_code svgtiny_parse_viewbox(const char *text, size_t textlen, float viewport_width, float viewport_height, struct svgtiny_transformation_matrix *tm); +svgtiny_code svgtiny_parse_inline_style(const char *text, size_t textlen, + struct svgtiny_parse_state *state, + struct svgtiny_parse_inline_style_op *styles); /* svgtiny_gradient.c */ svgtiny_code svgtiny_find_gradient(const char *id, diff --git a/src/svgtiny_parse.c b/src/svgtiny_parse.c index b459d1d..c488200 100644 --- a/src/svgtiny_parse.c +++ b/src/svgtiny_parse.c @@ -358,6 +358,19 @@ static inline void advance_id(const char **cursor, const char *textend) } } +static inline void advance_property_name(const char **cursor, const char *textend) +{ + while ((*cursor) < textend) { + if (((**cursor < 0x30 /* 0 */) || (**cursor > 0x39 /* 9 */)) && + ((**cursor < 0x41 /* A */) || (**cursor > 0x5A /* Z */)) && + ((**cursor < 0x61 /* a */) || (**cursor > 0x7A /* z */)) && + (**cursor != '-') && + (**cursor != '_')) { + break; + } + (*cursor)++; + } +} enum transform_type { TRANSFORM_UNK, @@ -1265,3 +1278,125 @@ svgtiny_parse_viewbox(const char *text, return svgtiny_OK; } + +/** + * parse a declaration in a style + * + * https://www.w3.org/TR/CSS21/syndata.html#declaration + * + * \param declaration The declaration without any preceeding space + * \param end The end of the declaration string + * \param state parse state to pass on + * \param styleops The table of style operations to apply + * + * declaration is " : " + */ +static inline svgtiny_code +parse_declaration(const char *declaration, + const char *end, + struct svgtiny_parse_state *state, + struct svgtiny_parse_inline_style_op *styleops) +{ + const char *cursor = declaration; /* text cursor */ + size_t key_len; /* key length */ + struct svgtiny_parse_inline_style_op *styleop; + + /* declaration must be at least 3 characters long (ie "a:b") */ + if ((end - declaration) < 3) { + return svgtiny_SVG_ERROR; + } + + /* find end of key */ + advance_property_name(&cursor, end); + + if ((cursor - declaration) < 1) { + /* no key */ + return svgtiny_SVG_ERROR; + } + + key_len = cursor - declaration; + + advance_whitespace(&cursor, end); + + if ((cursor >= end) || (*cursor != ':')) { + /* no colon */ + return svgtiny_SVG_ERROR; + } + cursor++; /* advance over colon */ + + advance_whitespace(&cursor, end); + + /* search style operations for a match */ + for (styleop = styleops; styleop->key != NULL; styleop++) { + if ((dom_string_byte_length(styleop->key) == key_len) && + (memcmp(declaration, dom_string_data(styleop->key), key_len) == 0)) { + float parse_len; + + switch (styleop->operation) { + case ISTYLEOP_PAINT: + svgtiny_parse_paint(cursor, + end - cursor, + styleop->param, + state, + styleop->value); + break; + + case ISTYLEOP_COLOR: + svgtiny_parse_color(cursor, + end - cursor, + styleop->value); + break; + + case ISTYLEOP_LENGTH: + svgtiny_parse_length(cursor, + end - cursor, + *((int *)styleop->param), + styleop->value); + break; + + case ISTYLEOP_INTLENGTH: + svgtiny_parse_length(cursor, + end - cursor, + *((int *)styleop->param), + &parse_len); + *((int *)styleop->value) = parse_len; + break; + + default: + break; + } + break; /* found the operation, stop iterating */ + } + } + + return svgtiny_OK; +} + +/** + * parse an inline style + */ +svgtiny_code +svgtiny_parse_inline_style(const char *text, + size_t textlen, + struct svgtiny_parse_state *state, + struct svgtiny_parse_inline_style_op *styles) +{ + const char *cursor = text; /* text cursor */ + const char *textend = text + textlen; + const char *declaration_start; + + while (cursor < textend) { + advance_whitespace(&cursor, textend); + declaration_start = cursor; + while (cursor < textend) { + if ((*cursor == ';') && + (*(cursor - 1) != '\\')) { + break; + } + cursor++; + } + parse_declaration(declaration_start, cursor, state, styles); + cursor++;/* skip semicolon */ + } + return svgtiny_OK; +} diff --git a/test/data/bad-grad.mvg b/test/data/bad-grad.mvg new file mode 100644 index 0000000..dedfdec --- /dev/null +++ b/test/data/bad-grad.mvg @@ -0,0 +1,42 @@ +viewbox 0 0 1000 1000 +fill #6598fd stroke none stroke-width 10 path 'M 100 100 L 140 100 L 100 900 Z ' +fill #6497fb stroke none stroke-width 10 path 'M 100 900 L 140 100 L 140 900 Z ' +fill #6395f7 stroke none stroke-width 10 path 'M 140 100 L 180 100 L 140 900 Z ' +fill #6394f5 stroke none stroke-width 10 path 'M 140 900 L 180 100 L 180 900 Z ' +fill #6293f1 stroke none stroke-width 10 path 'M 180 100 L 220 100 L 180 900 Z ' +fill #6192ef stroke none stroke-width 10 path 'M 180 900 L 220 100 L 220 900 Z ' +fill #6090eb stroke none stroke-width 10 path 'M 220 900 L 220 100 L 260 900 Z ' +fill #5f8fe9 stroke none stroke-width 10 path 'M 220 100 L 260 100 L 260 900 Z ' +fill #5e8de5 stroke none stroke-width 10 path 'M 260 900 L 260 100 L 300 900 Z ' +fill #5e8de3 stroke none stroke-width 10 path 'M 260 100 L 300 100 L 300 900 Z ' +fill #5c8bdf stroke none stroke-width 10 path 'M 300 900 L 300 100 L 340 900 Z ' +fill #5c8add stroke none stroke-width 10 path 'M 300 100 L 340 100 L 340 900 Z ' +fill #5b88d9 stroke none stroke-width 10 path 'M 340 900 L 340 100 L 380 900 Z ' +fill #5a88d7 stroke none stroke-width 10 path 'M 340 100 L 380 100 L 380 900 Z ' +fill #5986d3 stroke none stroke-width 10 path 'M 380 900 L 380 100 L 420 900 Z ' +fill #5885d1 stroke none stroke-width 10 path 'M 380 100 L 420 100 L 420 900 Z ' +fill #5783cd stroke none stroke-width 10 path 'M 420 900 L 420 100 L 460 900 Z ' +fill #5782cb stroke none stroke-width 10 path 'M 420 100 L 460 100 L 460 900 Z ' +fill #5681c7 stroke none stroke-width 10 path 'M 460 900 L 460 100 L 500 900 Z ' +fill #5580c5 stroke none stroke-width 10 path 'M 460 100 L 500 100 L 500 900 Z ' +fill #547ec1 stroke none stroke-width 10 path 'M 500 900 L 500 100 L 540 900 Z ' +fill #537dbf stroke none stroke-width 10 path 'M 500 100 L 540 100 L 540 900 Z ' +fill #527cbb stroke none stroke-width 10 path 'M 540 900 L 540 100 L 580 900 Z ' +fill #527bb9 stroke none stroke-width 10 path 'M 540 100 L 580 100 L 580 900 Z ' +fill #5179b5 stroke none stroke-width 10 path 'M 580 900 L 580 100 L 620 900 Z ' +fill #5078b3 stroke none stroke-width 10 path 'M 580 100 L 620 100 L 620 900 Z ' +fill #4f77af stroke none stroke-width 10 path 'M 620 900 L 620 100 L 660 900 Z ' +fill #4e76ad stroke none stroke-width 10 path 'M 620 100 L 660 100 L 660 900 Z ' +fill #4d74a9 stroke none stroke-width 10 path 'M 660 900 L 660 100 L 700 900 Z ' +fill #4d73a7 stroke none stroke-width 10 path 'M 660 100 L 700 100 L 700 900 Z ' +fill #4b71a3 stroke none stroke-width 10 path 'M 700 900 L 700 100 L 740 900 Z ' +fill #4b71a1 stroke none stroke-width 10 path 'M 700 100 L 740 100 L 740 900 Z ' +fill #4a6f9d stroke none stroke-width 10 path 'M 740 900 L 740 100 L 780 900 Z ' +fill #496e9b stroke none stroke-width 10 path 'M 740 100 L 780 100 L 780 900 Z ' +fill #486c97 stroke none stroke-width 10 path 'M 780 900 L 780 100 L 820 900 Z ' +fill #476b95 stroke none stroke-width 10 path 'M 780 100 L 820 100 L 820 900 Z ' +fill #466a91 stroke none stroke-width 10 path 'M 820 900 L 820 100 L 860 900 Z ' +fill #46698f stroke none stroke-width 10 path 'M 820 100 L 860 100 L 860 900 Z ' +fill #45678b stroke none stroke-width 10 path 'M 860 900 L 860 100 L 900 900 Z ' +fill #446689 stroke none stroke-width 10 path 'M 860 100 L 900 100 L 900 900 Z ' +fill none stroke #000000 stroke-width 10 path 'M 100 100 L 900 100 L 900 900 L 100 900 Z ' diff --git a/test/data/bad-grad.svg b/test/data/bad-grad.svg index 4a3d26d..26a7258 100644 --- a/test/data/bad-grad.svg +++ b/test/data/bad-grad.svg @@ -2,7 +2,7 @@ - + -- 2.49.0