Code

rationalize function names and remove spurious soft_ptr
[inkscape.git] / src / style.cpp
index e0a1dcc7d2bdaad8a62f85d9c88e61c3baae2c5c..2a0a30bd165b91fb4b12171307aa3bb3463b90da 100644 (file)
@@ -23,6 +23,8 @@
 #include "xml/croco-node-iface.h"
 
 #include "svg/svg.h"
+#include "svg/svg-color.h"
+#include "svg/svg-icc-color.h"
 
 #include "display/canvas-bpath.h"
 #include "attributes.h"
@@ -39,6 +41,9 @@
 #include "unit-constants.h"
 #include "isnan.h"
 
+using Inkscape::CSSOStringStream;
+using std::vector;
+
 namespace Inkscape {
 
 /**
@@ -145,9 +150,9 @@ static gint sp_style_write_ifontsize(gchar *p, gint len, gchar const *key, SPIFo
 static gint sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key, SPILengthOrNormal const *const val, SPILengthOrNormal const *const base, guint const flags);
 static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, SPITextDecoration const *const val, SPITextDecoration const *const base, guint const flags);
 
-void css2_unescape_unquote (SPIString *val);
+static void css2_unescape_unquote(SPIString *val);
 
-static void sp_style_paint_clear(SPStyle *style, SPIPaint *paint, unsigned hunref, unsigned unset);
+static void sp_style_paint_clear(SPStyle *style, SPIPaint *paint);
 
 #define SPS_READ_IENUM_IF_UNSET(v,s,d,i) if (!(v)->set) {sp_style_read_ienum((v), (s), (d), (i));}
 #define SPS_READ_PENUM_IF_UNSET(v,r,k,d,i) if (!(v)->set) {sp_style_read_penum((v), (r), (k), (d), (i));}
@@ -389,8 +394,12 @@ sp_style_new()
     sp_style_clear(style);
 
     style->cloned = false;
-    style->hreffed = false;
-    style->listening = false;
+
+    style->fill_hreffed = false;
+    style->stroke_hreffed = false;
+
+    style->fill_listening = false;
+    style->stroke_listening = false;
 
     return style;
 }
@@ -448,8 +457,8 @@ sp_style_unref(SPStyle *style)
             g_signal_handlers_disconnect_matched(G_OBJECT(style->object),
                                                  G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, style);
         if (style->text) sp_text_style_unref(style->text);
-        sp_style_paint_clear(style, &style->fill, TRUE, FALSE);
-        sp_style_paint_clear(style, &style->stroke, TRUE, FALSE);
+        sp_style_paint_clear(style, &style->fill);
+        sp_style_paint_clear(style, &style->stroke);
         g_free(style->stroke_dash.dash);
         g_free(style);
     }
@@ -658,7 +667,7 @@ sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr)
 
 /**
  * Read style properties from object's repr.
- * 
+ *
  * 1. Reset existing object style
  * 2. Load current effective object style
  * 3. Load i attributes from immediate parent (which has to be up-to-date)
@@ -692,7 +701,7 @@ sp_style_read_from_repr(SPStyle *style, Inkscape::XML::Node *repr)
 
 
 /**
- * 
+ *
  */
 static void
 sp_style_privatize_text(SPStyle *style)
@@ -706,7 +715,7 @@ sp_style_privatize_text(SPStyle *style)
 
 /**
  * Merge property into style.
- * 
+ *
  * Should be called in order of highest to lowest precedence.
  * E.g. for a single style string, call from the last declaration to the first,
  * as CSS says that later declarations override earlier ones.
@@ -725,7 +734,7 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
             if (!style->text_private) sp_style_privatize_text(style);
             if (!style->text->font_family.set) {
                 sp_style_read_istring(&style->text->font_family, val);
-                css2_unescape_unquote (&style->text->font_family);
+                css2_unescape_unquote(&style->text->font_family);
             }
             break;
         case SP_PROP_FONT_SIZE:
@@ -841,8 +850,8 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
             SPS_READ_IENUM_IF_UNSET(&style->display, val, enum_display, true);
             break;
         case SP_PROP_OVERFLOW:
-            /** \todo 
-             * FIXME: not supported properly yet, we just read and write it, 
+            /** \todo
+             * FIXME: not supported properly yet, we just read and write it,
              * but act as if it is always "display".
              */
             SPS_READ_IENUM_IF_UNSET(&style->overflow, val, enum_overflow, true);
@@ -1033,15 +1042,15 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
 static void
 sp_style_merge_style_from_decl(SPStyle *const style, CRDeclaration const *const decl)
 {
-    /** \todo Ensure that property is lcased, as per 
+    /** \todo Ensure that property is lcased, as per
      * http://www.w3.org/TR/REC-CSS2/syndata.html#q4.
      * Should probably be done in libcroco.
      */
     unsigned const prop_idx = sp_attribute_lookup(decl->property->stryng->str);
     if (prop_idx != SP_ATTR_INVALID) {
-        /** \todo 
-         * effic: Test whether the property is already set before trying to 
-         * convert to string. Alternatively, set from CRTerm directly rather 
+        /** \todo
+         * effic: Test whether the property is already set before trying to
+         * convert to string. Alternatively, set from CRTerm directly rather
          * than converting to string.
          */
         guchar *const str_value_unsigned = cr_term_to_string(decl->value);
@@ -1092,12 +1101,12 @@ sp_repr_sel_eng()
      * Check whether we need to register any pseudo-class handlers.
      * libcroco has its own default handlers for first-child and lang.
      *
-     * We probably want handlers for link and arguably visited (though 
-     * inkscape can't visit links at the time of writing).  hover etc. 
+     * We probably want handlers for link and arguably visited (though
+     * inkscape can't visit links at the time of writing).  hover etc.
      * more useful in inkview than the editor inkscape.
      *
-     * http://www.w3.org/TR/SVG11/styling.html#StylingWithCSS says that 
-     * the following should be honoured, at least by inkview: 
+     * http://www.w3.org/TR/SVG11/styling.html#StylingWithCSS says that
+     * the following should be honoured, at least by inkview:
      * :hover, :active, :focus, :visited, :link.
      */
 
@@ -1133,13 +1142,13 @@ void
 sp_style_merge_from_style_string(SPStyle *const style, gchar const *const p)
 {
     /*
-     * Reference: http://www.w3.org/TR/SVG11/styling.html#StyleAttribute: 
-     * ``When CSS styling is used, CSS inline style is specified by including 
-     * semicolon-separated property declarations of the form "name : value" 
+     * Reference: http://www.w3.org/TR/SVG11/styling.html#StyleAttribute:
+     * ``When CSS styling is used, CSS inline style is specified by including
+     * semicolon-separated property declarations of the form "name : value"
      * within the style attribute''.
      *
-     * That's fairly ambiguous.  Is a `value' allowed to contain semicolons?  
-     * Why does it say "including", what else is allowed in the style 
+     * That's fairly ambiguous.  Is a `value' allowed to contain semicolons?
+     * Why does it say "including", what else is allowed in the style
      * attribute value?
      */
 
@@ -1163,7 +1172,7 @@ sp_style_merge_font_size_from_parent(SPIFontSize &child, SPIFontSize const &pare
         child.computed = parent.computed;
     } else if (child.type == SP_FONT_SIZE_LITERAL) {
         /** \todo
-         * fixme: SVG and CSS do not specify clearly, whether we should use 
+         * fixme: SVG and CSS do not specify clearly, whether we should use
          * user or screen coordinates (Lauris)
          */
         if (child.value < SP_CSS_FONT_SIZE_SMALLER) {
@@ -1199,9 +1208,9 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
     g_return_if_fail(style != NULL);
 
     /** \todo
-     * fixme: Check for existing callers that might pass null parent.  
-     * This should probably be g_return_if_fail, or else we should make a 
-     * best attempt to set computed values correctly without having a parent 
+     * fixme: Check for existing callers that might pass null parent.
+     * This should probably be g_return_if_fail, or else we should make a
+     * best attempt to set computed values correctly without having a parent
      * (i.e., by assuming parent has initial values).
      */
     if (!parent)
@@ -1226,7 +1235,7 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
         style->font_weight.computed = parent->font_weight.computed;
     } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_NORMAL) {
         /** \todo
-         * fixme: This is unconditional, i.e., happens even if parent not 
+         * fixme: This is unconditional, i.e., happens even if parent not
          * present.
          */
         style->font_weight.computed = SP_CSS_FONT_WEIGHT_400;
@@ -1239,15 +1248,15 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
         // expressible in the current font family, but that's difficult to
         // find out, so jumping by 3 seems an appropriate approximation
         style->font_weight.computed = (parent_val <= SP_CSS_FONT_WEIGHT_100 + 3
-                           ? (unsigned)SP_CSS_FONT_WEIGHT_100
-                           : parent_val - 3);
+                                       ? (unsigned)SP_CSS_FONT_WEIGHT_100
+                                       : parent_val - 3);
         g_assert(style->font_weight.computed <= (unsigned) SP_CSS_FONT_WEIGHT_900);
     } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_BOLDER) {
         unsigned const parent_val = parent->font_weight.computed;
         g_assert(parent_val <= SP_CSS_FONT_WEIGHT_900);
         style->font_weight.computed = (parent_val >= SP_CSS_FONT_WEIGHT_900 - 3
-                           ? (unsigned)SP_CSS_FONT_WEIGHT_900
-                           : parent_val + 3);
+                                       ? (unsigned)SP_CSS_FONT_WEIGHT_900
+                                       : parent_val + 3);
         g_assert(style->font_weight.computed <= (unsigned) SP_CSS_FONT_WEIGHT_900);
     }
 
@@ -1257,15 +1266,15 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
     } else if (style->font_stretch.value == SP_CSS_FONT_STRETCH_NARROWER) {
         unsigned const parent_val = parent->font_stretch.computed;
         style->font_stretch.computed = (parent_val == SP_CSS_FONT_STRETCH_ULTRA_CONDENSED
-                        ? parent_val
-                        : parent_val - 1);
+                                        ? parent_val
+                                        : parent_val - 1);
         g_assert(style->font_stretch.computed <= (unsigned) SP_CSS_FONT_STRETCH_ULTRA_EXPANDED);
     } else if (style->font_stretch.value == SP_CSS_FONT_STRETCH_WIDER) {
         unsigned const parent_val = parent->font_stretch.computed;
         g_assert(parent_val <= SP_CSS_FONT_STRETCH_ULTRA_EXPANDED);
         style->font_stretch.computed = (parent_val == SP_CSS_FONT_STRETCH_ULTRA_EXPANDED
-                        ? parent_val
-                        : parent_val + 1);
+                                        ? parent_val
+                                        : parent_val + 1);
         g_assert(style->font_stretch.computed <= (unsigned) SP_CSS_FONT_STRETCH_ULTRA_EXPANDED);
     }
 
