Code

rationalize function names and remove spurious soft_ptr
[inkscape.git] / src / style.cpp
index ee67469ade716ac45dc3e9710748d83d23b53aa3..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);
     }
@@ -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:
@@ -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);
     }
 
@@ -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;
@@ -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);
     }
 }
 
@@ -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;
@@ -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);
     }
@@ -2819,6 +2838,8 @@ sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocume
 
 /**
  * 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;
+            }
         }
     }
 }
@@ -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);
 }
 
 /**
@@ -3682,18 +3754,18 @@ sp_css_attr_scale(SPCSSAttr *css, double ex)
  * 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';
         }
     }
 }