]> gitweb.michael.orlitzky.com - libsvgtiny.git/blobdiff - src/svgtiny.c
Fix leak of gradient dom strings.
[libsvgtiny.git] / src / svgtiny.c
index 18884a11f45012ab3ca2940971b2da829d736691..13249b62cdeb45068dfe7deb5b6ba8572ad2c1c2 100644 (file)
@@ -42,12 +42,12 @@ static svgtiny_code svgtiny_parse_poly(dom_element *poly,
                struct svgtiny_parse_state state, bool polygon);
 static svgtiny_code svgtiny_parse_text(dom_element *text,
                struct svgtiny_parse_state state);
-static void svgtiny_parse_position_attributes(const dom_element *node,
+static void svgtiny_parse_position_attributes(dom_element *node,
                const struct svgtiny_parse_state state,
                float *x, float *y, float *width, float *height);
-static void svgtiny_parse_paint_attributes(const dom_element *node,
+static void svgtiny_parse_paint_attributes(dom_element *node,
                struct svgtiny_parse_state *state);
-static void svgtiny_parse_font_attributes(const dom_element *node,
+static void svgtiny_parse_font_attributes(dom_element *node,
                struct svgtiny_parse_state *state);
 static void svgtiny_parse_transform_attributes(dom_element *node,
                struct svgtiny_parse_state *state);
@@ -56,6 +56,52 @@ static svgtiny_code svgtiny_add_path(float *p, unsigned int n,
 static void _svgtiny_parse_color(const char *s, svgtiny_colour *c,
                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.
+ */
+static void svgtiny_setup_state_local(struct svgtiny_parse_state *state)
+{
+       if (state->gradient_x1 != NULL) {
+               dom_string_ref(state->gradient_x1);
+       }
+       if (state->gradient_y1 != NULL) {
+               dom_string_ref(state->gradient_y1);
+       }
+       if (state->gradient_x2 != NULL) {
+               dom_string_ref(state->gradient_x2);
+       }
+       if (state->gradient_y2 != NULL) {
+               dom_string_ref(state->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.
+ */
+static void svgtiny_cleanup_state_local(struct svgtiny_parse_state *state)
+{
+       if (state->gradient_x1 != NULL) {
+               dom_string_unref(state->gradient_x1);
+               state->gradient_x1 = NULL;
+       }
+       if (state->gradient_y1 != NULL) {
+               dom_string_unref(state->gradient_y1);
+               state->gradient_y1 = NULL;
+       }
+       if (state->gradient_x2 != NULL) {
+               dom_string_unref(state->gradient_x2);
+               state->gradient_x2 = NULL;
+       }
+       if (state->gradient_y2 != NULL) {
+               dom_string_unref(state->gradient_y2);
+               state->gradient_y2 = NULL;
+       }
+}
+
 
 /**
  * Create a new svgtiny_diagram structure.
@@ -106,6 +152,11 @@ 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);
 
@@ -205,10 +256,11 @@ svgtiny_code svgtiny_parse(struct svgtiny_diagram *diagram,
        dom_node_unref(document);
 
 cleanup:
+       svgtiny_cleanup_state_local(&state);
 #define SVGTINY_STRING_ACTION2(s,n)                    \
        if (state.interned_##s != NULL)                 \
                dom_string_unref(state.interned_##s);
-//#include "svgtiny_strings.h"
+#include "svgtiny_strings.h"
 #undef SVGTINY_STRING_ACTION2
        return code;
 }
@@ -226,6 +278,8 @@ svgtiny_code svgtiny_parse_svg(dom_element *svg,
        dom_element *child;
        dom_exception exc;
 
+       svgtiny_setup_state_local(&state);
+
        svgtiny_parse_position_attributes(svg, state, &x, &y, &width, &height);
        svgtiny_parse_paint_attributes(svg, &state);
        svgtiny_parse_font_attributes(svg, &state);
@@ -233,12 +287,13 @@ svgtiny_code svgtiny_parse_svg(dom_element *svg,
        exc = dom_element_get_attribute(svg, state.interned_viewBox,
                                        &view_box);
        if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
        }
 
        if (view_box) {
                char *s = strndup(dom_string_data(view_box),
-                                 dom_string_length(view_box));
+                                 dom_string_byte_length(view_box));
                float min_x, min_y, vwidth, vheight;
                if (sscanf(s, "%f,%f,%f,%f",
                                &min_x, &min_y, &vwidth, &vheight) == 4 ||
@@ -255,8 +310,9 @@ svgtiny_code svgtiny_parse_svg(dom_element *svg,
 
        svgtiny_parse_transform_attributes(svg, &state);
 
-       exc = dom_node_get_first_child(svg, &child);
+       exc = dom_node_get_first_child(svg, (dom_node **) (void *) &child);
        if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
        }
        while (child != NULL) {
@@ -274,6 +330,7 @@ svgtiny_code svgtiny_parse_svg(dom_element *svg,
                        exc = dom_node_get_node_name(child, &nodename);
                        if (exc != DOM_NO_ERR) {
                                dom_node_unref(child);
+                               svgtiny_cleanup_state_local(&state);
                                return svgtiny_LIBDOM_ERROR;
                        }
                        if (dom_string_caseless_isequal(state.interned_svg,
@@ -313,16 +370,20 @@ svgtiny_code svgtiny_parse_svg(dom_element *svg,
                }
                if (code != svgtiny_OK) {
                        dom_node_unref(child);
+                       svgtiny_cleanup_state_local(&state);
                        return code;
                }
-               exc = dom_node_get_next_sibling(child, &next);
+               exc = dom_node_get_next_sibling(child,
+                                               (dom_node **) (void *) &next);
                dom_node_unref(child);
                if (exc != DOM_NO_ERR) {
+                       svgtiny_cleanup_state_local(&state);
                        return svgtiny_LIBDOM_ERROR;
                }
                child = next;
        }
 
+       svgtiny_cleanup_state_local(&state);
        return svgtiny_OK;
 }
 
@@ -337,6 +398,7 @@ svgtiny_code svgtiny_parse_svg(dom_element *svg,
 svgtiny_code svgtiny_parse_path(dom_element *path,
                struct svgtiny_parse_state state)
 {
+       svgtiny_code err;
        dom_string *path_d_str;
        dom_exception exc;
        char *s, *path_d;
@@ -346,6 +408,8 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
        float last_cubic_x = 0, last_cubic_y = 0;
        float last_quad_x = 0, last_quad_y = 0;
 
+       svgtiny_setup_state_local(&state);
+
        svgtiny_parse_paint_attributes(path, &state);
        svgtiny_parse_transform_attributes(path, &state);
 
@@ -354,25 +418,29 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
        if (exc != DOM_NO_ERR) {
                state.diagram->error_line = -1; /* path->line; */
                state.diagram->error_message = "path: error retrieving d attribute";
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_SVG_ERROR;
        }
 
        if (path_d_str == NULL) {
                state.diagram->error_line = -1; /* path->line; */
                state.diagram->error_message = "path: missing d attribute";
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_SVG_ERROR;
        }
 
        s = path_d = strndup(dom_string_data(path_d_str),
-                            dom_string_length(path_d_str));
+                            dom_string_byte_length(path_d_str));
        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) {
                free(path_d);
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
        }
 
@@ -569,10 +637,15 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
        if (i <= 4) {
                /* no real segments in path */
                free(p);
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OK;
        }
 
-       return svgtiny_add_path(p, i, &state);
+       err = svgtiny_add_path(p, i, &state);
+
+       svgtiny_cleanup_state_local(&state);
+
+       return err;
 }
 
 
@@ -585,17 +658,22 @@ svgtiny_code svgtiny_parse_path(dom_element *path,
 svgtiny_code svgtiny_parse_rect(dom_element *rect,
                struct svgtiny_parse_state state)
 {
+       svgtiny_code err;
        float x, y, width, height;
        float *p;
 
+       svgtiny_setup_state_local(&state);
+
        svgtiny_parse_position_attributes(rect, state,
                        &x, &y, &width, &height);
        svgtiny_parse_paint_attributes(rect, &state);
        svgtiny_parse_transform_attributes(rect, &state);
 
        p = malloc(13 * sizeof p[0]);
-       if (!p)
+       if (!p) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
+       }
 
        p[0] = svgtiny_PATH_MOVE;
        p[1] = x;
@@ -611,7 +689,11 @@ svgtiny_code svgtiny_parse_rect(dom_element *rect,
        p[11] = y + height;
        p[12] = svgtiny_PATH_CLOSE;
 
-       return svgtiny_add_path(p, 13, &state);
+       err = svgtiny_add_path(p, 13, &state);
+
+       svgtiny_cleanup_state_local(&state);
+
+       return err;
 }
 
 
@@ -622,30 +704,39 @@ svgtiny_code svgtiny_parse_rect(dom_element *rect,
 svgtiny_code svgtiny_parse_circle(dom_element *circle,
                struct svgtiny_parse_state state)
 {
+       svgtiny_code err;
        float x = 0, y = 0, r = -1;
        float *p;
        dom_string *attr;
        dom_exception exc;
 
+       svgtiny_setup_state_local(&state);
+
        exc = dom_element_get_attribute(circle, state.interned_cx, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                x = svgtiny_parse_length(attr, state.viewport_width, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(circle, state.interned_cy, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                y = svgtiny_parse_length(attr, state.viewport_height, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(circle, state.interned_r, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                r = svgtiny_parse_length(attr, state.viewport_width, state);
        }
@@ -657,14 +748,19 @@ svgtiny_code svgtiny_parse_circle(dom_element *circle,
        if (r < 0) {
                state.diagram->error_line = -1; /* circle->line; */
                state.diagram->error_message = "circle: r missing or negative";
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_SVG_ERROR;
        }
-       if (r == 0)
+       if (r == 0) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OK;
+       }
 
        p = malloc(32 * sizeof p[0]);
-       if (!p)
+       if (!p) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
+       }
 
        p[0] = svgtiny_PATH_MOVE;
        p[1] = x + r;
@@ -698,8 +794,12 @@ svgtiny_code svgtiny_parse_circle(dom_element *circle,
        p[29] = x + r;
        p[30] = y;
        p[31] = svgtiny_PATH_CLOSE;
+
+       err = svgtiny_add_path(p, 32, &state);
+
+       svgtiny_cleanup_state_local(&state);
        
-       return svgtiny_add_path(p, 32, &state);
+       return err;
 }
 
 
@@ -710,38 +810,49 @@ svgtiny_code svgtiny_parse_circle(dom_element *circle,
 svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse,
                struct svgtiny_parse_state state)
 {
+       svgtiny_code err;
        float x = 0, y = 0, rx = -1, ry = -1;
        float *p;
        dom_string *attr;
        dom_exception exc;
 
+       svgtiny_setup_state_local(&state);
+
        exc = dom_element_get_attribute(ellipse, state.interned_cx, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                x = svgtiny_parse_length(attr, state.viewport_width, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(ellipse, state.interned_cy, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                y = svgtiny_parse_length(attr, state.viewport_height, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(ellipse, state.interned_rx, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                rx = svgtiny_parse_length(attr, state.viewport_width, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(ellipse, state.interned_ry, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                ry = svgtiny_parse_length(attr, state.viewport_width, state);
        }
@@ -754,14 +865,19 @@ svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse,
                state.diagram->error_line = -1; /* ellipse->line; */
                state.diagram->error_message = "ellipse: rx or ry missing "
                                "or negative";
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_SVG_ERROR;
        }
-       if (rx == 0 || ry == 0)
+       if (rx == 0 || ry == 0) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OK;
+       }
 
        p = malloc(32 * sizeof p[0]);
-       if (!p)
+       if (!p) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
+       }
 
        p[0] = svgtiny_PATH_MOVE;
        p[1] = x + rx;
@@ -796,7 +912,11 @@ svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse,
        p[30] = y;
        p[31] = svgtiny_PATH_CLOSE;
        
-       return svgtiny_add_path(p, 32, &state);
+       err = svgtiny_add_path(p, 32, &state);
+
+       svgtiny_cleanup_state_local(&state);
+
+       return err;
 }
 
 
@@ -807,38 +927,49 @@ svgtiny_code svgtiny_parse_ellipse(dom_element *ellipse,
 svgtiny_code svgtiny_parse_line(dom_element *line,
                struct svgtiny_parse_state state)
 {
+       svgtiny_code err;
        float x1 = 0, y1 = 0, x2 = 0, y2 = 0;
        float *p;
        dom_string *attr;
        dom_exception exc;
 
+       svgtiny_setup_state_local(&state);
+
        exc = dom_element_get_attribute(line, state.interned_x1, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                x1 = svgtiny_parse_length(attr, state.viewport_width, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(line, state.interned_y1, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                y1 = svgtiny_parse_length(attr, state.viewport_height, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(line, state.interned_x2, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                x2 = svgtiny_parse_length(attr, state.viewport_width, state);
        }
        dom_string_unref(attr);
 
        exc = dom_element_get_attribute(line, state.interned_y2, &attr);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        if (attr != NULL) {
                y2 = svgtiny_parse_length(attr, state.viewport_height, state);
        }
@@ -848,8 +979,10 @@ svgtiny_code svgtiny_parse_line(dom_element *line,
        svgtiny_parse_transform_attributes(line, &state);
 
        p = malloc(7 * sizeof p[0]);
-       if (!p)
+       if (!p) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
+       }
 
        p[0] = svgtiny_PATH_MOVE;
        p[1] = x1;
@@ -859,7 +992,11 @@ svgtiny_code svgtiny_parse_line(dom_element *line,
        p[5] = y2;
        p[6] = svgtiny_PATH_CLOSE;
 
-       return svgtiny_add_path(p, 7, &state);
+       err = svgtiny_add_path(p, 7, &state);
+
+       svgtiny_cleanup_state_local(&state);
+
+       return err;
 }
 
 
@@ -873,37 +1010,46 @@ svgtiny_code svgtiny_parse_line(dom_element *line,
 svgtiny_code svgtiny_parse_poly(dom_element *poly,
                struct svgtiny_parse_state state, bool polygon)
 {
+       svgtiny_code err;
        dom_string *points_str;
        dom_exception exc;
        char *s, *points;
        float *p;
        unsigned int i;
 
+       svgtiny_setup_state_local(&state);
+
        svgtiny_parse_paint_attributes(poly, &state);
        svgtiny_parse_transform_attributes(poly, &state);
        
        exc = dom_element_get_attribute(poly, state.interned_points,
                                        &points_str);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
+       }
        
        if (points_str == NULL) {
                state.diagram->error_line = -1; /* poly->line; */
                state.diagram->error_message =
                                "polyline/polygon: missing points attribute";
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_SVG_ERROR;
        }
 
        s = points = strndup(dom_string_data(points_str),
-                            dom_string_length(points_str));
+                            dom_string_byte_length(points_str));
        dom_string_unref(points_str);
        /* read points attribute */
-       if (s == NULL)
+       if (s == NULL) {
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
+       }
        /* allocate space for path: it will never have more elements than s */
        p = malloc(sizeof p[0] * strlen(s));
        if (!p) {
                free(points);
+               svgtiny_cleanup_state_local(&state);
                return svgtiny_OUT_OF_MEMORY;
        }
 
@@ -933,7 +1079,11 @@ svgtiny_code svgtiny_parse_poly(dom_element *poly,
 
        free(points);
 
-       return svgtiny_add_path(p, i, &state);
+       err = svgtiny_add_path(p, i, &state);
+
+       svgtiny_cleanup_state_local(&state);
+
+       return err;
 }
 
 
@@ -949,6 +1099,8 @@ svgtiny_code svgtiny_parse_text(dom_element *text,
        dom_node *child;
        dom_exception exc;
 
+       svgtiny_setup_state_local(&state);
+
        svgtiny_parse_position_attributes(text, state,
                        &x, &y, &width, &height);
        svgtiny_parse_font_attributes(text, &state);
@@ -963,8 +1115,10 @@ svgtiny_code svgtiny_parse_text(dom_element *text,
        style.font_size.value.length.value *= state.ctm.a;*/
        
         exc = dom_node_get_first_child(text, &child);
-       if (exc != DOM_NO_ERR)
+       if (exc != DOM_NO_ERR) {
                return svgtiny_LIBDOM_ERROR;
+               svgtiny_cleanup_state_local(&state);
+       }
        while (child != NULL) {
                dom_node *next;
                dom_node_type nodetype;
@@ -973,6 +1127,7 @@ svgtiny_code svgtiny_parse_text(dom_element *text,
                exc = dom_node_get_node_type(child, &nodetype);
                if (exc != DOM_NO_ERR) {
                        dom_node_unref(child);
+                       svgtiny_cleanup_state_local(&state);
                        return svgtiny_LIBDOM_ERROR;
                }
                if (nodetype == DOM_ELEMENT_NODE) {
@@ -980,6 +1135,7 @@ svgtiny_code svgtiny_parse_text(dom_element *text,
                        exc = dom_node_get_node_name(child, &nodename);
                        if (exc != DOM_NO_ERR) {
                                dom_node_unref(child);
+                               svgtiny_cleanup_state_local(&state);
                                return svgtiny_LIBDOM_ERROR;
                        }
                        if (dom_string_caseless_isequal(nodename,
@@ -992,16 +1148,22 @@ svgtiny_code svgtiny_parse_text(dom_element *text,
                        dom_string *content;
                        if (shape == NULL) {
                                dom_node_unref(child);
+                               svgtiny_cleanup_state_local(&state);
                                return svgtiny_OUT_OF_MEMORY;
                        }
                        exc = dom_text_get_whole_text(child, &content);
                        if (exc != DOM_NO_ERR) {
                                dom_node_unref(child);
+                               svgtiny_cleanup_state_local(&state);
                                return svgtiny_LIBDOM_ERROR;
                        }
-                       shape->text = strndup(dom_string_data(content),
-                                             dom_string_length(content));
-                       dom_string_unref(content);
+                       if (content != NULL) {
+                               shape->text = strndup(dom_string_data(content),
+                                                     dom_string_byte_length(content));
+                               dom_string_unref(content);
+                       } else {
+                               shape->text = strdup("");
+                       }
                        shape->text_x = px;
                        shape->text_y = py;
                        state.diagram->shape_count++;
@@ -1009,15 +1171,20 @@ svgtiny_code svgtiny_parse_text(dom_element *text,
 
                if (code != svgtiny_OK) {
                        dom_node_unref(child);
+                       svgtiny_cleanup_state_local(&state);
                        return code;
                }
                exc = dom_node_get_next_sibling(child, &next);
                dom_node_unref(child);
-               if (exc != DOM_NO_ERR)
+               if (exc != DOM_NO_ERR) {
+                       svgtiny_cleanup_state_local(&state);
                        return svgtiny_LIBDOM_ERROR;
+               }
                child = next;
        }
 
+       svgtiny_cleanup_state_local(&state);
+
        return svgtiny_OK;
 }
 
@@ -1026,7 +1193,7 @@ svgtiny_code svgtiny_parse_text(dom_element *text,
  * Parse x, y, width, and height attributes, if present.
  */
 
-void svgtiny_parse_position_attributes(const dom_element *node,
+void svgtiny_parse_position_attributes(dom_element *node,
                const struct svgtiny_parse_state state,
                float *x, float *y, float *width, float *height)
 {
@@ -1108,7 +1275,7 @@ static float _svgtiny_parse_length(const char *s, int viewport_size,
 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_length(s));
+       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;
@@ -1118,7 +1285,7 @@ float svgtiny_parse_length(dom_string *s, int viewport_size,
  * Parse paint attributes, if present.
  */
 
-void svgtiny_parse_paint_attributes(const dom_element *node,
+void svgtiny_parse_paint_attributes(dom_element *node,
                struct svgtiny_parse_state *state)
 {
        dom_string *attr;
@@ -1146,7 +1313,7 @@ void svgtiny_parse_paint_attributes(const dom_element *node,
        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_length(attr));
+                                     dom_string_byte_length(attr));
                const char *s;
                char *value;
                if ((s = strstr(style, "fill:"))) {
@@ -1246,7 +1413,7 @@ 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 *state)
 {
-       char *ss = strndup(dom_string_data(s), dom_string_length(s));
+       char *ss = strndup(dom_string_data(s), dom_string_byte_length(s));
        _svgtiny_parse_color(ss, c, state);
        free(ss);
 }
@@ -1255,7 +1422,7 @@ void svgtiny_parse_color(dom_string *s, svgtiny_colour *c,
  * Parse font attributes, if present.
  */
 
-void svgtiny_parse_font_attributes(const dom_element *node,
+void svgtiny_parse_font_attributes(dom_element *node,
                struct svgtiny_parse_state *state)
 {
        /* TODO: Implement this, it never used to be */
@@ -1298,7 +1465,7 @@ void svgtiny_parse_transform_attributes(dom_element *node,
                                        &attr);
        if (exc == DOM_NO_ERR && attr != NULL) {
                transform = strndup(dom_string_data(attr),
-                                   dom_string_length(attr));
+                                   dom_string_byte_length(attr));
                svgtiny_parse_transform(transform, &state->ctm.a, &state->ctm.b,
                                &state->ctm.c, &state->ctm.d,
                                &state->ctm.e, &state->ctm.f);