summaryrefslogtreecommitdiffstats
path: root/src/style-internal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/style-internal.cpp')
-rw-r--r--src/style-internal.cpp2648
1 files changed, 2648 insertions, 0 deletions
diff --git a/src/style-internal.cpp b/src/style-internal.cpp
new file mode 100644
index 000000000..b858e5cb6
--- /dev/null
+++ b/src/style-internal.cpp
@@ -0,0 +1,2648 @@
+/**
+ * @file
+ * SVG stylesheets implementation - Classes used by SPStyle class.
+ */
+
+/* Authors:
+ * C++ conversion:
+ * Tavmjong Bah <tavmjong@free.fr>
+ * Legacy C implementation:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Peter Moulder <pmoulder@mail.csse.monash.edu.au>
+ * bulia byak <buliabyak@users.sf.net>
+ * Abhishek Sharma
+ * Kris De Gussem <Kris.DeGussem@gmail.com>
+ *
+ * Copyright (C) 2001-2002 Lauris Kaplinski
+ * Copyright (C) 2001 Ximian, Inc.
+ * Copyright (C) 2005 Monash University
+ * Copyright (C) 2012 Kris De Gussem
+ * Copyright (C) 2014 Tavmjong Bah
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "style-internal.h"
+#include "style-enums.h"
+#include "style.h"
+
+#include "svg/svg.h"
+#include "svg/svg-color.h"
+#include "svg/svg-icc-color.h"
+
+#include "streq.h"
+#include "strneq.h"
+
+#include "extract-uri.h"
+#include "preferences.h"
+#include "svg/css-ostringstream.h"
+#include "util/units.h"
+
+#include <sigc++/functors/ptr_fun.h>
+#include <sigc++/adaptors/bind.h>
+
+// TODO REMOVE OR MAKE MEMBER FUNCTIONS
+void sp_style_fill_paint_server_ref_changed( SPObject *old_ref, SPObject *ref, SPStyle *style);
+void sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style);
+void sp_style_filter_ref_changed( SPObject *old_ref, SPObject *ref, SPStyle *style);
+void sp_style_set_ipaint_to_uri(SPStyle *style, SPIPaint *paint, const Inkscape::URI *uri, SPDocument *document);
+void sp_style_set_ipaint_to_uri_string (SPStyle *style, SPIPaint *paint, const gchar *uri);
+
+using Inkscape::CSSOStringStream;
+
+// SPIBase --------------------------------------------------------------
+
+
+// SPIFloat -------------------------------------------------------------
+
+void
+SPIFloat::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if ( !strcmp(str, "inherit") ) {
+ set = true;
+ inherit = true;
+ } else {
+ gfloat value_tmp;
+ if (sp_svg_number_read_f(str, &value_tmp)) {
+ set = true;
+ inherit = false;
+ value = value_tmp;
+ }
+ }
+}
+
+const Glib::ustring
+SPIFloat::write( guint const flags, SPIBase const *const base) const {
+
+ SPIFloat const *const my_base = dynamic_cast<const SPIFloat*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ if (this->inherit) {
+ return (name + ":inherit;");
+ } else {
+ Inkscape::CSSOStringStream os;
+ os << name << ":" << this->value << ";";
+ return os.str();
+ }
+ }
+ return Glib::ustring("");
+}
+
+void
+SPIFloat::cascade( const SPIBase* const parent ) {
+ if( const SPIFloat* p = dynamic_cast<const SPIFloat*>(parent) ) {
+ if( (inherits && !set) || inherit ) value = p->value;
+ } else {
+ std::cerr << "SPIFloat::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPIFloat::merge( const SPIBase* const parent ) {
+ if( const SPIFloat* p = dynamic_cast<const SPIFloat*>(parent) ) {
+ if( inherits ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ value = p->value;
+ }
+ }
+ } else {
+ std::cerr << "SPIFloat::merge(): Incorrect parent type" << std::endl;
+ }
+}
+
+bool
+SPIFloat::operator==(const SPIBase& rhs) {
+ if( const SPIFloat* r = dynamic_cast<const SPIFloat*>(&rhs) ) {
+ return (value == r->value && SPIBase::operator==(rhs));
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIScale24 -----------------------------------------------------------
+
+void
+SPIScale24::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if ( !strcmp(str, "inherit") ) {
+ set = true;
+ inherit = true;
+ } else {
+ gfloat value_in;
+ if (sp_svg_number_read_f(str, &value_in)) {
+ set = true;
+ inherit = false;
+ value_in = CLAMP(value_in, 0.0, 1.0);
+ value = SP_SCALE24_FROM_FLOAT( value_in );
+ }
+ }
+}
+
+const Glib::ustring
+SPIScale24::write( guint const flags, SPIBase const *const base) const {
+
+ SPIScale24 const *const my_base = dynamic_cast<const SPIScale24*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ if (this->inherit) {
+ return (name + ":inherit;");
+ } else {
+ Inkscape::CSSOStringStream os;
+ os << name << ":" << SP_SCALE24_TO_FLOAT(this->value) << ";";
+ return os.str();
+ }
+ }
+ return Glib::ustring("");
+}
+
+void
+SPIScale24::cascade( const SPIBase* const parent ) {
+ if( const SPIScale24* p = dynamic_cast<const SPIScale24*>(parent) ) {
+ if( (inherits && !set) || inherit ) value = p->value;
+ } else {
+ std::cerr << "SPIScale24::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPIScale24::merge( const SPIBase* const parent ) {
+ if( const SPIScale24* p = dynamic_cast<const SPIScale24*>(parent) ) {
+ if( inherits ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ value = p->value;
+ }
+ } else {
+ // Needed only for 'opacity' which does not inherit. See comment at bottom of file.
+ if( name.compare( "opacity" ) != 0 )
+ std::cerr << "SPIScale24::merge: unhandled property: " << name << std::endl;
+ if( !set || (!inherit && value == SP_SCALE24_MAX) ) {
+ value = p->value;
+ } else {
+ if( inherit ) value = p->value; // Insures child is up-to-date
+ value = SP_SCALE24_MUL( value, p->value );
+ inherit = (inherit && p->inherit && (p->value == 0 || p->value == SP_SCALE24_MAX) );
+ set = (inherit || value < SP_SCALE24_MAX);
+ }
+ }
+ } else {
+ std::cerr << "SPIScale24::merge(): Incorrect parent type" << std::endl;
+ }
+}
+
+bool
+SPIScale24::operator==(const SPIBase& rhs) {
+ if( const SPIScale24* r = dynamic_cast<const SPIScale24*>(&rhs) ) {
+ return (value == r->value && SPIBase::operator==(rhs));
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPILength ------------------------------------------------------------
+
+void
+SPILength::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if (!strcmp(str, "inherit")) {
+ set = true;
+ inherit = true;
+ unit = SP_CSS_UNIT_NONE;
+ value = computed = 0.0;
+ } else {
+ gdouble value_tmp;
+ gchar *e;
+ /** \todo fixme: Move this to standard place (Lauris) */
+ value_tmp = g_ascii_strtod(str, &e);
+ if ( !IS_FINITE(value_tmp) ) { // fix for bug lp:935157
+ return;
+ }
+ if ((gchar const *) e != str) {
+
+ value = value_tmp;
+ if (!*e) {
+ /* Userspace */
+ unit = SP_CSS_UNIT_NONE;
+ computed = value;
+ } else if (!strcmp(e, "px")) {
+ /* Userspace */
+ unit = SP_CSS_UNIT_PX;
+ computed = value;
+ } else if (!strcmp(e, "pt")) {
+ /* Userspace / DEVICESCALE */
+ unit = SP_CSS_UNIT_PT;
+ computed = Inkscape::Util::Quantity::convert(value, "pt", "px");
+ } else if (!strcmp(e, "pc")) {
+ unit = SP_CSS_UNIT_PC;
+ computed = Inkscape::Util::Quantity::convert(value, "pc", "px");
+ } else if (!strcmp(e, "mm")) {
+ unit = SP_CSS_UNIT_MM;
+ computed = Inkscape::Util::Quantity::convert(value, "mm", "px");
+ } else if (!strcmp(e, "cm")) {
+ unit = SP_CSS_UNIT_CM;
+ computed = Inkscape::Util::Quantity::convert(value, "cm", "px");
+ } else if (!strcmp(e, "in")) {
+ unit = SP_CSS_UNIT_IN;
+ computed = Inkscape::Util::Quantity::convert(value, "in", "px");
+ } else if (!strcmp(e, "em")) {
+ /* EM square */
+ unit = SP_CSS_UNIT_EM;
+ if( style ) {
+ computed = value * style->font_size.computed;
+ } else {
+ computed = value * SPIFontSize::font_size_default;
+ }
+ } else if (!strcmp(e, "ex")) {
+ /* ex square */
+ unit = SP_CSS_UNIT_EX;
+ if( style ) {
+ computed = value * style->font_size.computed * 0.5; // FIXME
+ } else {
+ computed = value * SPIFontSize::font_size_default * 0.5;
+ }
+ } else if (!strcmp(e, "%")) {
+ /* Percentage */
+ unit = SP_CSS_UNIT_PERCENT;
+ value = value * 0.01;
+ if (name.compare( "line-height" ) == 0) {
+ // See: http://www.w3.org/TR/CSS2/visudet.html#propdef-line-height
+ if( style ) {
+ computed = value * style->font_size.computed;
+ } else {
+ computed = value * SPIFontSize::font_size_default;
+ }
+ }
+ } else {
+ /* Invalid */
+ return;
+ }
+ set = true;
+ inherit = false;
+ }
+ }
+}
+
+const Glib::ustring
+SPILength::write( guint const flags, SPIBase const *const base) const {
+
+ SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ if (this->inherit) {
+ return (name + ":inherit;");
+ } else {
+ Inkscape::CSSOStringStream os;
+ switch (this->unit) {
+ case SP_CSS_UNIT_NONE:
+ os << name << ":" << this->computed << ";";
+ break;
+ case SP_CSS_UNIT_PX:
+ os << name << ":" << this->computed << "px;";
+ break;
+ case SP_CSS_UNIT_PT:
+ os << name << ":" << Inkscape::Util::Quantity::convert(this->computed, "px", "pt") << "pt;";
+ break;
+ case SP_CSS_UNIT_PC:
+ os << name << ":" << Inkscape::Util::Quantity::convert(this->computed, "px", "pc") << "pc;";
+ break;
+ case SP_CSS_UNIT_MM:
+ os << name << ":" << Inkscape::Util::Quantity::convert(this->computed, "px", "mm") << "mm;";
+ break;
+ case SP_CSS_UNIT_CM:
+ os << name << ":" << Inkscape::Util::Quantity::convert(this->computed, "px", "cm") << "cm;";
+ break;
+ case SP_CSS_UNIT_IN:
+ os << name << ":" << Inkscape::Util::Quantity::convert(this->computed, "px", "in") << "in;";
+ break;
+ case SP_CSS_UNIT_EM:
+ os << name << ":" << this->value << "em;";
+ break;
+ case SP_CSS_UNIT_EX:
+ os << name << ":" << this->value << "ex;";
+ break;
+ case SP_CSS_UNIT_PERCENT:
+ os << name << ":" << (this->value * 100.0) << "%;";
+ break;
+ default:
+ /* Invalid */
+ break;
+ }
+ return os.str();
+ }
+ }
+ return Glib::ustring("");
+}
+
+void
+SPILength::cascade( const SPIBase* const parent ) {
+ if( const SPILength* p = dynamic_cast<const SPILength*>(parent) ) {
+ if( (inherits && !set) || inherit ) {
+ unit = p->unit;
+ value = p->value;
+ computed = p->computed;
+ } else {
+ // Recalculate based on new font-size, font-family inherited from parent
+ double const em = style->font_size.computed;
+ if (unit == SP_CSS_UNIT_EM) {
+ computed = value * em;
+ } else if (unit == SP_CSS_UNIT_EX) {
+ // FIXME: Get x height from libnrtype or pango.
+ computed = value * em * 0.5;
+ } else if (unit == SP_CSS_UNIT_PERCENT && name.compare( "line-height" ) == 0 ) {
+ // Special case
+ computed = value * em;
+ }
+ }
+ } else {
+ std::cerr << "SPILength::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPILength::merge( const SPIBase* const parent ) {
+ if( const SPILength* p = dynamic_cast<const SPILength*>(parent) ) {
+ if( inherits ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ unit = p->unit;
+ value = p->value;
+ computed = p->computed;
+
+ // Fix up so values are correct
+ switch (p->unit) {
+ case SP_CSS_UNIT_EM:
+ case SP_CSS_UNIT_EX:
+ g_assert( &style->font_size != NULL && &p->style->font_size != NULL );
+ value *= p->style->font_size.computed / style->font_size.computed;
+ /** \todo
+ * FIXME: Have separate ex ratio parameter.
+ * Get x height from libnrtype or pango.
+ */
+ if (!IS_FINITE(value)) {
+ value = computed;
+ unit = SP_CSS_UNIT_NONE;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ } else {
+ std::cerr << "SPIFloat::merge(): Incorrect parent type" << std::endl;
+ }
+}
+
+bool
+SPILength::operator==(const SPIBase& rhs) {
+ if( const SPILength* r = dynamic_cast<const SPILength*>(&rhs) ) {
+
+ if( unit != r->unit ) return false;
+
+ // If length depends on external parameter, lengths cannot be equal.
+ if (unit == SP_CSS_UNIT_EM) return false;
+ if (unit == SP_CSS_UNIT_EX) return false;
+ if (unit == SP_CSS_UNIT_PERCENT) return false;
+ if (r->unit == SP_CSS_UNIT_EM) return false;
+ if (r->unit == SP_CSS_UNIT_EX) return false;
+ if (r->unit == SP_CSS_UNIT_PERCENT) return false;
+
+ return (computed == r->computed );
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPILengthOrNormal ----------------------------------------------------
+
+void
+SPILengthOrNormal::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if ( !strcmp(str, "normal") ) {
+ set = true;
+ inherit = false;
+ unit = SP_CSS_UNIT_NONE;
+ value = computed = 0.0;
+ normal = true;
+ } else {
+ SPILength::read( str );
+ normal = false;
+ }
+};
+
+const Glib::ustring
+SPILengthOrNormal::write( guint const flags, SPIBase const *const base) const {
+
+ SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ if (this->normal) {
+ return (name + ":normal;");
+ } else {
+ return SPILength::write(flags, base);
+ }
+ }
+ return Glib::ustring("");
+}
+
+void
+SPILengthOrNormal::cascade( const SPIBase* const parent ) {
+ if( const SPILengthOrNormal* p = dynamic_cast<const SPILengthOrNormal*>(parent) ) {
+ if( (inherits && !set) || inherit ) {
+ normal = p->normal;
+ }
+ SPILength::cascade( parent );
+ } else {
+ std::cerr << "SPILengthOrNormal::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPILengthOrNormal::merge( const SPIBase* const parent ) {
+ if( const SPILengthOrNormal* p = dynamic_cast<const SPILengthOrNormal*>(parent) ) {
+ if( inherits ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ normal = p->normal;
+ SPILength::merge( parent );
+ }
+ }
+ }
+}
+
+bool
+SPILengthOrNormal::operator==(const SPIBase& rhs) {
+ if( const SPILengthOrNormal* r = dynamic_cast<const SPILengthOrNormal*>(&rhs) ) {
+ if( normal && r->normal ) { return true; }
+ if( normal != r->normal ) { return false; }
+ return SPILength::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIEnum --------------------------------------------------------------
+
+void
+SPIEnum::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if( !strcmp(str, "inherit") ) {
+ set = true;
+ inherit = true;
+ } else {
+ for (unsigned i = 0; enums[i].key; i++) {
+ if (!strcmp(str, enums[i].key)) {
+ set = true;
+ inherit = false;
+ value = enums[i].value;
+ /* Save copying for values not needing it */
+ computed = value;
+ break;
+ }
+ }
+ // The following is defined in CSS 2.1
+ if( name.compare("font-weight" ) == 0 ) {
+ if( value == SP_CSS_FONT_WEIGHT_NORMAL ) {
+ computed = SP_CSS_FONT_WEIGHT_400;
+ } else if (value == SP_CSS_FONT_WEIGHT_BOLD ) {
+ computed = SP_CSS_FONT_WEIGHT_700;
+ }
+ }
+ }
+}
+
+const Glib::ustring
+SPIEnum::write( guint const flags, SPIBase const *const base) const {
+
+ SPIEnum const *const my_base = dynamic_cast<const SPIEnum*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ if (this->inherit) {
+ return (name + ":inherit;");
+ }
+ for (unsigned i = 0; enums[i].key; ++i) {
+ if (enums[i].value == static_cast< gint > (this->value) ) {
+ return (name + ":" + enums[i].key + ";");
+ }
+ }
+ }
+ return Glib::ustring("");
+}
+
+void
+SPIEnum::cascade( const SPIBase* const parent ) {
+ if( const SPIEnum* p = dynamic_cast<const SPIEnum*>(parent) ) {
+ if( inherits && (!set || inherit) ) {
+ computed = p->computed;
+ } else {
+ if( name.compare("font-stretch" ) == 0 ) {
+ unsigned const parent_val = p->computed;
+ if( value == SP_CSS_FONT_STRETCH_NARROWER ) {
+ computed = (parent_val == SP_CSS_FONT_STRETCH_ULTRA_CONDENSED ?
+ parent_val : parent_val - 1);
+ } else if (value == SP_CSS_FONT_STRETCH_WIDER ) {
+ computed = (parent_val == SP_CSS_FONT_STRETCH_ULTRA_EXPANDED ?
+ parent_val : parent_val + 1);
+ }
+ }
+ // strictly, 'bolder' and 'lighter' should go to the next weight
+ // expressible in the current font family, but that's difficult to
+ // find out, so jumping by 3 seems an appropriate approximation
+ if( name.compare("font-weight" ) == 0 ) {
+ unsigned const parent_val = p->computed;
+ if( value == SP_CSS_FONT_WEIGHT_LIGHTER ) {
+ computed = (parent_val <= SP_CSS_FONT_WEIGHT_100 + 3 ?
+ (unsigned)SP_CSS_FONT_WEIGHT_100 : parent_val - 3);
+ } else if (value == SP_CSS_FONT_WEIGHT_BOLDER ) {
+ computed = (parent_val >= SP_CSS_FONT_WEIGHT_900 - 3 ?
+ (unsigned)SP_CSS_FONT_WEIGHT_900 : parent_val + 3);
+ }
+ }
+ }
+ } else {
+ std::cerr << "SPIEnum::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+// FIXME Handle font_stretch and font_weight (relative values) New derived class?
+void
+SPIEnum::merge( const SPIBase* const parent ) {
+ if( const SPIEnum* p = dynamic_cast<const SPIEnum*>(parent) ) {
+ if( inherits ) {
+ if( p->set && !p->inherit ) {
+ if( !set || inherit ) {
+ set = p->set;
+ inherit = p->inherit;
+ value = p->value;
+ computed = p->computed; // Different from value for font-weight and font-stretch
+ } else {
+ // The following is to special case 'font-stretch' and 'font-weight'
+ unsigned max_computed_val = 100;
+ unsigned smaller_val = 100;
+ if( name.compare("font-stretch" ) == 0 ) {
+ max_computed_val = SP_CSS_FONT_STRETCH_ULTRA_EXPANDED;
+ smaller_val = SP_CSS_FONT_STRETCH_NARROWER;
+ } else if( name.compare("font-weight" ) == 0 ) {
+ max_computed_val = SP_CSS_FONT_WEIGHT_900;
+ smaller_val = SP_CSS_FONT_WEIGHT_LIGHTER;
+ }
+ unsigned const min_computed_val = 0;
+ unsigned const larger_val = smaller_val + 1;
+ if( value < smaller_val ) {
+ // Child has absolute value, leave as is.
+ // Works for all enum properties
+ } else if( (value == smaller_val && p->value == larger_val ) ||
+ (value == larger_val && p->value == smaller_val) ) {
+ // Values cancel, unset
+ set = false;
+ } else if( value == p->value ) {
+ // Leave as is, what does applying "wider" twice do?
+ } else {
+ // Child is smaller or larger, adjust parent value accordingly
+ unsigned const parent_val = p->computed;
+ value = (value == smaller_val ?
+ ( parent_val == min_computed_val ? parent_val : parent_val - 1 ) :
+ ( parent_val == max_computed_val ? parent_val : parent_val + 1 ) );
+ g_assert(value <= max_computed_val);
+ inherit = false;
+ g_assert(set);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool
+SPIEnum::operator==(const SPIBase& rhs) {
+ if( const SPIEnum* r = dynamic_cast<const SPIEnum*>(&rhs) ) {
+ return (computed == r->computed && SPIBase::operator==(rhs));
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIString ------------------------------------------------------------
+
+void
+SPIString::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if (!strcmp(str, "inherit")) {
+ set = true;
+ inherit = true;
+ value = NULL;
+ } else {
+ set = true;
+ inherit = false;
+
+ Glib::ustring str_temp(str);
+ if( name.compare( "font-family" ) == 0 ) {
+ // Family names may be quoted in CSS, internally we use unquoted names.
+ css_font_family_unquote( str_temp );
+ } else if( name.compare( "-inkscape-font-specification" ) == 0 ) {
+ css_unquote( str_temp );
+ }
+
+ value = g_strdup(str_temp.c_str());
+ }
+}
+
+
+// This routine is actually rarely used. Writing is done usually
+// in sp_repr_css_write_string...
+const Glib::ustring
+SPIString::write( guint const flags, SPIBase const *const base) const {
+
+ SPIString const *const my_base = dynamic_cast<const SPIString*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ if (this->inherit) {
+ return (name + ":inherit;");
+ } else {
+ if( this->value ) {
+ if( name.compare( "font-family" ) == 0 ) {
+ Glib::ustring font_family( this->value );
+ css_font_family_quote( font_family );
+ return (name + ":" + font_family + ";");
+ } else if( name.compare( "-inkscape-font-specification" ) == 0 ) {
+ Glib::ustring font_spec( this->value );
+ css_quote( font_spec );
+ return (name + ":" + font_spec + ";");
+ } else {
+ return (name + ":" + this->value + ";");
+ }
+ }
+ }
+ }
+ return Glib::ustring("");
+}
+
+void
+SPIString::clear() {
+ SPIBase::clear();
+ g_free( value );
+ value = NULL;
+ if( value_default ) value = strdup( value_default );
+}
+
+void
+SPIString::cascade( const SPIBase* const parent ) {
+ if( const SPIString* p = dynamic_cast<const SPIString*>(parent) ) {
+ if( inherits && (!set || inherit) ) {
+ g_free(value);
+ value = g_strdup(p->value);
+ }
+ } else {
+ std::cerr << "SPIString::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPIString::merge( const SPIBase* const parent ) {
+ if( const SPIString* p = dynamic_cast<const SPIString*>(parent) ) {
+ if( inherits ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ g_free(value);
+ value = g_strdup(p->value);
+ }
+ }
+ }
+}
+
+bool
+SPIString::operator==(const SPIBase& rhs) {
+ if( const SPIString* r = dynamic_cast<const SPIString*>(&rhs) ) {
+ if( value == NULL && r->value == NULL ) return (SPIBase::operator==(rhs));
+ if( value == NULL || r->value == NULL ) return false;
+
+ return (strcmp(value, r->value) == 0 && SPIBase::operator==(rhs));
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIColor -------------------------------------------------------------
+
+// Used for 'color', 'text-decoration-color', 'flood-color', 'lighting-color', and 'stop-color'.
+// (The last three have yet to be implemented.)
+// CSS3: 'currentcolor' is allowed value and is equal to inherit for the 'color' property.
+// FIXME: We should preserve named colors, hsl colors, etc.
+void SPIColor::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ set = false;
+ inherit = false;
+ currentcolor = false;
+ if ( !strcmp(str, "inherit") ) {
+ set = true;
+ inherit = true;
+ } else if ( !strcmp(str, "currentColor") ) {
+ set = true;
+ currentcolor = true;
+ if( name.compare( "color") == 0 ) {
+ inherit = true; // CSS3
+ } else {
+ setColor( style->color.value.color );
+ }
+ } else {
+ guint32 const rgb0 = sp_svg_read_color(str, 0xff);
+ if (rgb0 != 0xff) {
+ setColor(rgb0);
+ set = true;
+ }
+ }
+}
+
+const Glib::ustring
+SPIColor::write( guint const flags, SPIBase const *const base) const {
+
+ SPIColor const *const my_base = dynamic_cast<const SPIColor*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ CSSOStringStream css;
+
+ if (this->currentcolor) {
+ // currentcolor goes first to handle special case for 'color' property
+ css << "currentColor";
+ } else if (this->inherit) {
+ css << "inherit";
+ } else {
+ char color_buf[8];
+ sp_svg_write_color(color_buf, sizeof(color_buf), this->value.color.toRGBA32( 0 ));
+ css << color_buf;
+
+ if (this->value.color.icc) {
+ if ( !css.str().empty() ) {
+ css << " ";
+ }
+ css << "icc-color(" << this->value.color.icc->colorProfile;
+ for (std::vector<double>::const_iterator i(this->value.color.icc->colors.begin()),
+ iEnd(this->value.color.icc->colors.end());
+ i != iEnd; ++i) {
+ css << ", " << *i;
+ }
+ css << ')';
+ }
+ }
+
+ if ( !css.str().empty() ) {
+ return (name + ":" + css.str() + ";");
+ }
+ }
+
+ return Glib::ustring("");
+}
+
+void
+SPIColor::cascade( const SPIBase* const parent ) {
+ if( const SPIColor* p = dynamic_cast<const SPIColor*>(parent) ) {
+ if( (inherits && !set) || inherit) { // FIXME verify for 'color'
+ if( !(inherit && currentcolor) ) currentcolor = p->currentcolor;
+ setColor( p->value.color );
+ } else {
+ // Add CSS4 Color: Lighter, Darker
+ }
+ } else {
+ std::cerr << "SPIColor::cascade(): Incorrect parent type" << std::endl;
+ }
+
+}
+
+void
+SPIColor::merge( const SPIBase* const parent ) {
+ if( const SPIColor* p = dynamic_cast<const SPIColor*>(parent) ) {
+ if( inherits ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ currentcolor = p->currentcolor;
+ value.color = p->value.color;
+ }
+ }
+ }
+}
+
+bool
+SPIColor::operator==(const SPIBase& rhs) {
+ if( const SPIColor* r = dynamic_cast<const SPIColor*>(&rhs) ) {
+
+ if ( (this->currentcolor != r->currentcolor ) ||
+ (this->value.color != r->value.color ) ||
+ (this->value.color.icc != r->value.color.icc ) ||
+ (this->value.color.icc && r->value.color.icc &&
+ this->value.color.icc->colorProfile != r->value.color.icc->colorProfile &&
+ this->value.color.icc->colors != r->value.color.icc->colors ) ) {
+ return false;
+ }
+
+ return SPIBase::operator==(rhs);
+
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIPaint -------------------------------------------------------------
+
+// Paint is used for 'fill' and 'stroke'. SPIPaint perhaps should be derived from SPIColor.
+// 'style' is set in SPStyle::SPStyle or in the legacy SPIPaint::read( gchar, style, document )
+// It is needed for computed value when value is 'currentColor'. It is also needed to
+// find the object for creating an href (this is done through document but should be done
+// directly so document not needed.. FIXME).
+
+SPIPaint::~SPIPaint() {
+ if( value.href ) {
+ clear();
+ delete value.href;
+ value.href = NULL;
+ }
+}
+
+/**
+ * Set SPIPaint object from string.
+ *
+ * \pre paint == \&style.fill || paint == \&style.stroke.
+ */
+void
+SPIPaint::read( gchar const *str ) {
+
+ // std::cout << "SPIPaint::read: Entrance: " << " |" << (str?str:"null") << "|" << std::endl;
+ // if( style ) {
+ // std::cout << " document: " << (void*)style->document << std::endl;
+ // std::cout << " object: " << (style->object?"present":"null") << std::endl;
+ // if( style->object )
+ // std::cout << " : " << (style->object->getId()?style->object->getId():"no ID")
+ // << " document: " << (style->object->document?"yes":"no") << std::endl;
+ // }
+
+ if(!str ) return;
+
+ reset( false ); // Do not init
+
+ // Is this necessary?
+ while (g_ascii_isspace(*str)) {
+ ++str;
+ }
+
+ if (streq(str, "inherit")) {
+ set = true;
+ inherit = true;
+ } else {
+ // Read any URL first. The other values can be stand-alone or backup to the URL.
+
+ if ( strneq(str, "url", 3) ) {
+
+ // FIXME: THE FOLLOWING CODE SHOULD BE PUT IN A PRIVATE FUNCTION FOR REUSE
+ gchar *uri = extract_uri( str, &str );
+ if(uri == NULL || uri[0] == '\0') {
+ std::cerr << "SPIPaint::read: url is empty or invalid" << std::endl;
+ } else if (!style ) {
+ std::cerr << "SPIPaint::read: url with empty SPStyle pointer" << std::endl;
+ } else {
+ set = true;
+ SPDocument *document = (style->object) ? style->object->document : NULL;
+
+ // Create href if not done already
+ if (!value.href && document) {
+ // std::cout << " Creating value.href" << std::endl;
+ value.href = new SPPaintServerReference(document);
+ if (this == &style->fill) {
+ style->fill_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style));
+ } else {
+ style->stroke_ps_changed_connection = value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style));
+ }
+ }
+
+ // std::cout << "uri: " << (uri?uri:"null") << std::endl;
+ // TODO check what this does in light of move away from union
+ sp_style_set_ipaint_to_uri_string ( style, this, uri);
+ }
+ g_free( uri );
+ }
+
+ while ( g_ascii_isspace(*str) ) {
+ ++str;
+ }
+
+ if (streq(str, "currentColor")) {
+ set = true;
+ currentcolor = true;
+ if (style) {
+ setColor( style->color.value.color );
+ } else {
+ // Normally an SPIPaint is part of an SPStyle and the value of 'color' is
+ // available. SPIPaint can be used 'stand-alone' (e.g. to parse color values) in
+ // which case a value of 'currentColor' is meaningless, thus we shouldn't reach
+ // here.
+ std::cerr << "SPIPaint::read(): value is 'currentColor' but 'color' not available." << std::endl;
+ setColor( 0 );
+ }
+ } else if (streq(str, "none")) {
+ set = true;
+ noneSet = true;
+ } else {
+ guint32 const rgb0 = sp_svg_read_color(str, &str, 0xff);
+ if (rgb0 != 0xff) {
+ setColor( rgb0 );
+ set = true;
+
+ 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;
+ }
+ value.color.icc = tmp;
+ }
+ }
+ }
+ }
+}
+
+// Stand-alone read (Legacy read()), used multiple places, e.g. sp-stop.cpp
+// This function should not be necessary. FIXME
+void
+SPIPaint::read( gchar const *str, SPStyle &style_in, SPDocument *document_in ) {
+ style = &style_in;
+ style->document = document_in;
+ read( str );
+}
+
+const Glib::ustring
+SPIPaint::write( guint const flags, SPIBase const *const base) const {
+
+ SPIPaint const *const my_base = dynamic_cast<const SPIPaint*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ CSSOStringStream css;
+
+ if (this->inherit) {
+ css << "inherit";
+ } else {
+
+ // url must go first as other values can serve as fallbacks
+ if ( this->value.href && this->value.href->getURI() ) {
+ const gchar* uri = this->value.href->getURI()->toString();
+ css << "url(" << uri << ")";
+ g_free((void *)uri);
+ }
+
+ if ( this->noneSet ) {
+ if ( !css.str().empty() ) {
+ css << " ";
+ }
+ css << "none";
+ }
+
+ if ( this->currentcolor ) {
+ if ( !css.str().empty() ) {
+ css << " ";
+ }
+ css << "currentColor";
+ }
+
+ if ( this->colorSet && !this->currentcolor ) {
+ if ( !css.str().empty() ) {
+ css << " ";
+ }
+ char color_buf[8];
+ sp_svg_write_color(color_buf, sizeof(color_buf), this->value.color.toRGBA32( 0 ));
+ css << color_buf;
+ }
+
+ if (this->value.color.icc && !this->currentcolor) {
+ if ( !css.str().empty() ) {
+ css << " ";
+ }
+ css << "icc-color(" << this->value.color.icc->colorProfile;
+ for (std::vector<double>::const_iterator i(this->value.color.icc->colors.begin()),
+ iEnd(this->value.color.icc->colors.end());
+ i != iEnd; ++i) {
+ css << ", " << *i;
+ }
+ css << ')';
+ }
+ }
+
+ if ( !css.str().empty() ) {
+ return (name + ":" + css.str() + ";");
+ }
+ }
+
+ return Glib::ustring("");
+}
+
+void
+SPIPaint::clear() {
+ // std::cout << "SPIPaint::clear(): " << name << std::endl;
+ reset( true ); // Reset and Init
+}
+
+void
+SPIPaint::reset( bool init ) {
+
+ // std::cout << "SPIPaint::reset(): " << name << " " << init << std::endl;
+ SPIBase::clear();
+ currentcolor = false;
+ colorSet = false;
+ noneSet = false;
+ value.color.set( false );
+ if (value.href){
+ if (value.href->getObject()) {
+ value.href->detach();
+ }
+ }
+ if( init ) {
+ if( name.compare( "fill" ) == 0 ) {
+ // 'black' is default for 'fill'
+ setColor(0.0, 0.0, 0.0);
+ }
+ if( name.compare( "text-decoration-color" ) == 0 ) {
+ // currentcolor = true;
+ }
+ }
+}
+
+void
+SPIPaint::cascade( const SPIBase* const parent ) {
+
+ // std::cout << "SPIPaint::cascade" << std::endl;
+ if( const SPIPaint* p = dynamic_cast<const SPIPaint*>(parent) ) {
+ if(!set || inherit) { // Always inherits
+
+ reset( false ); // Do not init
+
+ if( p->isPaintserver() ) {
+ if( p->value.href) {
+ // Why can we use p->document ?
+ sp_style_set_ipaint_to_uri( style, this, p->value.href->getURI(), p->value.href->getOwnerDocument());
+ } else {
+ std::cerr << "SPIPaint::cascade: Expected paint server not found." << std::endl;
+ }
+ } else if( p->isColor() ) {
+ setColor( p->value.color );
+ } else if( p->isNoneSet() ) {
+ noneSet = true;
+ } else if( p->currentcolor ) {
+ currentcolor = true;
+ setColor( style->color.value.color );
+ } else if( isNone() ) {
+ //
+ } else {
+ g_assert_not_reached();
+ }
+ } else {
+ if( currentcolor ) {
+ // Update in case color value changed.
+ setColor( style->color.value.color );
+ }
+ }
+
+ } else {
+ std::cerr << "SPIPaint::cascade(): Incorrect parent type" << std::endl;
+ }
+
+}
+
+void
+SPIPaint::merge( const SPIBase* const parent ) {
+ if( const SPIPaint* p = dynamic_cast<const SPIPaint*>(parent) ) {
+ // if( inherits ) { Paint always inherits
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ this->cascade( parent ); // Must call before setting 'set'
+ set = p->set;
+ inherit = p->inherit;
+ }
+ }
+}
+
+bool
+SPIPaint::operator==(const SPIBase& rhs) {
+
+ if( const SPIPaint* r = dynamic_cast<const SPIPaint*>(&rhs) ) {
+
+ if ( (this->isColor() != r->isColor() ) ||
+ (this->isPaintserver() != r->isPaintserver() ) ||
+ (this->currentcolor != r->currentcolor ) ) {
+ return false;
+ }
+
+ if ( this->isPaintserver() ) {
+ if( this->value.href == NULL || r->value.href == NULL ||
+ this->value.href->getObject() != r->value.href->getObject() ) {
+ return false;
+ }
+ }
+
+ if ( this->isColor() ) {
+ if ( (this->value.color != r->value.color ) ||
+ (this->value.color.icc != r->value.color.icc ) ||
+ (this->value.color.icc && r->value.color.icc &&
+ this->value.color.icc->colorProfile != r->value.color.icc->colorProfile &&
+ this->value.color.icc->colors != r->value.color.icc->colors ) ) {
+ return false;
+ }
+ }
+
+ return SPIBase::operator==(rhs);
+
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIPaintOrder --------------------------------------------------------
+
+void
+SPIPaintOrder::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ g_free(value);
+ set = false;
+ inherit = false;
+
+ if (!strcmp(str, "inherit")) {
+ set = true;
+ inherit = true;
+ } else {
+ set = true;
+ value = g_strdup(str);
+
+ if (!strcmp(value, "normal")) {
+ layer[0] = SP_CSS_PAINT_ORDER_NORMAL;
+ layer_set[0] = true;
+ } else {
+ // This certainly can be done more efficiently
+ gchar** c = g_strsplit(value, " ", PAINT_ORDER_LAYERS + 1);
+ bool used[3] = {false, false, false};
+ unsigned int i = 0;
+ for( ; i < PAINT_ORDER_LAYERS; ++i ) {
+ if( c[i] ) {
+ layer_set[i] = false;
+ if( !strcmp( c[i], "fill")) {
+ layer[i] = SP_CSS_PAINT_ORDER_FILL;
+ layer_set[i] = true;
+ used[0] = true;
+ } else if( !strcmp( c[i], "stroke")) {
+ layer[i] = SP_CSS_PAINT_ORDER_STROKE;
+ layer_set[i] = true;
+ used[1] = true;
+ } else if( !strcmp( c[i], "markers")) {
+ layer[i] = SP_CSS_PAINT_ORDER_MARKER;
+ layer_set[i] = true;
+ used[2] = true;
+ } else {
+ std::cerr << "sp_style_read_ipaintorder: illegal value: " << c[i] << std::endl;
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ g_strfreev(c);
+
+ // Fill out rest of the layers using the default order
+ if( !used[0] && i < PAINT_ORDER_LAYERS ) {
+ layer[i] = SP_CSS_PAINT_ORDER_FILL;
+ layer_set[i] = false;
+ ++i;
+ }
+ if( !used[1] && i < PAINT_ORDER_LAYERS ) {
+ layer[i] = SP_CSS_PAINT_ORDER_STROKE;
+ layer_set[i] = false;
+ ++i;
+ }
+ if( !used[2] && i < PAINT_ORDER_LAYERS ) {
+ layer[i] = SP_CSS_PAINT_ORDER_MARKER;
+ layer_set[i] = false;
+ }
+ }
+ }
+}
+
+const Glib::ustring
+SPIPaintOrder::write( guint const flags, SPIBase const *const base) const {
+
+ SPIPaintOrder const *const my_base = dynamic_cast<const SPIPaintOrder*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ CSSOStringStream css;
+
+ if (this->inherit) {
+ css << "inherit";
+ } else {
+ for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) {
+ if( this->layer_set[i] == true ) {
+ switch (this->layer[i]) {
+ case SP_CSS_PAINT_ORDER_NORMAL:
+ css << "normal";
+ assert( i == 0 );
+ break;
+ case SP_CSS_PAINT_ORDER_FILL:
+ if (i!=0) css << " ";
+ css << "fill";
+ break;
+ case SP_CSS_PAINT_ORDER_STROKE:
+ if (i!=0) css << " ";
+ css << "stroke";
+ break;
+ case SP_CSS_PAINT_ORDER_MARKER:
+ if (i!=0) css << " ";
+ css << "markers";
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return (name + ":" + css.str() + ";");
+ }
+ return Glib::ustring("");
+}
+
+void
+SPIPaintOrder::cascade( const SPIBase* const parent ) {
+ if( const SPIPaintOrder* p = dynamic_cast<const SPIPaintOrder*>(parent) ) {
+ if(!set || inherit) { // Always inherits
+ for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) {
+ layer[i] = p->layer[i];
+ layer_set[i] = p->layer_set[i];
+ }
+ g_free( value );
+ value = g_strdup(p->value);
+ }
+ } else {
+ std::cerr << "SPIPaintOrder::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPIPaintOrder::merge( const SPIBase* const parent ) {
+ if( const SPIPaintOrder* p = dynamic_cast<const SPIPaintOrder*>(parent) ) {
+ // if( inherits ) { PaintOrder always inherits
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ this->cascade( parent ); // Must call be setting 'set'
+ set = p->set;
+ inherit = p->inherit;
+ }
+ }
+}
+
+bool
+SPIPaintOrder::operator==(const SPIBase& rhs) {
+ if( const SPIPaintOrder* r = dynamic_cast<const SPIPaintOrder*>(&rhs) ) {
+ if( layer[0] == SP_CSS_PAINT_ORDER_NORMAL &&
+ r->layer[0] == SP_CSS_PAINT_ORDER_NORMAL ) return SPIBase::operator==(rhs);
+ for (unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) {
+ if( layer[i] != r->layer[i] ) return false;
+ }
+ return SPIBase::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIFilter ------------------------------------------------------------
+
+SPIFilter::~SPIFilter() {
+ if( href ) {
+ clear();
+ delete href;
+ href = NULL;
+ }
+}
+
+void
+SPIFilter::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ clear();
+
+ if ( streq(str, "inherit") ) {
+ set = true;
+ inherit = true;
+ } else if(streq(str, "none")) {
+ set = true;
+ } else if (strneq(str, "url", 3)) {
+ gchar *uri = extract_uri(str);
+ if(uri == NULL || uri[0] == '\0') {
+ std::cerr << "SPIFilter::read: url is empty or invalid" << std::endl;
+ return;
+ } else if (!style) {
+ std::cerr << "SPIFilter::read: url with empty SPStyle pointer" << std::endl;
+ return;
+ }
+ set = true;
+
+ // Create href if not already done.
+ if (!href) {
+ if (style->object) {
+ href = new SPFilterReference(style->object);
+ }
+ // Do we have href now?
+ if ( href ) {
+ href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style));
+ } else {
+ std::cerr << "SPIFilter::read(): Could not allocate 'href'" << std::endl;
+ return;
+ }
+ }
+
+ // We have href
+ try {
+ href->attach(Inkscape::URI(uri));
+ } catch (Inkscape::BadURIException &e) {
+ std::cerr << "SPIFilter::read() " << e.what() << std::endl;
+ href->detach();
+ }
+ g_free (uri);
+
+ } else {
+ std::cerr << "SPIFilter::read(): malformed value: " << str << std::endl;
+ }
+}
+
+const Glib::ustring SPIFilter::write( guint const flags, SPIBase const *const /*base*/) const
+{
+ // TODO: fix base
+ //SPILength const *const my_base = dynamic_cast<const SPILength*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set))
+ {
+ if (this->inherit) {
+ return (name + ":inherit;");
+ } else if(this->href && this->href->getURI()) {
+ gchar *uri = this->href->getURI()->toString();
+ Glib::ustring retval = name + ":url(" + uri + ");";
+ g_free(uri);
+ return retval;
+ }
+ }
+ return Glib::ustring("");
+}
+
+
+void
+SPIFilter::clear() {
+
+ SPIBase::clear();
+ if( href ) {
+ if( href->getObject() ) {
+ href->detach();
+ }
+ }
+}
+
+void
+SPIFilter::cascade( const SPIBase* const parent ) {
+ if( const SPIFilter* p = dynamic_cast<const SPIFilter*>(parent) ) {
+ if( inherit ) { // Only inherits if 'inherit' true/
+ // This is rather unlikely so ignore for now. FIXME
+ (void)p;
+ std::cerr << "SPIFilter::cascade: value 'inherit' not supported." << std::endl;
+ } else {
+ // Do nothing
+ }
+ } else {
+ std::cerr << "SPIFilter::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPIFilter::merge( const SPIBase* const parent ) {
+ if( const SPIFilter* p = dynamic_cast<const SPIFilter*>(parent) ) {
+ // The "correct" thing to do is to combine the filter primitives.
+ // The next best thing is to keep any filter on this object. If there
+ // is no filter on this object, then use any filter on the parent.
+ if( (!set || inherit) && p->href && p->href->getObject() ) { // is the getObject() needed?
+ set = p->set;
+ inherit = p->inherit;
+ if( href ) {
+ // If we alread have an href, use it (unlikely but heck...)
+ if( href->getObject() ) {
+ href->detach();
+ }
+ } else {
+ // If we don't have an href, create it
+ if( style->document ) { // FIXME
+ href = new SPFilterReference(style->document);
+ //href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style));
+ } else if (style->object) {
+ href = new SPFilterReference(style->object);
+ }
+ }
+ if( href ) {
+ // If we now have an href, try to attach parent filter
+ try {
+ href->attach(*p->href->getURI());
+ } catch (Inkscape::BadURIException &e) {
+ std::cerr << "SPIFilter::merge: " << e.what() << std::endl;
+ href->detach();
+ }
+ }
+ }
+ }
+}
+
+// FIXME
+bool
+SPIFilter::operator==(const SPIBase& rhs) {
+ if( const SPIFilter* r = dynamic_cast<const SPIFilter*>(&rhs) ) {
+ (void)r;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIDashArray ---------------------------------------------------------
+
+void
+SPIDashArray::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ set = true;
+
+ if( strcmp( str, "inherit") == 0 ) {
+ inherit = true;
+ return;
+ }
+
+ values.clear();
+
+ if( strcmp(str, "none") == 0) {
+ return;
+ }
+
+ gchar *e = NULL;
+ bool LineSolid = true;
+ while (e != str) {
+ /* TODO: Should allow <length> rather than just a unitless (px) number. */
+ double number = g_ascii_strtod(str, (char **) &e);
+ values.push_back( number );
+ if (number > 0.00000001)
+ LineSolid = false;
+ if (e != str) {
+ str = e;
+ }
+ while (str && *str && !isalnum(*str)) str += 1;
+ }
+
+ if (LineSolid) {
+ values.clear();
+ }
+ return;
+}
+
+const Glib::ustring
+SPIDashArray::write( guint const flags, SPIBase const *const base) const {
+
+ SPIDashArray const *const my_base = dynamic_cast<const SPIDashArray*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ if (this->inherit) {
+ return (name + ":inherit;");
+ } else if (this->values.empty() ) {
+ return (name + ":none;");
+ } else {
+ Inkscape::CSSOStringStream os;
+ os << name << ":";
+ for (unsigned i = 0; i < this->values.size(); ++i) {
+ if (i) {
+ os << ", ";
+ }
+ os << this->values[i];
+ }
+ os << ";";
+ return os.str();
+ }
+ }
+ return Glib::ustring("");
+}
+
+
+void
+SPIDashArray::cascade( const SPIBase* const parent ) {
+ if( const SPIDashArray* p = dynamic_cast<const SPIDashArray*>(parent) ) {
+ if( !set || inherit ) values = p->values; // Always inherits
+ } else {
+ std::cerr << "SPIDashArray::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPIDashArray::merge( const SPIBase* const parent ) {
+ if( const SPIDashArray* p = dynamic_cast<const SPIDashArray*>(parent) ) {
+ if( inherits ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ values = p->values;
+ }
+ }
+ } else {
+ std::cerr << "SPIDashArray::merge(): Incorrect parent type" << std::endl;
+ }
+}
+
+bool
+SPIDashArray::operator==(const SPIBase& rhs) {
+ if( const SPIDashArray* r = dynamic_cast<const SPIDashArray*>(&rhs) ) {
+ return values == r->values && SPIBase::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIFontSize ----------------------------------------------------------
+
+/** Indexed by SP_CSS_FONT_SIZE_blah. These seem a bit small */
+float const SPIFontSize::font_size_table[] = {6.0, 8.0, 10.0, 12.0, 14.0, 18.0, 24.0};
+float const SPIFontSize::font_size_default = 12.0;
+
+void
+SPIFontSize::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if (!strcmp(str, "inherit")) {
+ set = true;
+ inherit = true;
+ } else if ((*str == 'x') || (*str == 's') || (*str == 'm') || (*str == 'l')) {
+ // xx-small, x-small, etc.
+ for (unsigned i = 0; enum_font_size[i].key; i++) {
+ if (!strcmp(str, enum_font_size[i].key)) {
+ set = true;
+ inherit = false;
+ type = SP_FONT_SIZE_LITERAL;
+ literal = enum_font_size[i].value;
+ return;
+ }
+ }
+ /* Invalid */
+ return;
+ } else {
+ SPILength length("temp");
+ length.set = false;
+ length.read( str );
+ if( length.set ) {
+ set = true;
+ inherit = length.inherit;
+ unit = length.unit;
+ value = length.value;
+ computed = length.computed;
+ if( unit == SP_CSS_UNIT_PERCENT ) {
+ type = SP_FONT_SIZE_PERCENTAGE;
+ } else {
+ type = SP_FONT_SIZE_LENGTH;
+ }
+ }
+ return;
+ }
+}
+
+const Glib::ustring
+SPIFontSize::write( guint const flags, SPIBase const *const base) const {
+
+ SPIFontSize const *const my_base = dynamic_cast<const SPIFontSize*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ CSSOStringStream css;
+
+ if (this->inherit) {
+ css << "inherit";
+ } else if (this->type == SP_FONT_SIZE_LITERAL) {
+ for (unsigned i = 0; enum_font_size[i].key; i++) {
+ if (enum_font_size[i].value == static_cast< gint > (this->literal) ) {
+ css << enum_font_size[i].key;
+ }
+ }
+ } else if (this->type == SP_FONT_SIZE_LENGTH) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ int unit = prefs->getInt("/options/font/unitType", SP_CSS_UNIT_PT);
+ if (prefs->getBool("/options/font/textOutputPx", true)) {
+ unit = SP_CSS_UNIT_PX;
+ }
+ css << sp_style_css_size_px_to_units(this->computed, unit) << sp_style_get_css_unit_string(unit);
+ } else if (this->type == SP_FONT_SIZE_PERCENTAGE) {
+ css << (this->value * 100.0) << "%";
+ }
+ return (name + ":" + css.str() + ";");
+ }
+ return Glib::ustring("");
+}
+
+void
+SPIFontSize::cascade( const SPIBase* const parent ) {
+ if( const SPIFontSize* p = dynamic_cast<const SPIFontSize*>(parent) ) {
+ if( !set || inherit ) { // Always inherits
+ computed = p->computed;value = p->value;
+
+
+ // Calculate computed based on parent as needed
+ } else if( type == SP_FONT_SIZE_LITERAL ) {
+ if( literal < SP_CSS_FONT_SIZE_SMALLER ) {
+ computed = font_size_table[ literal ];
+ } else if( literal == SP_CSS_FONT_SIZE_SMALLER ) {
+ computed = p->computed / 1.2;
+ } else if( literal == SP_CSS_FONT_SIZE_LARGER ) {
+ computed = p->computed * 1.2;
+ } else {
+ std::cerr << "SPIFontSize::cascade: Illegal literal value" << std::endl;
+ }
+ } else if( type == SP_FONT_SIZE_PERCENTAGE ) {
+ // Percentage for font size is relative to parent computed (rather than viewport)
+ computed = p->computed * value;
+ } else if( type == SP_FONT_SIZE_LENGTH ) {
+ switch ( unit ) {
+ case SP_CSS_UNIT_EM:
+ /* Relative to parent font size */
+ computed = p->computed * value;
+ break;
+ case SP_CSS_UNIT_EX:
+ /* Relative to parent font size */
+ computed = p->computed * value * 0.5; /* Hack FIXME */
+ break;
+ default:
+ /* No change */
+ break;
+ }
+ }
+ } else {
+ std::cerr << "SPIFontSize::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+double
+SPIFontSize::relative_fraction() const {
+
+ switch (type) {
+ case SP_FONT_SIZE_LITERAL: {
+ switch (literal) {
+ case SP_CSS_FONT_SIZE_SMALLER:
+ return 5.0 / 6.0;
+
+ case SP_CSS_FONT_SIZE_LARGER:
+ return 6.0 / 5.0;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ case SP_FONT_SIZE_PERCENTAGE:
+ return value;
+
+ case SP_FONT_SIZE_LENGTH: {
+ switch (unit ) {
+ case SP_CSS_UNIT_EM:
+ return value;
+
+ case SP_CSS_UNIT_EX:
+ return value * 0.5;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+ }
+ g_assert_not_reached();
+}
+
+void
+SPIFontSize::merge( const SPIBase* const parent ) {
+ if( const SPIFontSize* p = dynamic_cast<const SPIFontSize*>(parent) ) {
+ if( p->set && !(p->inherit) ) {
+ // Parent has definined font-size
+ if( (!set || inherit) ) {
+ // Computed value same as parent
+ set = p->set;
+ inherit = p->inherit;
+ value = p->value;
+ computed = p->computed; // Just to be sure
+ } else if ( type == SP_FONT_SIZE_LENGTH &&
+ unit != SP_CSS_UNIT_EM &&
+ unit != SP_CSS_UNIT_EX ) {
+ // Absolute size, computed value already set
+ } else if ( type == SP_FONT_SIZE_LITERAL &&
+ literal < SP_CSS_FONT_SIZE_SMALLER ) {
+ // Absolute size, computed value already set
+ //g_assert( literal < G_N_ELEMENTS(font_size_table) );
+ g_assert( computed == font_size_table[literal] );
+ } else {
+ // Relative size
+ double const child_frac( relative_fraction() );
+ set = true;
+ inherit = false;
+ computed = p->computed * child_frac;
+
+ if ( ( p->type == SP_FONT_SIZE_LITERAL &&
+ p->literal < SP_CSS_FONT_SIZE_SMALLER ) ||
+ ( p->type == SP_FONT_SIZE_LENGTH &&
+ p->unit != SP_CSS_UNIT_EM &&
+ p->unit != SP_CSS_UNIT_EX ) ) {
+ // Parent absolut size
+ type = SP_FONT_SIZE_LENGTH;
+
+ } else {
+ // Parent relative size
+ double const parent_frac( p->relative_fraction() );
+ if( type == SP_FONT_SIZE_LENGTH ) {
+ // ex/em
+ value *= parent_frac;
+ } else {
+ value = parent_frac * child_frac;
+ type = SP_FONT_SIZE_PERCENTAGE;
+ }
+ }
+ } // Relative size
+ } // Parent set and not inherit
+ } else {
+ std::cerr << "SPIFontSize::merge(): Incorrect parent type" << std::endl;
+ }
+}
+
+// What about different SVG units?
+bool
+SPIFontSize::operator==(const SPIBase& rhs) {
+ if( const SPIFontSize* r = dynamic_cast<const SPIFontSize*>(&rhs) ) {
+ if( type != r->type ) { return false;}
+ if( type == SP_FONT_SIZE_LENGTH ) {
+ if( computed != r->computed ) { return false;}
+ } else if (type == SP_FONT_SIZE_LITERAL ) {
+ if( literal != r->literal ) { return false;}
+ } else {
+ if( value != r->value ) { return false;}
+ }
+ return SPIBase::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIFont ----------------------------------------------------------
+
+void
+SPIFont::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if( !style ) {
+ std::cerr << "SPIFont::read(): style is void" << std::endl;
+ return;
+ }
+
+ if ( !strcmp(str, "inherit") ) {
+ set = true;
+ inherit = true;
+ } else {
+
+ // Break string into white space separated tokens
+ std::stringstream os( str );
+ Glib::ustring param;
+
+ while (os >> param) {
+
+ // CSS is case insensitive but we're comparing against lowercase strings
+ Glib::ustring lparam = param.lowercase();
+
+ if (lparam == "/" ) {
+ // line_height follows... note: font-size already read
+
+ os >> param;
+ lparam = param.lowercase();
+ style->line_height.readIfUnset( lparam.c_str() );
+
+ } else {
+ // Try to parse each property in turn
+
+ SPIEnum test_style("font-style", enum_font_style);
+ test_style.read( lparam.c_str() );
+ if( test_style.set ) {
+ style->font_style = test_style;
+ continue;
+ }
+
+ // font-variant (Note: only CSS2.1 value small-caps is valid in shortcut.)
+ SPIEnum test_variant("font-variant", enum_font_variant);
+ test_variant.read( lparam.c_str() );
+ if( test_variant.set ) {
+ style->font_variant = test_variant;
+ continue;
+ }
+
+ // font-weight
+ SPIEnum test_weight("font-weight", enum_font_weight);
+ test_weight.read( lparam.c_str() );
+ if( test_weight.set ) {
+ style->font_weight = test_weight;
+ continue;
+ }
+
+ // font-stretch (added in CSS 3 Fonts)
+ SPIEnum test_stretch("font-stretch", enum_font_stretch);
+ test_stretch.read( lparam.c_str() );
+ if( test_stretch.set ) {
+ style->font_stretch = test_stretch;
+ continue;
+ }
+
+ // font-size
+ SPIFontSize test_size;
+ test_size.read( lparam.c_str() );
+ if( test_size.set ) {
+ style->font_size = test_size;
+ continue;
+ }
+
+ // No valid property value found.
+ break;
+ }
+ } // params
+
+ // The rest must be font-family...
+ std::string str_s = str; // Why this extra step?
+ std::string family = str_s.substr( str_s.find( param ) );
+
+ style->font_family.readIfUnset( family.c_str() );
+
+ // Everything in shorthand is set per CSS rules, this works since
+ // properties are read backwards from end to start.
+ style->font_style.set = true;
+ style->font_variant.set = true;
+ style->font_weight.set = true;
+ style->font_stretch.set = true;
+ style->font_size.set = true;
+ style->line_height.set = true;
+ style->font_family.set = true;
+ // style->font_size_adjust.set = true;
+ // style->font_kerning.set = true;
+ // style->font_language_override.set = true;;
+ }
+}
+
+const Glib::ustring SPIFont::write( guint const /*flags*/, SPIBase const *const /*base*/) const
+{
+ // At the moment, do nothing. We could add a preference to write out
+ // 'font' shorthand rather than longhand properties.
+
+ // SPIFontSize const *const my_base = dynamic_cast<const SPIFontSize*>(base);
+ // if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ // ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ // ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ // && (!my_base->set || this != my_base )))
+ // {
+ // CSSOStringStream css;
+ // }
+ return Glib::ustring("");
+}
+
+// void
+// SPIFont::cascade( const SPIBase* const parent ) {
+// }
+
+// void
+// SPIFont::merge( const SPIBase* const parent ) {
+// }
+
+// Does nothing...
+bool
+SPIFont::operator==(const SPIBase& rhs) {
+ if( /* const SPIFont* r = */ dynamic_cast<const SPIFont*>(&rhs) ) {
+ return SPIBase::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPIBaselineShift -----------------------------------------------------
+
+void
+SPIBaselineShift::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if (!strcmp(str, "inherit")) {
+ set = true;
+ inherit = true;
+ } else if ((*str == 'b') || (*str == 's')) {
+ // baseline or sub or super
+ for (unsigned i = 0; enum_baseline_shift[i].key; i++) {
+ if (!strcmp(str, enum_baseline_shift[i].key)) {
+ set = true;
+ inherit = false;
+ type = SP_BASELINE_SHIFT_LITERAL;
+ literal = enum_baseline_shift[i].value;
+ return;
+ }
+ }
+ /* Invalid */
+ return;
+ } else {
+ SPILength length( "temp" );
+ length.read( str );
+ set = length.set;
+ inherit = length.inherit;
+ unit = length.unit;
+ value = length.value;
+ computed = length.computed;
+ if( unit == SP_CSS_UNIT_PERCENT ) {
+ type = SP_BASELINE_SHIFT_PERCENTAGE;
+ } else {
+ type = SP_BASELINE_SHIFT_LENGTH;
+ }
+ return;
+ }
+}
+
+const Glib::ustring
+SPIBaselineShift::write( guint const flags, SPIBase const *const base) const {
+
+ SPIBaselineShift const *const my_base = dynamic_cast<const SPIBaselineShift*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || !this->isZero() )))
+ {
+ CSSOStringStream css;
+
+ if (this->inherit) {
+ css << "inherit";
+ } else if (this->type == SP_BASELINE_SHIFT_LITERAL) {
+ for (unsigned i = 0; enum_baseline_shift[i].key; i++) {
+ if (enum_baseline_shift[i].value == static_cast< gint > (this->literal) ) {
+ css << enum_baseline_shift[i].key;
+ }
+ }
+ } else if (this->type == SP_BASELINE_SHIFT_LENGTH) {
+ if( this->unit == SP_CSS_UNIT_EM || this->unit == SP_CSS_UNIT_EX ) {
+ css << this->value << (this->unit == SP_CSS_UNIT_EM ? "em" : "ex");
+ } else {
+ css << this->computed << "px"; // must specify px, see inkscape bug 1221626, mozilla bug 234789
+ }
+ } else if (this->type == SP_BASELINE_SHIFT_PERCENTAGE) {
+ css << (this->value * 100.0) << "%";
+ }
+ return (name + ":" + css.str() + ";");
+ }
+ return Glib::ustring("");
+}
+
+void
+SPIBaselineShift::cascade( const SPIBase* const parent ) {
+ if( const SPIBaselineShift* p = dynamic_cast<const SPIBaselineShift*>(parent) ) {
+ SPIFontSize *pfont_size = &(p->style->font_size);
+ g_assert( pfont_size != NULL );
+
+ if( !set || inherit ) {
+ computed = p->computed; // Shift relative to parent shift, corrected below
+ } else if (type == SP_BASELINE_SHIFT_LITERAL) {
+ if( literal == SP_CSS_BASELINE_SHIFT_BASELINE ) {
+ computed = 0; // No change
+ } else if (literal == SP_CSS_BASELINE_SHIFT_SUB ) {
+ // Should use subscript position from font relative to alphabetic baseline
+ // OpenOffice, Adobe: -0.33, Word -0.14, LaTex about -0.2.
+ computed = -0.2 * pfont_size->computed;
+ } else if (literal == SP_CSS_BASELINE_SHIFT_SUPER ) {
+ // Should use superscript position from font relative to alphabetic baseline
+ // OpenOffice, Adobe: 0.33, Word 0.35, LaTex about 0.45.
+ computed = 0.4 * pfont_size->computed;
+ } else {
+ /* Illegal value */
+ }
+ } else if (type == SP_BASELINE_SHIFT_PERCENTAGE) {
+ // Percentage for baseline shift is relative to computed "line-height"
+ // which is just font-size (see SVG1.1 'font').
+ computed = pfont_size->computed * value;
+ } else if (type == SP_BASELINE_SHIFT_LENGTH) {
+ switch (unit) {
+ case SP_CSS_UNIT_EM:
+ computed = value * pfont_size->computed;
+ break;
+ case SP_CSS_UNIT_EX:
+ computed = value * 0.5 * pfont_size->computed;
+ break;
+ default:
+ /* No change */
+ break;
+ }
+ }
+ // baseline-shifts are relative to parent baseline
+ computed += p->computed;
+
+ } else {
+ std::cerr << "SPIBaselineShift::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+// This was not defined in the legacy C code, it needs some serious thinking (but is low priority).
+// FIX ME
+void
+SPIBaselineShift::merge( const SPIBase* const parent ) {
+ if( const SPIBaselineShift* p = dynamic_cast<const SPIBaselineShift*>(parent) ) {
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ value = p->value;
+ }
+ } else {
+ std::cerr << "SPIBaselineShift::merge(): Incorrect parent type" << std::endl;
+ }
+}
+
+// This is not used but we have it for completeness, it has not been tested.
+bool
+SPIBaselineShift::operator==(const SPIBase& rhs) {
+ if( const SPIBaselineShift* r = dynamic_cast<const SPIBaselineShift*>(&rhs) ) {
+ if( type != r->type ) return false;
+ if( type == SP_BASELINE_SHIFT_LENGTH ) {
+ if( computed != r->computed ) return false;
+ } else if ( type == SP_BASELINE_SHIFT_LITERAL ) {
+ if( literal != r->literal ) return false;
+ } else {
+ if( value != r->value ) return false;
+ }
+ return SPIBase::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+bool
+SPIBaselineShift::isZero() const {
+ if( type == SP_BASELINE_SHIFT_LITERAL ) {
+ if( literal == SP_CSS_BASELINE_SHIFT_BASELINE ) return true;
+ } else {
+ if( value == 0.0 ) return true;
+ }
+ return false;
+}
+
+
+
+// SPITextDecorationLine ------------------------------------------------
+
+void
+SPITextDecorationLine::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ if (!strcmp(str, "inherit")) {
+ set = true;
+ inherit = true;
+ } else if (!strcmp(str, "none")) {
+ set = true;
+ inherit = false;
+ underline = false;
+ overline = false;
+ line_through = false;
+ blink = false;
+ } else {
+ bool found_one = false;
+ bool hit_one = false;
+
+ // CSS 2 keywords
+ bool found_underline = false;
+ bool found_overline = false;
+ bool found_line_through = false;
+ bool found_blink = false;
+
+ // This method ignores inlineid keys and extra delimiters, so " ,,, blink hello" will set
+ // blink and ignore hello
+ const gchar *hstr = str;
+ while (1) {
+ if (*str == ' ' || *str == ',' || *str == '\0'){
+ int slen = str - hstr;
+ // CSS 2 keywords
+ while(1){ // not really a loop, used to avoid a goto
+ hit_one = true; // most likely we will
+ if ((slen == 9) && strneq(hstr, "underline", slen)){ found_underline = true; break; }
+ if ((slen == 8) && strneq(hstr, "overline", slen)){ found_overline = true; break; }
+ if ((slen == 12) && strneq(hstr, "line-through", slen)){ found_line_through = true; break; }
+ if ((slen == 5) && strneq(hstr, "blink", slen)){ found_blink = true; break; }
+ if ((slen == 4) && strneq(hstr, "none", slen)){ break; }
+
+ hit_one = false; // whatever this thing is, we do not recognize it
+ break;
+ }
+ found_one |= hit_one;
+ if(*str == '\0')break;
+ hstr = str + 1;
+ }
+ str++;
+ }
+ if (found_one) {
+ set = true;
+ inherit = false;
+ underline = found_underline;
+ overline = found_overline;
+ line_through = found_line_through;
+ blink = found_blink;
+ }
+ else {
+ set = false;
+ inherit = false;
+ }
+ }
+}
+
+const Glib::ustring
+SPITextDecorationLine::write( guint const flags, SPIBase const *const base) const {
+ SPITextDecorationLine const *const my_base = dynamic_cast<const SPITextDecorationLine*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ Inkscape::CSSOStringStream os;
+ os << name << ":";
+ if( inherit ) {
+ os << "inherit";
+ } else if (this->underline || this->overline || this->line_through || this->blink) {
+ if (this->underline) os << " underline";
+ if (this->overline) os << " overline";
+ if (this->line_through) os << " line-through";
+ if (this->blink) os << " blink"; // Deprecated
+ } else {
+ os << "none";
+ }
+ os << ";";
+ return ( os.str() );
+ }
+ return Glib::ustring("");
+}
+
+void
+SPITextDecorationLine::cascade( const SPIBase* const parent ) {
+ if( const SPITextDecorationLine* p = dynamic_cast<const SPITextDecorationLine*>(parent) ) {
+ if( inherits && (!set || inherit) ) {
+ underline = p->underline;
+ overline = p->overline;
+ line_through = p->line_through;
+ blink = p->blink;
+ }
+ } else {
+ std::cerr << "SPITextDecorationLine::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPITextDecorationLine::merge( const SPIBase* const parent ) {
+ if( const SPITextDecorationLine* p = dynamic_cast<const SPITextDecorationLine*>(parent) ) {
+ if( inherits ) { // Always inherits... but special rules?
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ underline = p->underline;
+ overline = p->overline;
+ line_through = p->line_through;
+ blink = p->blink;
+ }
+ }
+ }
+}
+
+bool
+SPITextDecorationLine::operator==(const SPIBase& rhs) {
+ if( const SPITextDecorationLine* r = dynamic_cast<const SPITextDecorationLine*>(&rhs) ) {
+ return
+ (underline == r->underline ) &&
+ (overline == r->overline ) &&
+ (line_through == r->line_through ) &&
+ (blink == r->blink ) &&
+ SPIBase::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+
+
+// SPITextDecorationStyle -----------------------------------------------
+
+void
+SPITextDecorationStyle::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ set = false;
+ inherit = false;
+
+ solid = true; // Default
+ isdouble = false;
+ dotted = false;
+ dashed = false;
+ wavy = false;
+
+ if (!strcmp(str, "inherit")) {
+ set = true;
+ inherit = true;
+ solid = false;
+ } else {
+ // note, these are CSS 3 keywords
+ bool found_solid = false;
+ bool found_double = false;
+ bool found_dotted = false;
+ bool found_dashed = false;
+ bool found_wavy = false;
+ bool found_one = false;
+
+ // this method ignores inlineid keys and extra delimiters, so " ,,, style hello" will set style and ignore hello
+ // if more than one style is present, the first is used
+ const gchar *hstr = str;
+ while (1) {
+ if (*str == ' ' || *str == ',' || *str == '\0'){
+ int slen = str - hstr;
+ if ( (slen == 5) && strneq(hstr, "solid", slen)){ found_solid = true; found_one = true; break; }
+ else if ((slen == 6) && strneq(hstr, "double", slen)){ found_double = true; found_one = true; break; }
+ else if ((slen == 6) && strneq(hstr, "dotted", slen)){ found_dotted = true; found_one = true; break; }
+ else if ((slen == 6) && strneq(hstr, "dashed", slen)){ found_dashed = true; found_one = true; break; }
+ else if ((slen == 4) && strneq(hstr, "wavy", slen)){ found_wavy = true; found_one = true; break; }
+ if(*str == '\0')break; // nothing more to test
+ hstr = str + 1;
+ }
+ str++;
+ }
+ if(found_one){
+ set = true;
+ solid = found_solid;
+ isdouble = found_double;
+ dotted = found_dotted;
+ dashed = found_dashed;
+ wavy = found_wavy;
+ }
+ else {
+ set = false;
+ inherit = false;
+ }
+ }
+}
+
+const Glib::ustring
+SPITextDecorationStyle::write( guint const flags, SPIBase const *const base) const {
+ SPITextDecorationStyle const *const my_base = dynamic_cast<const SPITextDecorationStyle*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && this->set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && this->set
+ && (!my_base->set || this != my_base )))
+ {
+ Inkscape::CSSOStringStream os;
+ os << name << ":";
+ if( inherit ) {
+ os << "inherit";
+ } else if (this->solid ) {
+ os << "solid";
+ } else if (this->isdouble ) {
+ os << "double";
+ } else if (this->dotted ) {
+ os << "dotted";
+ } else if (this->dashed ) {
+ os << "dashed";
+ } else if (this->wavy ) {
+ os << "wavy";
+ } else {
+ std::cerr << "SPITextDecorationStyle::write(): No valid value for property" << std::endl;
+ return Glib::ustring("");
+ }
+ os << ";";
+ return ( os.str() );
+ }
+ return Glib::ustring("");
+}
+
+void
+SPITextDecorationStyle::cascade( const SPIBase* const parent ) {
+ if( const SPITextDecorationStyle* p = dynamic_cast<const SPITextDecorationStyle*>(parent) ) {
+ if( inherits && (!set || inherit) ) {
+ solid = p->solid;
+ isdouble = p->isdouble;
+ dotted = p->dotted;
+ dashed = p->dashed;
+ wavy = p->wavy;
+ }
+ } else {
+ std::cerr << "SPITextDecorationStyle::cascade(): Incorrect parent type" << std::endl;
+ }
+}
+
+void
+SPITextDecorationStyle::merge( const SPIBase* const parent ) {
+ if( const SPITextDecorationStyle* p = dynamic_cast<const SPITextDecorationStyle*>(parent) ) {
+ if( inherits ) { // Always inherits... but special rules?
+ if( (!set || inherit) && p->set && !(p->inherit) ) {
+ set = p->set;
+ inherit = p->inherit;
+ solid = p->solid;
+ isdouble = p->isdouble;
+ dotted = p->dotted;
+ dashed = p->dashed;
+ wavy = p->wavy;
+ }
+ }
+ }
+}
+
+bool
+SPITextDecorationStyle::operator==(const SPIBase& rhs) {
+ if( const SPITextDecorationStyle* r = dynamic_cast<const SPITextDecorationStyle*>(&rhs) ) {
+ return
+ (solid == r->solid ) &&
+ (isdouble == r->isdouble ) &&
+ (dotted == r->dotted ) &&
+ (dashed == r->dashed ) &&
+ (wavy == r->wavy ) &&
+ SPIBase::operator==(rhs);
+ } else {
+ return false;
+ }
+}
+
+
+
+// TextDecorationColor is handled by SPIPaint (should be SPIColor), default value is "currentColor"
+// FIXME
+
+
+
+// SPITextDecoration ----------------------------------------------------
+
+void
+SPITextDecoration::read( gchar const *str ) {
+
+ if( !str ) return;
+
+ bool is_css3 = false;
+
+ SPITextDecorationLine test_line;
+ test_line.read( str );
+ if( test_line.set ) {
+ style->text_decoration_line = test_line;
+ }
+
+ SPITextDecorationStyle test_style;
+ test_style.read( str );
+ if( test_style.set ) {
+ style->text_decoration_style = test_style;
+ is_css3 = true;
+ }
+
+ // the color routine must be fed one token at a time - if multiple colors are found the LAST
+ // one is used ???? then why break on set?
+
+ // This could certainly be designed better
+ SPIColor test_color("text-decoration-color");
+ test_color.setStylePointer( style );
+ test_color.read( "currentColor" ); // Default value
+ test_color.set = false;
+ const gchar *hstr = str;
+ while (1) {
+ if (*str == ' ' || *str == ',' || *str == '\0'){
+ int slen = str - hstr;
+ gchar *frag = g_strndup(hstr,slen+1); // only send one piece at a time, since keywords may be intermixed
+
+ if( strcmp( frag, "none" ) != 0 ) { // 'none' not allowed
+ test_color.read( frag );
+ }
+
+ free(frag);
+ if( test_color.set ) {
+ style->text_decoration_color = test_color;
+ is_css3 = true;
+ break;
+ }
+ test_color.read( "currentColor" ); // Default value
+ test_color.set = false;
+ if( *str == '\0' )break;
+ hstr = str + 1;
+ }
+ str++;
+ }
+
+ // If we read a style or color then we have CSS3 which require any non-set values to be
+ // set to their default values.
+ if( is_css3 ) {
+ style->text_decoration_line.set = true;
+ style->text_decoration_style.set = true;
+ style->text_decoration_color.set = true;
+ }
+
+ // If we set text_decoration_line, then update style_td (for CSS2 text-decoration)
+ if( style->text_decoration_line.set == true ) {
+ style_td = style;
+ }
+}
+
+// Returns CSS2 'text-decoration' (using settings in SPTextDecorationLine)
+// This is required until all SVG renderers support CSS3 'text-decoration'
+const Glib::ustring
+SPITextDecoration::write( guint const flags, SPIBase const *const base) const {
+ SPITextDecoration const *const my_base = dynamic_cast<const SPITextDecoration*>(base);
+ if ( (flags & SP_STYLE_FLAG_ALWAYS) ||
+ ((flags & SP_STYLE_FLAG_IFSET) && style->text_decoration_line.set) ||
+ ((flags & SP_STYLE_FLAG_IFDIFF) && style->text_decoration_line.set
+ && (!my_base->style->text_decoration_line.set ||
+ style->text_decoration_line != my_base->style->text_decoration_line )))
+ {
+ Inkscape::CSSOStringStream os;
+ os << name << ":";
+ if( inherit ) {
+ os << "inherit";
+ } else if (style->text_decoration_line.underline ||
+ style->text_decoration_line.overline ||
+ style->text_decoration_line.line_through ||
+ style->text_decoration_line.blink) {
+ if (style->text_decoration_line.underline) os << " underline";
+ if (style->text_decoration_line.overline) os << " overline";
+ if (style->text_decoration_line.line_through) os << " line-through";
+ if (style->text_decoration_line.blink) os << " blink"; // Deprecated
+ } else {
+ os << "none";
+ }
+ os << ";";
+ return ( os.str() );
+ }
+ return Glib::ustring("");
+}
+
+void
+SPITextDecoration::cascade( const SPIBase* const parent ) {
+ if( const SPITextDecoration* p = dynamic_cast<const SPITextDecoration*>(parent) ) {
+ if( style_td == NULL ) {
+ style_td = p->style_td;
+ }
+ } else {
+ std::cerr << "SPITextDecoration::cascade(): Incorrect parent type" << std::endl;
+ }
+
+}
+
+void
+SPITextDecoration::merge( const SPIBase* const parent ) {
+ if( const SPITextDecoration* p = dynamic_cast<const SPITextDecoration*>(parent) ) {
+ if( style_td == NULL ) {
+ style_td = p->style_td;
+ }
+ } else {
+ std::cerr << "SPITextDecoration::merge(): Incorrect parent type" << std::endl;
+ }
+
+}
+
+// Use CSS2 value
+bool
+SPITextDecoration::operator==(const SPIBase& rhs) {
+ if( const SPITextDecoration* r = dynamic_cast<const SPITextDecoration*>(&rhs) ) {
+ return (style->text_decoration_line == r->style->text_decoration_line &&
+ SPIBase::operator==(rhs));
+ } else {
+ return false;
+ }
+}
+
+
+
+/* ---------------------------- NOTES ----------------------------- */
+
+/*
+ * 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.
+ */
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :