]> gitweb.michael.orlitzky.com - libsvgtiny.git/commitdiff
src/svgtiny.c: implement composition of parent styles
authorMichael Orlitzky <michael@orlitzky.com>
Sat, 7 Jun 2025 15:52:31 +0000 (11:52 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Mon, 9 Jun 2025 01:13:07 +0000 (21:13 -0400)
src/svgtiny.c

index 0f47df92da59bd68521fde01bf293b1d7046fa59..f2fb5b4b4fd365ca7401364114ed3e3167e18d59 100644 (file)
@@ -405,13 +405,11 @@ static css_stylesheet *svgtiny_parse_style_inline(const uint8_t *data,
  * styles that we support and set the corresponding fields in the
  * parser state.
  */
-static void svgtiny_parse_styles(dom_element *node,
+static css_select_results *svgtiny_parse_styles(dom_element *node,
                struct svgtiny_parse_state *state)
 {
        css_error code;
-       uint8_t   fill_opacity_type;
        css_fixed fill_opacity;
-       uint8_t   stroke_opacity_type;
        css_fixed stroke_opacity;
 
        /* We store the result of svgtiny_parse_style_inline() in
@@ -425,13 +423,16 @@ static void svgtiny_parse_styles(dom_element *node,
        * safely destroy it later even if we never populated it. */
        css_select_results *styles = NULL;
 
+       /* The result of composing this node's styles with its
+        * parent's styles. */
+       css_computed_style *composed = NULL;
 
        dom_exception exc;
        dom_string *attr;
 
        exc = dom_element_get_attribute(node, state->interned_style, &attr);
        if (exc != DOM_NO_ERR) {
-               return;
+               return NULL;
        }
        if (attr != NULL) {
                inline_sheet = svgtiny_parse_style_inline(
@@ -457,7 +458,7 @@ static void svgtiny_parse_styles(dom_element *node,
                * <svg> element.
                */
                css_stylesheet_destroy(inline_sheet);
-               return;
+               return NULL;
        }
        else {
                /* We only needed to know if it was NULL */
@@ -467,23 +468,42 @@ static void svgtiny_parse_styles(dom_element *node,
        code = svgtiny_select_style(state, node, inline_sheet, &styles);
        css_stylesheet_destroy(inline_sheet);
        if (code != CSS_OK) {
-               return;
+               return NULL;
        }
 
-       fill_opacity_type = css_computed_fill_opacity(
-                               styles->styles[CSS_PSEUDO_ELEMENT_NONE],
-                               &fill_opacity);
-       stroke_opacity_type = css_computed_stroke_opacity(
-                               styles->styles[CSS_PSEUDO_ELEMENT_NONE],
-                               &stroke_opacity);
-       css_select_results_destroy(styles);
+       if (state->parent_style != NULL) {
+               code = css_computed_style_compose(
+                                       state->parent_style,
+                                       styles->styles[CSS_PSEUDO_ELEMENT_NONE],
+                                       &state->unit_ctx,
+                                       &composed);
 
-       if (fill_opacity_type == CSS_FILL_OPACITY_SET) {
-               state->fill_opacity = FIXTOFLT(fill_opacity);
-       }
-       if (stroke_opacity_type == CSS_STROKE_OPACITY_SET) {
-               state->stroke_opacity = FIXTOFLT(stroke_opacity);
+               if (code != CSS_OK || composed == NULL) {
+                       /* This function promises to return a
+                        * fully-composed set of styles, so if
+                        * we can't do that, we should fail. */
+                       css_select_results_destroy(styles);
+                       return NULL;
+               }
+
+               /* Replace my original computed styles with the
+                * composed ones */
+               css_computed_style_destroy(
+                               styles->styles[CSS_PSEUDO_ELEMENT_NONE]);
+               styles->styles[CSS_PSEUDO_ELEMENT_NONE] = composed;
        }
+
+       css_computed_fill_opacity(
+                       styles->styles[CSS_PSEUDO_ELEMENT_NONE],
+                       &fill_opacity);
+       css_computed_stroke_opacity(
+                       styles->styles[CSS_PSEUDO_ELEMENT_NONE],
+                       &stroke_opacity);
+
+       state->fill_opacity = FIXTOFLT(fill_opacity);
+       state->stroke_opacity = FIXTOFLT(stroke_opacity);
+
+       return styles;
 }
 
 
@@ -505,7 +525,7 @@ svgtiny_parse_path(dom_element *path, struct svgtiny_parse_state state)
 
        svgtiny_parse_paint_attributes(path, &state);
        svgtiny_parse_transform_attributes(path, &state);
-       svgtiny_parse_styles(path, &state);
+       css_select_results_destroy(svgtiny_parse_styles(path, &state));
 
        /* read d attribute */
        exc = dom_element_get_attribute(path, state.interned_d, &path_d_str);
@@ -566,7 +586,7 @@ svgtiny_parse_rect(dom_element *rect, struct svgtiny_parse_state state)
                                          &x, &y, &width, &height);
        svgtiny_parse_paint_attributes(rect, &state);
        svgtiny_parse_transform_attributes(rect, &state);
-       svgtiny_parse_styles(rect, &state);
+       css_select_results_destroy(svgtiny_parse_styles(rect, &state));
 
        p = malloc(13 * sizeof p[0]);
        if (!p) {
@@ -636,7 +656,7 @@ svgtiny_parse_circle(dom_element *circle, struct svgtiny_parse_state state)
 
        svgtiny_parse_paint_attributes(circle, &state);
        svgtiny_parse_transform_attributes(circle, &state);
-       svgtiny_parse_styles(circle, &state);
+       css_select_results_destroy(svgtiny_parse_styles(circle, &state));
 
        if (r < 0) {
                state.diagram->error_line = -1; /* circle->line; */
@@ -741,7 +761,7 @@ svgtiny_parse_ellipse(dom_element *ellipse, struct svgtiny_parse_state state)
 
        svgtiny_parse_paint_attributes(ellipse, &state);
        svgtiny_parse_transform_attributes(ellipse, &state);
-       svgtiny_parse_styles(ellipse, &state);
+       css_select_results_destroy(svgtiny_parse_styles(ellipse, &state));
 
        if (rx < 0 || ry < 0) {
                state.diagram->error_line = -1; /* ellipse->line; */
@@ -847,7 +867,7 @@ svgtiny_parse_line(dom_element *line, struct svgtiny_parse_state state)
 
        svgtiny_parse_paint_attributes(line, &state);
        svgtiny_parse_transform_attributes(line, &state);
-       svgtiny_parse_styles(line, &state);
+       css_select_results_destroy(svgtiny_parse_styles(line, &state));
 
        p = malloc(7 * sizeof p[0]);
        if (!p) {
@@ -892,7 +912,7 @@ svgtiny_parse_poly(dom_element *poly,
 
        svgtiny_parse_paint_attributes(poly, &state);
        svgtiny_parse_transform_attributes(poly, &state);
-       svgtiny_parse_styles(poly, &state);
+       css_select_results_destroy(svgtiny_parse_styles(poly, &state));
 
        exc = dom_element_get_attribute(poly, state.interned_points,
                                        &points_str);
@@ -1093,17 +1113,19 @@ svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state)
        dom_string *view_box;
        dom_element *child;
        dom_exception exc;
+       css_select_results *styles;
 
        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);
-       svgtiny_parse_styles(svg, &state);
+       styles = svgtiny_parse_styles(svg, &state);
 
        exc = dom_element_get_attribute(svg, state.interned_viewBox,
                                        &view_box);
        if (exc != DOM_NO_ERR) {
+               css_select_results_destroy(styles);
                svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
        }
@@ -1121,6 +1143,7 @@ svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state)
 
        exc = dom_node_get_first_child(svg, (dom_node **) (void *) &child);
        if (exc != DOM_NO_ERR) {
+               css_select_results_destroy(styles);
                svgtiny_cleanup_state_local(&state);
                return svgtiny_LIBDOM_ERROR;
        }
@@ -1129,9 +1152,19 @@ svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state)
                dom_node_type nodetype;
                svgtiny_code code = svgtiny_OK;
 
+               /* Before we descend to one of my child elements, set
+                * the "parent style" to my style. */
+               if (styles) {
+                       /* For now at least, the root element won't
+                        * have any styles; hence the null check. */
+                       state.parent_style = styles->styles[CSS_PSEUDO_ELEMENT_NONE];
+               }
+
                exc = dom_node_get_node_type(child, &nodetype);
                if (exc != DOM_NO_ERR) {
                        dom_node_unref(child);
+                       css_select_results_destroy(styles);
+                       svgtiny_cleanup_state_local(&state);
                        return svgtiny_LIBDOM_ERROR;
                }
                if (nodetype == DOM_ELEMENT_NODE) {
@@ -1139,6 +1172,7 @@ svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state)
                }
                if (code != svgtiny_OK) {
                        dom_node_unref(child);
+                       css_select_results_destroy(styles);
                        svgtiny_cleanup_state_local(&state);
                        return code;
                }
@@ -1146,12 +1180,16 @@ svgtiny_parse_svg(dom_element *svg, struct svgtiny_parse_state state)
                                                (dom_node **) (void *) &next);
                dom_node_unref(child);
                if (exc != DOM_NO_ERR) {
+                       css_select_results_destroy(styles);
                        svgtiny_cleanup_state_local(&state);
                        return svgtiny_LIBDOM_ERROR;
                }
                child = next;
        }
 
+       /* Hoping that destroying "styles" destroys state.parent_style
+        * as well. */
+       css_select_results_destroy(styles);
        svgtiny_cleanup_state_local(&state);
        return svgtiny_OK;
 }