@@ -1374,7 +1383,7 @@ sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
 
     if (!style->stroke_dasharray_set && parent->stroke_dasharray_set) {
         /** \todo
-         * This code looks wrong.  Why does the logic differ from the 
+         * This code looks wrong.  Why does the logic differ from the
          * above properties? Similarly dashoffset below.
          */
         style->stroke_dash.n_dash = parent->stroke_dash.n_dash;
@@ -1440,7 +1449,7 @@ sp_style_merge_paint_prop_from_dying_parent(SPStyle *style,
                                             SPIPaint &child, SPIPaint const &parent)
 {
     /** \todo
-     * I haven't given this much attention.  See comments below about 
+     * I haven't given this much attention.  See comments below about
      * currentColor, colorProfile, and relative URIs.
      */
     if (!child.set || child.inherit || child.currentcolor) {
@@ -1476,10 +1485,10 @@ sp_style_merge_rel_enum_prop_from_dying_parent(SPIEnum &child, SPIEnum const &pa
         {
             child.set = false;
             /*
-             * Note that this can result in a change in computed value in the 
-             * rare case that the parent's setting was a no-op (i.e. if the 
-             * parent's parent's computed value was already ultra-condensed or 
-             * ultra-expanded).  However, I'd guess that the change is for the 
+             * Note that this can result in a change in computed value in the
+             * rare case that the parent's setting was a no-op (i.e. if the
+             * parent's parent's computed value was already ultra-condensed or
+             * ultra-expanded).  However, I'd guess that the change is for the
              * better: I'd guess that if the properties were specified
              * relatively, then the intent is to counteract parent's effect.
              * I.e. I believe this is the best code even in that case.
@@ -1487,17 +1496,17 @@ sp_style_merge_rel_enum_prop_from_dying_parent(SPIEnum &child, SPIEnum const &pa
         } else if (child.value == parent.value) {
             /* Leave as is. */
             /** \todo
-             * It's unclear what to do if style and parent specify the same 
-             * relative directive (narrower or wider).  We can either convert 
-             * to absolute specification or coalesce to a single relative 
+             * It's unclear what to do if style and parent specify the same
+             * relative directive (narrower or wider).  We can either convert
+             * to absolute specification or coalesce to a single relative
              * request (of half the strength of the original pair).
              *
-             * Converting to a single level of relative specification is a 
-             * better choice if the newly-unlinked clone is itself cloned to 
-             * other contexts (inheriting different font stretchiness): it 
+             * Converting to a single level of relative specification is a
+             * better choice if the newly-unlinked clone is itself cloned to
+             * other contexts (inheriting different font stretchiness): it
              * would preserve the effect that it will be narrower than
-             * the inherited context in each case.  The disadvantage is that 
-             * it will ~certainly affect the computed value of the 
+             * the inherited context in each case.  The disadvantage is that
+             * it will ~certainly affect the computed value of the
              * newly-unlinked clone.
              */
         } else {
@@ -1530,7 +1539,7 @@ sp_style_merge_length_prop_from_dying_parent(LengthT &child, LengthT const &pare
             case SP_CSS_UNIT_EX:
                 child.value *= parent_child_em_ratio;
                 /** \todo
-                 * fixme: Have separate ex ratio parameter. 
+                 * fixme: Have separate ex ratio parameter.
                  * Get x height from libnrtype or pango.
                  */
                 if (!isFinite(child.value)) {
@@ -1605,7 +1614,7 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare
 
     /* 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 
+     * fixme: We'll need to have more font-related things up the top once
      * we're getting x-height from pango or libnrtype.
      */
 
@@ -1699,8 +1708,8 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare
         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 
+         * 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,
@@ -1757,9 +1766,9 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare
     /* 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.)  
+         * 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
@@ -1847,7 +1856,7 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare
 
         /* 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 ) {
+             && parent->display.value == SP_CSS_DISPLAY_NONE ) {
             style->display.value = SP_CSS_DISPLAY_NONE;
             style->display.set = true;
             style->display.inherit = false;
@@ -1860,8 +1869,8 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare
         }
 
         /** \todo
-         * fixme: Check that we correctly handle all properties that don't 
-         * inherit by default (as shown in 
+         * 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).
          */
     }
@@ -1869,9 +1878,9 @@ sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const pare
     /* 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 
+         * 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[] = {
@@ -1929,13 +1938,13 @@ sp_style_paint_server_release(SPPaintServer *server, SPStyle *style)
     if ((style->fill.type == SP_PAINT_TYPE_PAINTSERVER)
         && (server == style->fill.value.paint.server))
     {
-        sp_style_paint_clear(style, &style->fill, TRUE, FALSE);
+        sp_style_paint_clear(style, &style->fill);
     }
 
     if ((style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)
         && (server == style->stroke.value.paint.server))
     {
-        sp_style_paint_clear(style, &style->stroke, TRUE, FALSE);
+        sp_style_paint_clear(style, &style->stroke);
     }
 }
 
@@ -1954,11 +1963,11 @@ sp_style_paint_server_modified(SPPaintServer *server, guint flags, SPStyle *styl
     {
         if (style->object) {
             /** \todo
-             * fixme: I do not know, whether it is optimal - we are 
+             * fixme: I do not know, whether it is optimal - we are
              * forcing reread of everything (Lauris)
              */
             /** \todo
-             * fixme: We have to use object_modified flag, because parent 
+             * fixme: We have to use object_modified flag, because parent
              * flag is only available downstreams.
              */
             style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
@@ -1983,7 +1992,7 @@ sp_style_paint_server_modified(SPPaintServer *server, guint flags, SPStyle *styl
 static void
 sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent)
 {
-    sp_style_paint_clear(style, paint, TRUE, FALSE);
+    sp_style_paint_clear(style, paint);
 
     if ((paint->set && paint->currentcolor) || parent->currentcolor) {
         paint->currentcolor = TRUE;
@@ -2002,15 +2011,25 @@ sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent)
             paint->value.paint.uri = parent->value.paint.uri;
             if (paint->value.paint.server) {
                 if (style->object && !style->cloned) { // href paintserver for style of non-clones only
-                    style->hreffed = true;
                     sp_object_href(SP_OBJECT(paint->value.paint.server), style);
+                    if (paint == &style->fill) {
+                        style->fill_hreffed = true;
+                    } else {
+                        assert(paint == &style->stroke);
+                        style->stroke_hreffed = true;
+                    }
                 }
                 if (style->object || style->cloned) { // connect to signals for style of real objects or clones (this excludes temp styles)
                     g_signal_connect(G_OBJECT(paint->value.paint.server), "release",
                                      G_CALLBACK(sp_style_paint_server_release), style);
                     g_signal_connect(G_OBJECT(paint->value.paint.server), "modified",
                                      G_CALLBACK(sp_style_paint_server_modified), style);
-                    style->listening = true;
+                    if (paint == &style->fill) {
+                        style->fill_listening = true;
+                    } else {
+                        assert(paint == &style->stroke);
+                        style->stroke_listening = true;
+                    }
                 }
             }
             break;
@@ -2024,10 +2043,10 @@ sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent)
 
 
 /**
- * Dumps the style to a CSS string, with either SP_STYLE_FLAG_IFSET or 
- * SP_STYLE_FLAG_ALWAYS flags. Used with Always for copying an object's 
- * complete cascaded style to style_clipboard. When you need a CSS string 
- * for an object in the document tree, you normally call 
+ * Dumps the style to a CSS string, with either SP_STYLE_FLAG_IFSET or
+ * SP_STYLE_FLAG_ALWAYS flags. Used with Always for copying an object's
+ * complete cascaded style to style_clipboard. When you need a CSS string
+ * for an object in the document tree, you normally call
  * sp_style_write_difference instead to take into account the object's parent.
  *
  * \pre style != NULL.
@@ -2164,7 +2183,7 @@ sp_style_write_string(SPStyle const *const style, guint const flags)
 
 /**
  * Dumps style to CSS string, see sp_style_write_string()
- * 
+ *
  * \pre from != NULL.
  * \pre to != NULL.
  * \post ret != NULL.
@@ -2198,7 +2217,7 @@ sp_style_write_difference(SPStyle const *const from, SPStyle const *const to)
 
     p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &from->text_anchor, &to->text_anchor, SP_STYLE_FLAG_IFDIFF);
 
-    /// \todo fixme: Per type methods need default flag too 
+    /// \todo fixme: Per type methods need default flag too
     if (from->opacity.set && from->opacity.value != SP_SCALE24_MAX) {
         p += sp_style_write_iscale24(p, c + BMAX - p, "opacity", &from->opacity, &to->opacity, SP_STYLE_FLAG_IFSET);
     }
@@ -2287,8 +2306,8 @@ sp_style_clear(SPStyle *style)
 {
     g_return_if_fail(style != NULL);
 
-    sp_style_paint_clear(style, &style->fill, TRUE, FALSE);
-    sp_style_paint_clear(style, &style->stroke, TRUE, FALSE);
+    sp_style_paint_clear(style, &style->fill);
+    sp_style_paint_clear(style, &style->stroke);
     if (style->stroke_dash.dash) {
         g_free(style->stroke_dash.dash);
     }
@@ -2413,7 +2432,7 @@ sp_style_clear(SPStyle *style)
 
 
 /**
- * 
+ *
  */
 static void
 sp_style_read_dash(SPStyle *style, gchar const *str)
@@ -2667,9 +2686,9 @@ sp_style_read_ilength(SPILength *val, gchar const *str)
         /** \todo fixme: Move this to standard place (Lauris) */
         value = g_ascii_strtod(str, &e);
         if ((gchar const *) e != str) {
-            /** \todo 
-             * Allow the number of px per inch to vary (document preferences, 
-             * X server or whatever).  E.g. don't fill in computed here, do 
+            /** \todo
+             * Allow the number of px per inch to vary (document preferences,
+             * X server or whatever).  E.g. don't fill in computed here, do
              * it at the same time as percentage units are done.
              */
             if (!*e) {
@@ -2818,7 +2837,9 @@ sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume
 
 
 /**
- * Set SPIPaint object from string. 
+ * Set SPIPaint object from string.
+ *
+ * \pre paint == \&style.fill || paint == \&style.stroke.
  */
 static void
 sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document)
@@ -2831,16 +2852,16 @@ sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume
         paint->set = TRUE;
         paint->inherit = TRUE;
         paint->currentcolor = FALSE;
-    } else if (streq(str, "currentColor")) {
+    } else if (streq(str, "currentColor") && paint != &style->color) {
         paint->set = TRUE;
         paint->inherit = FALSE;
         paint->currentcolor = TRUE;
-    } else if (streq(str, "none")) {
+    } else if (streq(str, "none") && paint != &style->color) {
         paint->type = SP_PAINT_TYPE_NONE;
         paint->set = TRUE;
         paint->inherit = FALSE;
         paint->currentcolor = FALSE;
-    } else if (strneq(str, "url", 3)) {
+    } else if (strneq(str, "url", 3) && paint != &style->color) {
         // this is alloc'd uri, but seems to be shared with a parent
         // potentially, so it's not any easy g_free case...
         paint->value.paint.uri = extract_uri(str);
@@ -2858,27 +2879,49 @@ sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume
                 paint->value.paint.server = SP_PAINT_SERVER(ps);
                 if (style->object && !style->cloned) {
                     sp_object_href(SP_OBJECT(paint->value.paint.server), style);
-                    style->hreffed = true;
+                    if (paint == &style->fill) {
+                        style->fill_hreffed = true;
+                    } else {
+                        assert(paint == &style->stroke);
+                        style->stroke_hreffed = true;
+                    }
                 }
                 if (style->object || style->cloned) {
                     g_signal_connect(G_OBJECT(paint->value.paint.server), "release",
                                      G_CALLBACK(sp_style_paint_server_release), style);
                     g_signal_connect(G_OBJECT(paint->value.paint.server), "modified",
                                      G_CALLBACK(sp_style_paint_server_modified), style);
-                    style->listening = true;
+                    if (paint == &style->fill) {
+                        style->fill_listening = true;
+                    } else {
+                        assert(paint == &style->stroke);
+                        style->stroke_listening = true;
+                    }
                 }
             } else {
                 paint->value.paint.server = NULL;
             }
         }
     } else {
-        guint32 const rgb0 = sp_svg_read_color(str, 0xff);
+        guint32 const rgb0 = sp_svg_read_color(str, &str, 0xff);
         if (rgb0 != 0xff) {
             paint->type = SP_PAINT_TYPE_COLOR;
             sp_color_set_rgb_rgba32(&paint->value.color, rgb0);
             paint->set = TRUE;
             paint->inherit = FALSE;
             paint->currentcolor = FALSE;
+
+            while (g_ascii_isspace(*str)) {
+                ++str;
+            }
+            if (strneq(str, "icc-color(", 10)) {
+                SVGICCColor* tmp = new SVGICCColor();
+                if ( ! sp_svg_read_icc_color( str, &str, tmp ) ) {
+                    delete tmp;
+                    tmp = 0;
+                }
+                paint->iccColor = tmp;
+            }
         }
     }
 }
@@ -3106,7 +3149,7 @@ sp_style_write_istring(gchar *p, gint const len, gchar const *const key,
 
 
 /**
- * 
+ *
  */
 static bool
 sp_length_differ(SPILength const *const a, SPILength const *const b)
@@ -3298,7 +3341,13 @@ sp_paint_differ(SPIPaint const *const a, SPIPaint const *const b)
     if (a->type != b->type)
         return true;
     if (a->type == SP_PAINT_TYPE_COLOR)
-        return !sp_color_is_equal(&a->value.color, &b->value.color);
+        return !(sp_color_is_equal(&a->value.color, &b->value.color)
+                 && ((a->iccColor == b->iccColor)
+                     || (a->iccColor && b->iccColor
+                         && (a->iccColor->colorProfile == b->iccColor->colorProfile)
+                         && (a->iccColor->colors == b->iccColor->colors))));
+    /* todo: Allow for epsilon differences in iccColor->colors, e.g. changes small enough not to show up
+     * in the string representation. */
     if (a->type == SP_PAINT_TYPE_PAINTSERVER)
         return (a->value.paint.server != b->value.paint.server);
     return false;
@@ -3324,14 +3373,27 @@ sp_style_write_ipaint(gchar *b, gint const len, gchar const *const key,
             return g_snprintf(b, len, "%s:currentColor;", key);
         } else {
             switch (paint->type) {
-            case SP_PAINT_TYPE_COLOR:
-                return g_snprintf(b, len, "%s:#%06x;", key, sp_color_get_rgba32_falpha(&paint->value.color, 0.0) >> 8);
-                break;
-            case SP_PAINT_TYPE_PAINTSERVER:
+                case SP_PAINT_TYPE_COLOR: {
+                    char color_buf[8];
+                    sp_svg_write_color(color_buf, sizeof(color_buf), sp_color_get_rgba32_ualpha(&paint->value.color, 0));
+                    if (paint->iccColor) {
+                        CSSOStringStream css;
+                        css << color_buf << " icc-color(" << paint->iccColor->colorProfile;
+                        for (vector<double>::const_iterator i(paint->iccColor->colors.begin()),
+                                 iEnd(paint->iccColor->colors.end());
+                             i != iEnd; ++i) {
+                            css << ", " << *i;
+                        }
+                        css << ')';
+                        return g_snprintf(b, len, "%s:%s;", key, css.gcharp());
+                    } else {
+                        return g_snprintf(b, len, "%s:%s;", key, color_buf);
+                    }
+                }
+                case SP_PAINT_TYPE_PAINTSERVER:
                     return g_snprintf(b, len, "%s:url(%s);", key, paint->value.paint.uri);
-                break;
-            default:
-                break;
+                default:
+                    break;
             }
             return g_snprintf(b, len, "%s:none;", key);
         }
@@ -3395,31 +3457,41 @@ sp_style_write_ifontsize(gchar *p, gint const len, gchar const *key,
 
 
 /**
- * Clear paint object; conditionally disconnect style from paintserver.
+ * Clear paint object, and disconnect style from paintserver (if present).
  */
 static void
-sp_style_paint_clear(SPStyle *style, SPIPaint *paint,
-                     unsigned hunref, unsigned unset)
+sp_style_paint_clear(SPStyle *style, SPIPaint *paint)
 {
-    if (hunref && (paint->type == SP_PAINT_TYPE_PAINTSERVER) && paint->value.paint.server) {
-        if (style->hreffed) {
-            sp_object_hunref(SP_OBJECT(paint->value.paint.server), style);
-            style->hreffed = false;
-        }
-        if (style->listening) {
-            g_signal_handlers_disconnect_matched(G_OBJECT(paint->value.paint.server),
+    if ((paint->type == SP_PAINT_TYPE_PAINTSERVER) && paint->value.paint.server) {
+        if (paint == &style->fill) {
+            if (style->fill_hreffed) {
+                sp_object_hunref(SP_OBJECT(paint->value.paint.server), style);
+                style->fill_hreffed = false;
+            }
+            if (style->fill_listening) {
+                g_signal_handlers_disconnect_matched(G_OBJECT(paint->value.paint.server),
+                                                 G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, style);
+                style->fill_listening = false;
+            }
+        } else {
+            assert(paint == &style->stroke);  // Only fill & stroke can have a paint server.
+            if (style->stroke_hreffed) {
+                sp_object_hunref(SP_OBJECT(paint->value.paint.server), style);
+                style->stroke_hreffed = false;
+            }
+            if (style->stroke_listening) {
+                g_signal_handlers_disconnect_matched(G_OBJECT(paint->value.paint.server),
                                                  G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, style);
-            style->listening = false;
+                style->stroke_listening = false;
+            }
         }
-        paint->value.paint.server = NULL;
-        paint->value.paint.uri = NULL;
-        paint->type = SP_PAINT_TYPE_NONE;
-    }
 
-    if (unset) {
-        paint->set = FALSE;
-        paint->inherit = FALSE;
+        paint->value.paint.server = NULL;
     }
+    paint->value.paint.uri = NULL;
+    paint->type = SP_PAINT_TYPE_NONE;
+    delete paint->iccColor;
+    paint->iccColor = NULL;
 }
 
 /**
@@ -3527,7 +3599,7 @@ sp_css_attr_from_object(SPObject *object, guint const flags)
     SPStyle const *const style = SP_OBJECT_STYLE(object);
     if (style == NULL)
         return NULL;
-    return sp_css_attr_from_style (style, flags);
+    return sp_css_attr_from_style(style, flags);
 }
 
 /**
@@ -3569,16 +3641,16 @@ is_url(char const *p)
     if (p == NULL)
         return false;
 /** \todo
- * FIXME: I'm not sure if this applies to SVG as well, but CSS2 says any URIs 
+ * FIXME: I'm not sure if this applies to SVG as well, but CSS2 says any URIs
  * in property values must start with 'url('.
  */
     return (g_ascii_strncasecmp(p, "url(", 4) == 0);
 }
 
 /**
- * Unset any properties that contain URI values. 
+ * Unset any properties that contain URI values.
  *
- * Used for storing style that will be reused across documents when carrying 
+ * Used for storing style that will be reused across documents when carrying
  * the referenced defs is impractical.
  */
 SPCSSAttr *
@@ -3677,23 +3749,23 @@ sp_css_attr_scale(SPCSSAttr *css, double ex)
 
 /**
  * Remove quotes from SPIString object value.
- * 
- * \todo FIXME: now used for font family, but perhaps this should apply to 
- * ALL strings (check CSS spec), in which case this should be part of 
+ *
+ * \todo FIXME: now used for font family, but perhaps this should apply to
+ * ALL strings (check CSS spec), in which case this should be part of
  * read_istring.
  */
-void
-css2_unescape_unquote (SPIString *val)
+static void
+css2_unescape_unquote(SPIString *val)
 {
     if (val->set && val->value && strlen(val->value) >= 2) {
 
-       /// \todo unescape all \-escaped chars
+        /// \todo unescape all \-escaped chars
 
         int l = strlen(val->value);
-        if ((val->value[0] == '"' && val->value[l - 1] == '"') || 
-            (val->value[0] == '\'' && val->value[l - 1] == '\'')) {
-                memcpy (val->value, val->value+1, l - 2);
-                val->value[l - 2] = '\0';
+        if ( ( val->value[0] == '"' && val->value[l - 1] == '"' )  ||
+             ( val->value[0] == '\'' && val->value[l - 1] == '\'' )  ) {
+            memcpy(val->value, val->value + 1, l - 2);
+            val->value[l - 2] = '\0';
         }
     }
 }