Logo Search packages:      
Sourcecode: inkscape version File versions  Download package

void sp_style_merge_from_dying_parent ( SPStyle *const   style,
SPStyle const *const   parent 
)

Combine style and parent style specifications into a single style specification that preserves (as much as possible) the effect of the existing style being a child of parent.

Called when the parent repr is to be removed (e.g. the parent is a <use> element that is being unlinked), in which case we copy/adapt property values that are explicitly set in parent, trying to retain the same visual appearance once the parent is removed. Interesting cases are when there is unusual interaction with the parent's value (opacity, display) or when the value can be specified as relative to the parent computed value (font-size, font-weight etc.).

Doesn't update computed values of style. For correctness, you should subsequently call sp_style_merge_from_parent against the new parent (presumably parent's parent) even if style was previously up-to-date wrt parent.

Precondition:
parent's computed values are already up-to-date. (style's computed values needn't be up-to-date.)

Note:
The general rule for each property is as follows:
If style is set to an absolute value, then leave it as is.

Otherwise (i.e. if style has a relative value):

If parent is set to an absolute value, then set style to the computed value.

Otherwise, calculate the combined relative value (e.g. multiplying the two percentages).

Todo:
fixme: We'll need to have more font-related things up the top once we're getting x-height from pango or libnrtype.

Todo:
We currently treat text-decoration as if it were a simple inherited property (fixme). This code may need changing once we do the special fill/stroke inheritance mentioned by the spec.

Todo:
Could cause problems if original object was in another document and it used a relative URL. (At the time of writing, we don't allow hrefs to other documents, so this isn't a problem yet.) Paint properties also allow URIs.

Todo:
fixme: Check that we correctly handle all properties that don't inherit by default (as shown in http://www.w3.org/TR/SVG11/propidx.html for most SVG 1.1 properties).

Todo:
Think about the issues involved if specified as currentColor or if specified relative to colorProfile, and if the currentColor or colorProfile differs between parent & child. See also comments elsewhere in this function about URIs.

Definition at line 1600 of file style.cpp.

References SPStyle::block_progression, SPStyle::color, SPIFontSize::computed, NRVpathDash::dash, SPStyle::direction, SPStyle::display, SPStyle::fill, SPStyle::fill_opacity, SPStyle::fill_rule, SPTextStyle::font_family, SPStyle::font_size, SPStyle::font_stretch, SPStyle::font_style, SPStyle::font_variant, SPStyle::font_weight, SPIEnum::inherit, SPIScale24::inherit, SPIFontSize::inherit, SPStyle::letter_spacing, SPStyle::line_height, SPStyle::marker, NRVpathDash::n_dash, NRVpathDash::offset, SPStyle::opacity, SPIEnum::set, SPIScale24::set, SPIFontSize::set, SP_SCALE24_MUL, sp_style_merge_paint_prop_from_dying_parent(), sp_style_merge_rel_enum_prop_from_dying_parent(), sp_style_merge_string_prop_from_dying_parent(), SPStyle::stroke, SPStyle::stroke_dash, SPStyle::stroke_dasharray_inherit, SPStyle::stroke_dasharray_set, SPStyle::stroke_dashoffset_set, SPStyle::stroke_linecap, SPStyle::stroke_linejoin, SPStyle::stroke_miterlimit, SPStyle::stroke_opacity, SPStyle::stroke_width, SPStyle::text, SPStyle::text_align, SPStyle::text_anchor, SPStyle::text_decoration, SPStyle::text_indent, SPStyle::text_transform, SPIFontSize::type, SPIEnum::value, SPIScale24::value, SPIFontSize::value, SPStyle::visibility, SPStyle::word_spacing, and SPStyle::writing_mode.

{
    /** \note
     * The general rule for each property is as follows:
     *
     *   If style is set to an absolute value, then leave it as is.
     *
     *   Otherwise (i.e. if style has a relative value):
     *
     *     If parent is set to an absolute value, then set style to the computed value.
     *
     *     Otherwise, calculate the combined relative value (e.g. multiplying the two percentages).
     */

    /* We do font-size first, to ensure that em size is up-to-date. */
    /** \todo
     * fixme: We'll need to have more font-related things up the top once 
     * we're getting x-height from pango or libnrtype.
     */

    /* Some things that allow relative specifications. */
    {
        /* font-size.  Note that we update the computed font-size of style,
           to assist in em calculations later in this function. */
        if (parent->font_size.set && !parent->font_size.inherit) {
            if (!style->font_size.set || style->font_size.inherit) {
                /* font_size inherits the computed value, so we can use the parent value
                 * verbatim. */
                style->font_size = parent->font_size;
            } else if ( style->font_size.type == SP_FONT_SIZE_LENGTH ) {
                /* Child already has absolute size (stored in computed value), so do nothing. */
            } else if ( style->font_size.type == SP_FONT_SIZE_LITERAL
                        && style->font_size.value < SP_CSS_FONT_SIZE_SMALLER ) {
                /* Child already has absolute size, but we ensure that the computed value
                   is up-to-date. */
                unsigned const ix = style->font_size.value;
                g_assert(ix < G_N_ELEMENTS(font_size_table));
                style->font_size.computed = font_size_table[ix];
            } else {
                /* Child has relative size. */
                double const child_frac(get_relative_font_size_frac(style->font_size));
                style->font_size.set = true;
                style->font_size.inherit = false;
                style->font_size.computed = parent->font_size.computed * child_frac;

                if ( ( parent->font_size.type == SP_FONT_SIZE_LITERAL
                       && parent->font_size.value < SP_CSS_FONT_SIZE_SMALLER )
                     || parent->font_size.type == SP_FONT_SIZE_LENGTH )
                {
                    /* Absolute value. */
                    style->font_size.type = SP_FONT_SIZE_LENGTH;
                    /* .value is unused for SP_FONT_SIZE_LENGTH. */
                } else {
                    /* Relative value. */
                    double const parent_frac(get_relative_font_size_frac(parent->font_size));
                    style->font_size.type = SP_FONT_SIZE_PERCENTAGE;
                    style->font_size.value = SP_F8_16_FROM_FLOAT(parent_frac * child_frac);
                }
            }
        }

        /* 'font-stretch' */
        sp_style_merge_rel_enum_prop_from_dying_parent(style->font_stretch,
                                                       parent->font_stretch,
                                                       SP_CSS_FONT_STRETCH_ULTRA_EXPANDED,
                                                       SP_CSS_FONT_STRETCH_NARROWER);

        /* font-weight */
        sp_style_merge_rel_enum_prop_from_dying_parent(style->font_weight,
                                                       parent->font_weight,
                                                       SP_CSS_FONT_WEIGHT_900,
                                                       SP_CSS_FONT_WEIGHT_LIGHTER);
    }


    /* Enum values that don't have any relative settings (other than `inherit'). */
    {
        SPIEnum SPStyle::*const fields[] = {
            //nyi: SPStyle::clip_rule,
            //nyi: SPStyle::color_interpolation,
            //nyi: SPStyle::color_interpolation_filters,
            //nyi: SPStyle::color_rendering,
            &SPStyle::direction,
            &SPStyle::fill_rule,
            &SPStyle::font_style,
            &SPStyle::font_variant,
            //nyi: SPStyle::image_rendering,
            //nyi: SPStyle::pointer_events,
            //nyi: SPStyle::shape_rendering,
            &SPStyle::stroke_linecap,
            &SPStyle::stroke_linejoin,
            &SPStyle::text_anchor,
            //nyi: &SPStyle::text_rendering,
            &SPStyle::visibility,
            &SPStyle::writing_mode
        };

        for (unsigned i = 0; i < G_N_ELEMENTS(fields); ++i) {
            SPIEnum SPStyle::*const fld = fields[i];
            sp_style_merge_prop_from_dying_parent<SPIEnum>(style->*fld, parent->*fld);
        }
    }

    /* A few other simple inheritance properties. */
    {
        sp_style_merge_prop_from_dying_parent<SPIScale24>(style->fill_opacity, parent->fill_opacity);
        sp_style_merge_prop_from_dying_parent<SPIScale24>(style->stroke_opacity, parent->stroke_opacity);
        sp_style_merge_prop_from_dying_parent<SPIFloat>(style->stroke_miterlimit, parent->stroke_miterlimit);

        /** \todo
         * We currently treat text-decoration as if it were a simple inherited 
         * property (fixme). This code may need changing once we do the 
         * special fill/stroke inheritance mentioned by the spec.
         */
        sp_style_merge_prop_from_dying_parent<SPITextDecoration>(style->text_decoration,
                                                                 parent->text_decoration);

        //nyi: font-size-adjust,  // <number> | none | inherit
        //nyi: glyph-orientation-horizontal,
        //nyi: glyph-orientation-vertical,
    }

    /* Properties that involve length but are easy in other respects. */
    {
        /* The difficulty with lengths is that font-relative units need adjusting if the font
         * varies between parent & child.
         *
         * Lengths specified in the existing child can stay as they are: its computed font
         * specification should stay unchanged, so em & ex lengths should continue to mean the same
         * size.
         *
         * Lengths specified in the dying parent in em or ex need to be scaled according to the
         * ratio of em or ex size between parent & child.
         */
        double const parent_child_em_ratio = parent->font_size.computed / style->font_size.computed;

        SPILength SPStyle::*const lfields[] = {
            &SPStyle::stroke_width,
            &SPStyle::text_indent
        };
        for (unsigned i = 0; i < G_N_ELEMENTS(lfields); ++i) {
            SPILength SPStyle::*fld = lfields[i];
            sp_style_merge_length_prop_from_dying_parent<SPILength>(style->*fld,
                                                                    parent->*fld,
                                                                    parent_child_em_ratio);
        }

        SPILengthOrNormal SPStyle::*const nfields[] = {
            &SPStyle::letter_spacing,
            &SPStyle::line_height,
            &SPStyle::word_spacing
        };
        for (unsigned i = 0; i < G_N_ELEMENTS(nfields); ++i) {
            SPILengthOrNormal SPStyle::*fld = nfields[i];
            sp_style_merge_length_prop_from_dying_parent<SPILengthOrNormal>(style->*fld,
                                                                            parent->*fld,
                                                                            parent_child_em_ratio);
        }

        //nyi: &SPStyle::kerning: length or `auto'

        /* fixme: Move stroke-dash and stroke-dash-offset here once they
           can accept units. */
    }

    /* Properties that involve a URI but are easy in other respects. */
    {
        /** \todo
         * Could cause problems if original object was in another document 
         * and it used a relative URL.  (At the time of writing, we don't 
         * allow hrefs to other documents, so this isn't a problem yet.)  
         * Paint properties also allow URIs.
         */
        //nyi: cursor,   // may involve change in effect, but we can't do much better
        //nyi: color-profile,

        // Markers (marker-start, marker-mid, marker-end).
        for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) {
            sp_style_merge_string_prop_from_dying_parent(style->marker[i], parent->marker[i]);
        }
    }

    /* CSS2 */
    /* Font */

    if (style->text && parent->text) {
        sp_style_merge_string_prop_from_dying_parent(style->text->font_family,
                                                     parent->text->font_family);
    }

    /* Properties that don't inherit by default.  Most of these need special handling. */
    {
        /*
         * opacity's effect is cumulative; we set the new value to the combined effect.  The
         * default value for opacity is 1.0, not inherit.  (Note that stroke-opacity and
         * fill-opacity are quite different from opacity, and don't need any special handling.)
         *
         * Cases:
         * - parent & child were each previously unset, in which case the effective
         *   opacity value is 1.0, and style should remain unset.
         * - parent was previously unset (so computed opacity value of 1.0)
         *   and child was set to inherit.  The merged child should
         *   get a value of 1.0, and shouldn't inherit (lest the new parent
         *   has a different opacity value).  Given that opacity's default
         *   value is 1.0 (rather than inherit), we might as well have the
         *   merged child's opacity be unset.
         * - parent was previously unset (so opacity 1.0), and child was set to a number.
         *   The merged child should retain its existing settings (though it doesn't matter
         *   if we make it unset if that number was 1.0).
         * - parent was inherit and child was unset.  Merged child should be set to inherit.
         * - parent was inherit and child was inherit.  (We can't in general reproduce this
         *   effect (short of introducing a new group), but setting opacity to inherit is rare.)
         *   If the inherited value was strictly between 0.0 and 1.0 (exclusive) then the merged
         *   child's value should be set to the product of the two, i.e. the square of the
         *   inherited value, and should not be marked as inherit.  (This decision assumes that it
         *   is more important to retain the effective opacity than to retain the inheriting
         *   effect, and assumes that the inheriting effect either isn't important enough to create
         *   a group or isn't common enough to bother maintaining the code to create a group.)  If
         *   the inherited value was 0.0 or 1.0, then marking the merged child as inherit comes
         *   closer to maintaining the effect.
         * - parent was inherit and child was set to a numerical value.  If the child's value
         *   was 1.0, then the merged child should have the same settings as the parent.
         *   If the child's value was 0, then the merged child should also be set to 0.
         *   If the child's value was anything else, then we do the same as for the inherit/inherit
         *   case above: have the merged child set to the product of the two opacities and not
         *   marked as inherit, for the same reasons as for that case.
         * - parent was set to a value, and child was unset.  The merged child should have
         *   parent's settings.
         * - parent was set to a value, and child was inherit.  The merged child should
         *   be set to the product, i.e. the square of the parent's value.
         * - parent & child are each set to a value.  The merged child should be set to the
         *   product.
         */
        if ( !style->opacity.set
             || ( !style->opacity.inherit
                  && style->opacity.value == SP_SCALE24_MAX ) )
        {
            style->opacity = parent->opacity;
        } else {
            /* Ensure that style's computed value is up-to-date. */
            if (style->opacity.inherit) {
                style->opacity.value = parent->opacity.value;
            }

            /* Multiplication of opacities occurs even if a child's opacity is set to inherit. */
            style->opacity.value = SP_SCALE24_MUL(style->opacity.value,
                                                  parent->opacity.value);

            style->opacity.inherit = (parent->opacity.inherit
                                      && style->opacity.inherit
                                      && (parent->opacity.value == 0 ||
                                          parent->opacity.value == SP_SCALE24_MAX));
            style->opacity.set = ( style->opacity.inherit
                                   || style->opacity.value < SP_SCALE24_MAX );
        }

        /* display is in principle similar to opacity, but implementation is easier. */
        if ( parent->display.set && !parent->display.inherit
                    && parent->display.value == SP_CSS_DISPLAY_NONE ) {
            style->display.value = SP_CSS_DISPLAY_NONE;
            style->display.set = true;
            style->display.inherit = false;
        } else if (style->display.inherit) {
            style->display.value = parent->display.value;
            style->display.set = parent->display.set;
            style->display.inherit = parent->display.inherit;
        } else {
            /* Leave as is.  (display doesn't inherit by default.) */
        }

        /** \todo
         * fixme: Check that we correctly handle all properties that don't 
         * inherit by default (as shown in 
         * http://www.w3.org/TR/SVG11/propidx.html for most SVG 1.1 properties).
         */
    }

    /* SPIPaint properties (including color). */
    {
        /** \todo
         * Think about the issues involved if specified as currentColor or 
         * if specified relative to colorProfile, and if the currentColor or 
         * colorProfile differs between parent \& child.  See also comments 
         * elsewhere in this function about URIs.
         */
        SPIPaint SPStyle::*const fields[] = {
            &SPStyle::color,
            &SPStyle::fill,
            &SPStyle::stroke
        };
        for (unsigned i = 0; i < G_N_ELEMENTS(fields); ++i) {
            SPIPaint SPStyle::*const fld = fields[i];
            sp_style_merge_paint_prop_from_dying_parent(style, style->*fld, parent->*fld);
        }
    }

    /* Things from SVG 1.2 or CSS3. */
    {
        /* Note: If we ever support setting string values for text-align then we'd need strdup
         * handling here. */
        sp_style_merge_prop_from_dying_parent<SPIEnum>(style->text_align, parent->text_align);

        sp_style_merge_prop_from_dying_parent<SPIEnum>(style->text_transform, parent->text_transform);
        sp_style_merge_prop_from_dying_parent<SPIEnum>(style->block_progression, parent->block_progression);
    }

    /* Note: this will need length handling once dasharray supports units. */
    if ( ( !style->stroke_dasharray_set || style->stroke_dasharray_inherit )
         && parent->stroke_dasharray_set && !parent->stroke_dasharray_inherit )
    {
        style->stroke_dash.n_dash = parent->stroke_dash.n_dash;
        if (style->stroke_dash.n_dash > 0) {
            style->stroke_dash.dash = g_new(gdouble, style->stroke_dash.n_dash);
            memcpy(style->stroke_dash.dash, parent->stroke_dash.dash, style->stroke_dash.n_dash * sizeof(gdouble));
        }
        style->stroke_dasharray_set = parent->stroke_dasharray_set;
        style->stroke_dasharray_inherit = parent->stroke_dasharray_inherit;
    }

    /* Note: this will need length handling once dasharray_offset supports units. */
    if (!style->stroke_dashoffset_set && parent->stroke_dashoffset_set) {
        style->stroke_dash.offset = parent->stroke_dash.offset;
        style->stroke_dashoffset_set = parent->stroke_dashoffset_set;
        /* fixme: we don't currently allow stroke-dashoffset to be `inherit'.  TODO: Try to
         * represent it as a normal SPILength; though will need to do something about existing
         * users of stroke_dash.offset and stroke_dashoffset_set. */
    }
}


Generated by  Doxygen 1.6.0   Back to index