]> gitweb.michael.orlitzky.com - libsvgtiny.git/commitdiff
Reimplement style processing
authorVincent Sanders <vince@kyllikki.org>
Wed, 3 Jul 2024 16:16:07 +0000 (17:16 +0100)
committerVincent Sanders <vince@kyllikki.org>
Sun, 7 Jul 2024 21:51:30 +0000 (22:51 +0100)
src/svgtiny.c
src/svgtiny_gradient.c
src/svgtiny_internal.h
src/svgtiny_parse.c
test/data/bad-grad.mvg [new file with mode: 0644]
test/data/bad-grad.svg

index 3813d7c2ce155a359488d0af83f5d83b56561ab0..2e3d7044740fbf96cc7c07d4963007387e948b9b 100644 (file)
@@ -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);
        }
 }
index b6b2084ad625f0fc44719406d74eb640b8906046..ed20985e2a8905cdca958b68774e85725a933894 100644 (file)
@@ -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) {
index 519f65fd275e70656cc5e40cc9c65f2b4f8fc4c1..20680fbefc93c7b5b9f09efbef3dec31ee8a1d80 100644 (file)
@@ -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,
index b459d1d2ea593feb08a5a429a416bac7ecf6e4ff..c4882009b36326a28218f4d5aece44ab998acf9f 100644 (file)
@@ -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 "<property name> : <property value>"
+ */
+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 (file)
index 0000000..dedfdec
--- /dev/null
@@ -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 ' 
index 4a3d26d5de0e8c28bc77ae69f97743e853233fd0..26a725883cfbcb32ebcabacba2908be46aa84c24 100644 (file)
@@ -2,7 +2,7 @@
   <defs>
     <linearGradient id="foo">
       <stop stop-color="#69f" offset="0"/>
-      <stop stop-color="#468" offset="1"/>
+      <stop style="stop-color:#468" offset="100%"/>
     </linearGradient>
   </defs>
   <path fill="url(#foo)" stroke='url(#bar)' d='M10 10 H 90 V 90 H 10 Z' />