]> gitweb.michael.orlitzky.com - libsvgtiny.git/blobdiff - src/svgtiny.c
Parse: Make the parse state have two sets of gradient details.
[libsvgtiny.git] / src / svgtiny.c
index f02f3d6c09dffbc2460e1720aa3721cee1d56371..efac78882da2185d0cfea1b13a880f994bc72d83 100644 (file)
@@ -54,54 +54,74 @@ static void svgtiny_parse_transform_attributes(dom_element *node,
 static svgtiny_code svgtiny_add_path(float *p, unsigned int n,
                struct svgtiny_parse_state *state);
 static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
+               struct svgtiny_parse_state_gradient *grad,
                struct svgtiny_parse_state *state);
 
 /**
- * Set the local externally-stored parts of a parse state.
- * Call this in functions that made a new state on the stack.
- * Doesn't make own copy of global state, such as the interned string list.
+ * Call this to ref the strings in a gradient state.
  */
-static void svgtiny_setup_state_local(struct svgtiny_parse_state *state)
+static void svgtiny_grad_string_ref(struct svgtiny_parse_state_gradient *grad)
 {
-       if (state->gradient_x1 != NULL) {
-               dom_string_ref(state->gradient_x1);
+       if (grad->gradient_x1 != NULL) {
+               dom_string_ref(grad->gradient_x1);
        }
-       if (state->gradient_y1 != NULL) {
-               dom_string_ref(state->gradient_y1);
+       if (grad->gradient_y1 != NULL) {
+               dom_string_ref(grad->gradient_y1);
        }
-       if (state->gradient_x2 != NULL) {
-               dom_string_ref(state->gradient_x2);
+       if (grad->gradient_x2 != NULL) {
+               dom_string_ref(grad->gradient_x2);
        }
-       if (state->gradient_y2 != NULL) {
-               dom_string_ref(state->gradient_y2);
+       if (grad->gradient_y2 != NULL) {
+               dom_string_ref(grad->gradient_y2);
        }
 }
 
 /**
- * Cleanup the local externally-stored parts of a parse state.
- * Call this in functions that made a new state on the stack.
- * Doesn't cleanup global state, such as the interned string list.
+ * Call this to clean up the strings in a gradient state.
  */
-static void svgtiny_cleanup_state_local(struct svgtiny_parse_state *state)
+static void svgtiny_grad_string_cleanup(
+               struct svgtiny_parse_state_gradient *grad)
 {
-       if (state->gradient_x1 != NULL) {
-               dom_string_unref(state->gradient_x1);
-               state->gradient_x1 = NULL;
+       if (grad->gradient_x1 != NULL) {
+               dom_string_unref(grad->gradient_x1);
+               grad->gradient_x1 = NULL;
        }
-       if (state->gradient_y1 != NULL) {
-               dom_string_unref(state->gradient_y1);
-               state->gradient_y1 = NULL;
+       if (grad->gradient_y1 != NULL) {
+               dom_string_unref(grad->gradient_y1);
+               grad->gradient_y1 = NULL;
        }
-       if (state->gradient_x2 != NULL) {
-               dom_string_unref(state->gradient_x2);
-               state->gradient_x2 = NULL;
+       if (grad->gradient_x2 != NULL) {
+               dom_string_unref(grad->gradient_x2);
+               grad->gradient_x2 = NULL;
        }
-       if (state->gradient_y2 != NULL) {
-               dom_string_unref(state->gradient_y2);
-               state->gradient_y2 = NULL;
+       if (grad->gradient_y2 != NULL) {
+               dom_string_unref(grad->gradient_y2);
+               grad->gradient_y2 = NULL;
        }
 }
 
+/**
+ * Set the local externally-stored parts of a parse state.
+ * Call this in functions that made a new state on the stack.
+ * Doesn't make own copy of global state, such as the interned string list.
+ */
+static void svgtiny_setup_state_local(struct svgtiny_parse_state *state)
+{
+       svgtiny_grad_string_ref(&(state->fill_grad));
+       svgtiny_grad_string_ref(&(state->stroke_grad));
+}
+
+/**
+ * Cleanup the local externally-stored parts of a parse state.
+ * Call this in functions that made a new state on the stack.
+ * Doesn't cleanup global state, such as the interned string list.
+ */
+static void svgtiny_cleanup_state_local(struct svgtiny_parse_state *state)
+{
+       svgtiny_grad_string_cleanup(&(state->fill_grad));
+       svgtiny_grad_string_cleanup(&(state->stroke_grad));
+}
+
 
 /**
  * Create a new svgtiny_diagram structure.
@@ -152,11 +172,6 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
 
        UNUSED(url);
 
-       state.gradient_x1 = NULL;
-       state.gradient_y1 = NULL;
-       state.gradient_x2 = NULL;
-       state.gradient_y2 = NULL;
-
        parser = dom_xml_parser_create(NULL, NULL,
                                       ignore_msg, NULL, &document);
 
@@ -188,6 +203,12 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
                dom_node_unref(document);
                return svgtiny_LIBDOM_ERROR;
        }
+        if (svg == NULL) {
+                /* no root svg element */
+                dom_node_unref(document);
+               return svgtiny_SVG_ERROR;
+        }
+
        exc = dom_node_get_node_name(svg, &svg_name);
        if (exc != DOM_NO_ERR) {
                dom_node_unref(svg);
@@ -247,7 +268,6 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
        state.fill = 0x000000;
        state.stroke = svgtiny_TRANSPARENT;
        state.stroke_width = 1;
-       state.linear_gradient_stop_count = 0;
 
        /* parse tree */
        code = svgtiny_parse_svg(svg, state);
@@ -402,7 +422,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
        dom_string *path_d_str;
        dom_exception exc;
        char *s, *path_d;
-       float *p;
+       float *p; /* path elemets */
+        unsigned int palloc; /* number of path elements allocated */
        unsigned int i;
        float last_x = 0, last_y = 0;
        float last_cubic_x = 0, last_cubic_y = 0;
@@ -430,16 +451,31 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                return svgtiny_SVG_ERROR;
        }
 
-       s = path_d = strndup(dom_string_data(path_d_str),
-                            dom_string_byte_length(path_d_str));
+        /* empty path is permitted it just disables the path */
+        palloc = dom_string_byte_length(path_d_str);
+        if (palloc == 0) {
+               svgtiny_cleanup_state_local(&state);
+               return svgtiny_OK;
+        }
+
+        /* local copy of the path data allowing in-place modification */
+       s = path_d = strndup(dom_string_data(path_d_str), palloc);
        dom_string_unref(path_d_str);
        if (s == NULL) {
                svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
        }
-       /* allocate space for path: it will never have more elements than d */
-       p = malloc(sizeof p[0] * strlen(s));
-       if (!p) {
+
+        /* ensure path element allocation is sensibly bounded */
+        if (palloc < 8) {
+            palloc = 8;
+        } else if (palloc > 64) {
+            palloc = palloc / 2;
+        }
+
+       /* allocate initial space for path elements */
+       p = malloc(sizeof p[0] * palloc);
+       if (p == NULL) {
                free(path_d);
                svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
@@ -456,6 +492,24 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                float x, y, x1, y1, x2, y2, rx, ry, rotation, large_arc, sweep;
                int n;
 
+                /* Ensure there is sufficient space for path elements */
+#define ALLOC_PATH_ELEMENTS(NUM_ELEMENTS)                               \
+                do {                                                    \
+                        if ((palloc - i) < NUM_ELEMENTS) {              \
+                                float *tp;                              \
+                                palloc = (palloc * 2) + (palloc / 2);   \
+                                tp = realloc(p, sizeof p[0] * palloc);  \
+                                if (tp == NULL) {                       \
+                                        free(p);                        \
+                                        free(path_d);                   \
+                                        svgtiny_cleanup_state_local(&state); \
+                                        return svgtiny_OUT_OF_MEMORY;   \
+                                }                                       \
+                                p = tp;                                 \
+                        }                                               \
+                } while(0)
+
+
                /* moveto (M, m), lineto (L, l) (2 arguments) */
                if (sscanf(s, " %1[MmLl] %f %f %n", command, &x, &y, &n) == 3) {
                        /*LOG(("moveto or lineto"));*/
@@ -464,6 +518,7 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                        else
                                plot_command = svgtiny_PATH_LINE;
                        do {
+                                ALLOC_PATH_ELEMENTS(3);
                                p[i++] = plot_command;
                                if ('a' <= *command) {
                                        x += last_x;
@@ -484,6 +539,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                /* closepath (Z, z) (no arguments) */
                } else if (sscanf(s, " %1[Zz] %n", command, &n) == 1) {
                        /*LOG(("closepath"));*/
+                        ALLOC_PATH_ELEMENTS(1);
+
                        p[i++] = svgtiny_PATH_CLOSE;
                        s += n;
                        last_cubic_x = last_quad_x = last_x = subpath_first_x;
@@ -493,6 +550,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                } else if (sscanf(s, " %1[Hh] %f %n", command, &x, &n) == 2) {
                        /*LOG(("horizontal lineto"));*/
                        do {
+                                ALLOC_PATH_ELEMENTS(3);
+
                                p[i++] = svgtiny_PATH_LINE;
                                if (*command == 'h')
                                        x += last_x;
@@ -506,6 +565,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                } else if (sscanf(s, " %1[Vv] %f %n", command, &y, &n) == 2) {
                        /*LOG(("vertical lineto"));*/
                        do {
+                                ALLOC_PATH_ELEMENTS(3);
+
                                p[i++] = svgtiny_PATH_LINE;
                                if (*command == 'v')
                                        y += last_y;
@@ -520,6 +581,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                                &x1, &y1, &x2, &y2, &x, &y, &n) == 7) {
                        /*LOG(("curveto"));*/
                        do {
+                                ALLOC_PATH_ELEMENTS(7);
+
                                p[i++] = svgtiny_PATH_BEZIER;
                                if (*command == 'c') {
                                        x1 += last_x;
@@ -544,6 +607,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                                &x2, &y2, &x, &y, &n) == 5) {
                        /*LOG(("shorthand/smooth curveto"));*/
                        do {
+                                ALLOC_PATH_ELEMENTS(7);
+
                                p[i++] = svgtiny_PATH_BEZIER;
                                x1 = last_x + (last_x - last_cubic_x);
                                y1 = last_y + (last_y - last_cubic_y);
@@ -568,6 +633,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                                &x1, &y1, &x, &y, &n) == 5) {
                        /*LOG(("quadratic Bezier curveto"));*/
                        do {
+                                ALLOC_PATH_ELEMENTS(7);
+
                                p[i++] = svgtiny_PATH_BEZIER;
                                last_quad_x = x1;
                                last_quad_y = y1;
@@ -593,6 +660,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                                &x, &y, &n) == 3) {
                        /*LOG(("shorthand/smooth quadratic Bezier curveto"));*/
                        do {
+                                ALLOC_PATH_ELEMENTS(7);
+
                                p[i++] = svgtiny_PATH_BEZIER;
                                x1 = last_x + (last_x - last_quad_x);
                                y1 = last_y + (last_y - last_quad_y);
@@ -619,6 +688,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                                &rx, &ry, &rotation, &large_arc, &sweep,
                                &x, &y, &n) == 8) {
                        do {
+                                ALLOC_PATH_ELEMENTS(3);
+
                                p[i++] = svgtiny_PATH_LINE;
                                if (*command == 'a') {
                                        x += last_x;
@@ -648,6 +719,19 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
                return svgtiny_OK;
        }
 
+        /* resize path element array to not be over allocated */
+        if (palloc != i) {
+                float *tp;
+
+                /* try the resize, if it fails just continue to use the old
+                 * allocation
+                 */
+                tp = realloc(p, sizeof p[0] * i);
+                if (tp != NULL) {
+                        p = tp;
+                }
+        }
+
        err = svgtiny_add_path(p, i, &state);
 
        svgtiny_cleanup_state_local(&state);
@@ -1300,13 +1384,13 @@ void svgtiny_parse_paint_attributes(dom_element *node,
        
        exc = dom_element_get_attribute(node, state->interned_fill, &attr);
        if (exc == DOM_NO_ERR && attr != NULL) {
-               svgtiny_parse_color(attr, &state->fill, state);
+               svgtiny_parse_color(attr, &state->fill, &state->fill_grad, state);
                dom_string_unref(attr);
        }
 
        exc = dom_element_get_attribute(node, state->interned_stroke, &attr);
        if (exc == DOM_NO_ERR && attr != NULL) {
-               svgtiny_parse_color(attr, &state->stroke, state);
+               svgtiny_parse_color(attr, &state->stroke, &state->stroke_grad, state);
                dom_string_unref(attr);
        }
 
@@ -1328,7 +1412,7 @@ void svgtiny_parse_paint_attributes(dom_element *node,
                        while (*s == ' ')
                                s++;
                        value = strndup(s, strcspn(s, "; "));
-                       _svgtiny_parse_color(value, &state->fill, state);
+                       _svgtiny_parse_color(value, &state->fill, &state->fill_grad, state);
                        free(value);
                }
                if ((s = strstr(style, "stroke:"))) {
@@ -1336,7 +1420,7 @@ void svgtiny_parse_paint_attributes(dom_element *node,
                        while (*s == ' ')
                                s++;
                        value = strndup(s, strcspn(s, "; "));
-                       _svgtiny_parse_color(value, &state->stroke, state);
+                       _svgtiny_parse_color(value, &state->stroke, &state->stroke_grad, state);
                        free(value);
                }
                if ((s = strstr(style, "stroke-width:"))) {
@@ -1359,6 +1443,7 @@ void svgtiny_parse_paint_attributes(dom_element *node,
  */
 
 static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
+               struct svgtiny_parse_state_gradient *grad,
                struct svgtiny_parse_state *state)
 {
        unsigned int r, g, b;
@@ -1390,19 +1475,21 @@ static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
 
        } else if (5 < len && s[0] == 'u' && s[1] == 'r' && s[2] == 'l' &&
                        s[3] == '(') {
-               if (s[4] == '#') {
+               if (grad == NULL) {
+                       *c = svgtiny_RGB(0, 0, 0);
+               } else if (s[4] == '#') {
                        id = strdup(s + 5);
                        if (!id)
                                return;
                        rparen = strchr(id, ')');
                        if (rparen)
                                *rparen = 0;
-                       svgtiny_find_gradient(id, state);
+                       svgtiny_find_gradient(id, grad, state);
                        free(id);
-                       if (state->linear_gradient_stop_count == 0)
+                       if (grad->linear_gradient_stop_count == 0)
                                *c = svgtiny_TRANSPARENT;
-                       else if (state->linear_gradient_stop_count == 1)
-                               *c = state->gradient_stop[0].color;
+                       else if (grad->linear_gradient_stop_count == 1)
+                               *c = grad->gradient_stop[0].color;
                        else
                                *c = svgtiny_LINEAR_GRADIENT;
                }
@@ -1416,11 +1503,12 @@ static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
 }
 
 void svgtiny_parse_color(dom_string *s, svgtiny_colour *c,
+               struct svgtiny_parse_state_gradient *grad,
                struct svgtiny_parse_state *state)
 {
-       char *ss = strndup(dom_string_data(s), dom_string_byte_length(s));
-       _svgtiny_parse_color(ss, c, state);
-       free(ss);
+       dom_string_ref(s);
+       _svgtiny_parse_color(dom_string_data(s), c, grad, state);
+       dom_string_unref(s);
 }
 
 /**