diff options
| author | Jabier Arraiza Cenoz <jabier.arraiza@marker.es> | 2014-04-26 23:27:03 +0000 |
|---|---|---|
| committer | Jabiertxof <jtx@jtx.marker.es> | 2014-04-26 23:27:03 +0000 |
| commit | cf159c3d0415a1a761eb7ca760256cd224927d3c (patch) | |
| tree | 03cfd9a7f543ec07377c96ed23b9f5b0626a6e2a /src | |
| parent | Redesign of the BSpline LPE widgets (diff) | |
| parent | Clean up of style code: Patch from suv: SPStyle: struct -> class (diff) | |
| download | inkscape-cf159c3d0415a1a761eb7ca760256cd224927d3c.tar.gz inkscape-cf159c3d0415a1a761eb7ca760256cd224927d3c.zip | |
update to trunk
(bzr r11950.1.336)
Diffstat (limited to 'src')
48 files changed, 4994 insertions, 4585 deletions
diff --git a/src/2geom/CMakeLists.txt b/src/2geom/CMakeLists.txt index 3d516dc18..a2c02e682 100644 --- a/src/2geom/CMakeLists.txt +++ b/src/2geom/CMakeLists.txt @@ -115,7 +115,6 @@ set(2geom_SRC solver.h svg-elliptical-arc.h svg-path-parser.h - svg-path.h sweep.h toposweep.h transforms.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8408d6270..fae4c484c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -259,6 +259,7 @@ set(inkscape_SRC snapped-point.cpp snapper.cpp style.cpp + style-internal.cpp svg-view-widget.cpp svg-view.cpp text-chemistry.cpp diff --git a/src/Makefile_insert b/src/Makefile_insert index 8872b045d..6d0d6b08c 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -210,7 +210,8 @@ ink_common_sources += \ streq.h \ strneq.h \ style.cpp style.h \ - style-enums.h style-internal.h \ + style-enums.h \ + style-internal.cpp style-internal.h \ svg-profile.h \ svg-view.cpp svg-view.h \ svg-view-widget.cpp svg-view-widget.h \ diff --git a/src/color.h b/src/color.h index 746ecebbf..604dff0e3 100644 --- a/src/color.h +++ b/src/color.h @@ -46,6 +46,7 @@ struct SPColor { SPColor& operator= (SPColor const& other); bool operator == ( SPColor const& other ) const; + bool operator != ( SPColor const& other ) const { return !(*this == other); }; bool isClose( SPColor const& other, float epsilon ) const; void set( float r, float g, float b ); diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index bab9635a9..37f537cc5 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -722,10 +722,10 @@ objects_query_strokewidth (GSList *objects, SPStyle *style_res) } if ( style->stroke.isNone() && !( - style->marker[SP_MARKER_LOC].set || // stroke width affects markers, so if there's no stroke but only markers then we should - style->marker[SP_MARKER_LOC_START].set || // still calculate the stroke width - style->marker[SP_MARKER_LOC_MID].set || - style->marker[SP_MARKER_LOC_END].set)) + style->marker.set || // stroke width affects markers, so if there's no + style->marker_start.set || // stroke but only markers then we should + style->marker_mid.set || // still calculate the stroke width + style->marker_end.set)) { continue; } @@ -1227,11 +1227,11 @@ objects_query_fontfamily (GSList *objects, SPStyle *style_res) bool different = false; int texts = 0; - if (style_res->text->font_family.value) { - g_free(style_res->text->font_family.value); - style_res->text->font_family.value = NULL; + if (style_res->font_family.value) { + g_free(style_res->font_family.value); + style_res->font_family.value = NULL; } - style_res->text->font_family.set = FALSE; + style_res->font_family.set = FALSE; for (GSList const *i = objects; i != NULL; i = i->next) { SPObject *obj = SP_OBJECT (i->data); @@ -1250,21 +1250,21 @@ objects_query_fontfamily (GSList *objects, SPStyle *style_res) texts ++; - if (style_res->text->font_family.value && style->text->font_family.value && - strcmp (style_res->text->font_family.value, style->text->font_family.value)) { + if (style_res->font_family.value && style->font_family.value && + strcmp (style_res->font_family.value, style->font_family.value)) { different = true; // different fonts } - if (style_res->text->font_family.value) { - g_free(style_res->text->font_family.value); - style_res->text->font_family.value = NULL; + if (style_res->font_family.value) { + g_free(style_res->font_family.value); + style_res->font_family.value = NULL; } - style_res->text->font_family.set = TRUE; - style_res->text->font_family.value = g_strdup(style->text->font_family.value); + style_res->font_family.set = TRUE; + style_res->font_family.value = g_strdup(style->font_family.value); } - if (texts == 0 || !style_res->text->font_family.set) { + if (texts == 0 || !style_res->font_family.set) { return QUERY_STYLE_NOTHING; } @@ -1285,11 +1285,11 @@ objects_query_fontspecification (GSList *objects, SPStyle *style_res) bool different = false; int texts = 0; - if (style_res->text->font_specification.value) { - g_free(style_res->text->font_specification.value); - style_res->text->font_specification.value = NULL; + if (style_res->font_specification.value) { + g_free(style_res->font_specification.value); + style_res->font_specification.value = NULL; } - style_res->text->font_specification.set = FALSE; + style_res->font_specification.set = FALSE; for (GSList const *i = objects; i != NULL; i = i->next) { SPObject *obj = SP_OBJECT (i->data); @@ -1308,21 +1308,21 @@ objects_query_fontspecification (GSList *objects, SPStyle *style_res) texts ++; - if (style_res->text->font_specification.value && style_res->text->font_specification.set && - style->text->font_specification.value && style->text->font_specification.set && - strcmp (style_res->text->font_specification.value, style->text->font_specification.value)) { + if (style_res->font_specification.value && style_res->font_specification.set && + style->font_specification.value && style->font_specification.set && + strcmp (style_res->font_specification.value, style->font_specification.value)) { different = true; // different fonts } - if (style->text->font_specification.set) { + if (style->font_specification.set) { - if (style_res->text->font_specification.value) { - g_free(style_res->text->font_specification.value); - style_res->text->font_specification.value = NULL; + if (style_res->font_specification.value) { + g_free(style_res->font_specification.value); + style_res->font_specification.value = NULL; } - style_res->text->font_specification.set = TRUE; - style_res->text->font_specification.value = g_strdup(style->text->font_specification.value); + style_res->font_specification.set = TRUE; + style_res->font_specification.value = g_strdup(style->font_specification.value); } } diff --git a/src/desktop-style.h b/src/desktop-style.h index 47575de75..fc20e97b9 100644 --- a/src/desktop-style.h +++ b/src/desktop-style.h @@ -16,10 +16,10 @@ #include <glib.h> class ColorRGBA; -class SPCSSAttr; -class SPDesktop; -class SPObject; -struct SPStyle; +class SPCSSAttr; +class SPDesktop; +class SPObject; +class SPStyle; namespace Inkscape { namespace XML { class Node; diff --git a/src/desktop.h b/src/desktop.h index fec6249e9..be2bf891f 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -54,10 +54,10 @@ class ToolBase; } } -class SPItem; +class SPItem; class SPNamedView; -class SPObject; -struct SPStyle; +class SPObject; +class SPStyle; typedef struct _DocumentInterface DocumentInterface;//struct DocumentInterface; namespace Gtk diff --git a/src/display/drawing-group.h b/src/display/drawing-group.h index 651e9d8af..ab1f9895d 100644 --- a/src/display/drawing-group.h +++ b/src/display/drawing-group.h @@ -14,7 +14,7 @@ #include "display/drawing-item.h" -struct SPStyle; +class SPStyle; namespace Inkscape { diff --git a/src/display/drawing-item.h b/src/display/drawing-item.h index db803cf60..d89299eeb 100644 --- a/src/display/drawing-item.h +++ b/src/display/drawing-item.h @@ -20,7 +20,7 @@ #include <2geom/rect.h> #include <2geom/affine.h> -struct SPStyle; +class SPStyle; namespace Inkscape { diff --git a/src/display/drawing-shape.h b/src/display/drawing-shape.h index 405c789e0..f37de9ce7 100644 --- a/src/display/drawing-shape.h +++ b/src/display/drawing-shape.h @@ -15,7 +15,7 @@ #include "display/drawing-item.h" #include "display/nr-style.h" -struct SPStyle; +class SPStyle; class SPCurve; namespace Inkscape { diff --git a/src/display/drawing-text.h b/src/display/drawing-text.h index b863ca2a4..41039d85d 100644 --- a/src/display/drawing-text.h +++ b/src/display/drawing-text.h @@ -15,7 +15,7 @@ #include "display/drawing-group.h" #include "display/nr-style.h" -struct SPStyle; +class SPStyle; class font_instance; namespace Inkscape { diff --git a/src/display/nr-filter-primitive.h b/src/display/nr-filter-primitive.h index 94bd6abb0..214b2cfc5 100644 --- a/src/display/nr-filter-primitive.h +++ b/src/display/nr-filter-primitive.h @@ -16,7 +16,7 @@ #include "display/nr-filter-types.h" #include "svg/svg-length.h" -struct SPStyle; +class SPStyle; namespace Inkscape { namespace Filters { diff --git a/src/display/nr-style.cpp b/src/display/nr-style.cpp index 125d0c6d6..3d2d36483 100644 --- a/src/display/nr-style.cpp +++ b/src/display/nr-style.cpp @@ -201,8 +201,7 @@ void NRStyle::set(SPStyle *style) if( style->text_decoration_color.set || style->text_decoration_color.inherit || - style->text_decoration_color.currentcolor || - style->text_decoration_color.colorSet){ + style->text_decoration_color.currentcolor ){ text_decoration_color.set(style->text_decoration_color.value.color); text_decoration_useColor = true; } diff --git a/src/display/nr-style.h b/src/display/nr-style.h index 717cda899..8b5a0ee3d 100644 --- a/src/display/nr-style.h +++ b/src/display/nr-style.h @@ -17,7 +17,7 @@ #include "color.h" class SPPaintServer; -struct SPStyle; +class SPStyle; namespace Inkscape { class DrawingContext; diff --git a/src/extension/implementation/implementation.h b/src/extension/implementation/implementation.h index 9d679982a..fb323cd78 100644 --- a/src/extension/implementation/implementation.h +++ b/src/extension/implementation/implementation.h @@ -29,7 +29,7 @@ namespace Gtk { } class SPDocument; -struct SPStyle; +class SPStyle; namespace Inkscape { diff --git a/src/extension/internal/emf-inout.h b/src/extension/internal/emf-inout.h index a97cb0a54..fc98958bb 100644 --- a/src/extension/internal/emf-inout.h +++ b/src/extension/internal/emf-inout.h @@ -40,7 +40,7 @@ typedef struct { } EMF_STRINGS, *PEMF_STRINGS; typedef struct emf_device_context { - struct SPStyle style; + class SPStyle style; char *font_name; bool stroke_set; int stroke_mode; // enumeration from drawmode, not used if fill_set is not True diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp index f4f7f08cb..8b80fec1c 100644 --- a/src/extension/internal/emf-print.cpp +++ b/src/extension/internal/emf-print.cpp @@ -1869,7 +1869,7 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te _lookup_ppt_fontfix("Convert To Wingdings", params); break; default: //also CVTNON - _lookup_ppt_fontfix(style->text->font_family.value, params); + _lookup_ppt_fontfix(style->font_family.value, params); break; } if (params.f2 != 0 || params.f3 != 0) { @@ -1897,7 +1897,7 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te // of the special fonts. uint16_t *wfacename; if (!newfont) { - wfacename = U_Utf8ToUtf16le(style->text->font_family.value, 0, NULL); + wfacename = U_Utf8ToUtf16le(style->font_family.value, 0, NULL); } else { wfacename = U_Utf8ToUtf16le(FontName(newfont), 0, NULL); } diff --git a/src/extension/internal/wmf-inout.h b/src/extension/internal/wmf-inout.h index 3d23ca749..94a290873 100644 --- a/src/extension/internal/wmf-inout.h +++ b/src/extension/internal/wmf-inout.h @@ -39,7 +39,7 @@ typedef struct { } WMF_STRINGS, *PWMF_STRINGS; typedef struct wmf_device_context { - struct SPStyle style; + class SPStyle style; char *font_name; bool stroke_set; int stroke_mode; // enumeration from drawmode, not used if fill_set is not True diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp index 5a552ad83..55ad5da5f 100644 --- a/src/extension/internal/wmf-print.cpp +++ b/src/extension/internal/wmf-print.cpp @@ -1387,7 +1387,7 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te _lookup_ppt_fontfix("Convert To Wingdings", params); break; default: //also CVTNON - _lookup_ppt_fontfix(style->text->font_family.value, params); + _lookup_ppt_fontfix(style->font_family.value, params); break; } if (params.f2 != 0 || params.f3 != 0) { @@ -1416,7 +1416,7 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te // of the special fonts. char *facename; if (!newfont) { - facename = U_Utf8ToLatin1(style->text->font_family.value, 0, NULL); + facename = U_Utf8ToLatin1(style->font_family.value, 0, NULL); } else { facename = U_Utf8ToLatin1(FontName(newfont), 0, NULL); } diff --git a/src/graphlayout.cpp b/src/graphlayout.cpp index 7e10ccca1..18159cb41 100644 --- a/src/graphlayout.cpp +++ b/src/graphlayout.cpp @@ -195,8 +195,8 @@ void graphlayout(GSList const *const items) { unsigned v=v_pair->second; //cout << "Edge: (" << u <<","<<v<<")"<<endl; es.push_back(make_pair(u,v)); - if(conn->style->marker[SP_MARKER_LOC_END].set) { - if(directed && strcmp(conn->style->marker[SP_MARKER_LOC_END].value,"none")) { + if(conn->style->marker_end.set) { + if(directed && strcmp(conn->style->marker_end.value,"none")) { scy.push_back(new SimpleConstraint(v, u, (ideal_connector_length * directed_edge_height_modifier))); } diff --git a/src/id-clash.cpp b/src/id-clash.cpp index f59b3b920..66357b75b 100644 --- a/src/id-clash.cpp +++ b/src/id-clash.cpp @@ -53,12 +53,12 @@ const char *href_like_attributes[] = { #define NUM_HREF_LIKE_ATTRIBUTES (sizeof(href_like_attributes) / sizeof(*href_like_attributes)) const SPIPaint SPStyle::* SPIPaint_members[] = { - &SPStyle::color, + //&SPStyle::color, &SPStyle::fill, &SPStyle::stroke, }; const char* SPIPaint_properties[] = { - "color", + //"color", "fill", "stroke", }; @@ -76,7 +76,7 @@ const char* other_url_properties[] = { #define NUM_OTHER_URL_PROPERTIES (sizeof(other_url_properties) / sizeof(*other_url_properties)) const char* clipboard_properties[] = { - "color", + //"color", "fill", "filter", "stroke", @@ -161,7 +161,7 @@ find_references(SPObject *elem, refmap_type *refmap) /* check for url(#...) references in markers */ const gchar *markers[4] = { "", "marker-start", "marker-mid", "marker-end" }; for (unsigned i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) { - const gchar *value = style->marker[i].value; + const gchar *value = style->marker_ptrs[i]->value; if (value) { gchar *uri = extract_uri(value); if (uri && uri[0] == '#') { diff --git a/src/libnrtype/FontFactory.cpp b/src/libnrtype/FontFactory.cpp index 7c0b4ffba..4ae408397 100644 --- a/src/libnrtype/FontFactory.cpp +++ b/src/libnrtype/FontFactory.cpp @@ -825,17 +825,17 @@ font_instance* font_factory::FaceFromStyle(SPStyle const *style) if (style) { // First try to use the font specification if it is set - if (style->text->font_specification.set - && style->text->font_specification.value - && *style->text->font_specification.value) { + if (style->font_specification.set + && style->font_specification.value + && *style->font_specification.value) { - font = FaceFromFontSpecification(style->text->font_specification.value); + font = FaceFromFontSpecification(style->font_specification.value); } // If that failed, try using the CSS information in the style if (!font) { - font = Face(style->text->font_family.value, font_style_to_pos(*style)); + font = Face(style->font_family.value, font_style_to_pos(*style)); // That was a hatchet job... so we need to check if this font exists!! Glib::ustring fontSpec = font_factory::Default()->ConstructFontSpecification(font); diff --git a/src/libnrtype/Layout-TNG-Input.cpp b/src/libnrtype/Layout-TNG-Input.cpp index cb3e6f620..fa1e8c11b 100644 --- a/src/libnrtype/Layout-TNG-Input.cpp +++ b/src/libnrtype/Layout-TNG-Input.cpp @@ -286,16 +286,15 @@ font_instance *Layout::InputStreamTextSource::styleGetFontInstance() const PangoFontDescription *Layout::InputStreamTextSource::styleGetFontDescription() const { - if (style->text == NULL) return NULL; PangoFontDescription *descr = pango_font_description_new(); // Pango can't cope with spaces before or after the commas - let's remove them. // this code is not exactly unicode-safe, but it's similar to what's done in // pango, so it's not the limiting factor Glib::ustring family; - if (style->text->font_family.value == NULL) { + if (style->font_family.value == NULL) { family = "sans-serif"; } else { - gchar **families = g_strsplit(style->text->font_family.value, ",", -1); + gchar **families = g_strsplit(style->font_family.value, ",", -1); if (families) { for (gchar **f = families ; *f ; ++f) { g_strstrip(*f); diff --git a/src/libnrtype/Layout-TNG.h b/src/libnrtype/Layout-TNG.h index c3ccbffb5..efb5ebc24 100644 --- a/src/libnrtype/Layout-TNG.h +++ b/src/libnrtype/Layout-TNG.h @@ -34,7 +34,7 @@ namespace Inkscape { using Inkscape::Extension::Internal::CairoRenderContext; #endif -struct SPStyle; +class SPStyle; class Shape; struct SPPrintContext; class SVGLength; diff --git a/src/libnrtype/font-lister.cpp b/src/libnrtype/font-lister.cpp index dde0ee4a9..98589d9d7 100644 --- a/src/libnrtype/font-lister.cpp +++ b/src/libnrtype/font-lister.cpp @@ -358,8 +358,8 @@ namespace Inkscape sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION); //std::cout << " Attempting selected style" << std::endl; - if( result != QUERY_STYLE_NOTHING && query->text->font_specification.set ) { - fontspec = query->text->font_specification.value; + if( result != QUERY_STYLE_NOTHING && query->font_specification.set ) { + fontspec = query->font_specification.value; //std::cout << " fontspec from query :" << fontspec << ":" << std::endl; } @@ -693,15 +693,15 @@ std::pair<Glib::ustring, Glib::ustring> FontLister::new_font_family (Glib::ustri if (style) { // First try to use the font specification if it is set - if (style->text->font_specification.set - && style->text->font_specification.value - && *style->text->font_specification.value) { + if (style->font_specification.set + && style->font_specification.value + && *style->font_specification.value) { - fontspec = style->text->font_specification.value; + fontspec = style->font_specification.value; } else { - fontspec = style->text->font_family.value; + fontspec = style->font_family.value; fontspec += ","; switch (style->font_weight.computed) { diff --git a/src/libnrtype/font-lister.h b/src/libnrtype/font-lister.h index c42bf98fd..a460388d3 100644 --- a/src/libnrtype/font-lister.h +++ b/src/libnrtype/font-lister.h @@ -26,7 +26,7 @@ class SPObject; class SPDocument; class SPCSSAttr; -struct SPStyle; +class SPStyle; namespace Inkscape { diff --git a/src/libnrtype/font-style-to-pos.h b/src/libnrtype/font-style-to-pos.h index 56eb391c2..41ba6cf72 100644 --- a/src/libnrtype/font-style-to-pos.h +++ b/src/libnrtype/font-style-to-pos.h @@ -3,7 +3,7 @@ #include <libnrtype/nr-type-pos-def.h> -struct SPStyle; +class SPStyle; NRTypePosDef font_style_to_pos(SPStyle const &style); diff --git a/src/livarot/Path.h b/src/livarot/Path.h index a109298a2..32ee71ffc 100644 --- a/src/livarot/Path.h +++ b/src/livarot/Path.h @@ -20,7 +20,7 @@ struct PathDescrCubicTo; struct PathDescrBezierTo; struct PathDescrIntermBezierTo; -struct SPStyle; +class SPStyle; /* * the Path class: a structure to hold path description and their polyline approximation (not kept in sync) diff --git a/src/print.h b/src/print.h index 80d0446fd..bbf95b833 100644 --- a/src/print.h +++ b/src/print.h @@ -18,7 +18,7 @@ class Window; } class SPDocument; -struct SPStyle; +class SPStyle; namespace Inkscape { namespace Extension { diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 5a981c6a0..868a9d743 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -2076,9 +2076,9 @@ GSList *sp_get_same_stroke_style(SPItem *sel, GSList *src, SPSelectStrokeStyleTy match = true; int len = sizeof(sel_style->marker)/sizeof(SPIString); for (int i = 0; i < len; i++) { - match = (sel_style->marker[i].set == iter_style->marker[i].set); - if (sel_style->marker[i].set && iter_style->marker[i].set && - (strcmp(sel_style->marker[i].value, iter_style->marker[i].value))) { + match = (sel_style->marker_ptrs[i]->set == iter_style->marker_ptrs[i]->set); + if (sel_style->marker_ptrs[i]->set && iter_style->marker_ptrs[i]->set && + (strcmp(sel_style->marker_ptrs[i]->value, iter_style->marker_ptrs[i]->value))) { match = false; break; } diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index 7dd14f5d9..f59bc33ee 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -16,6 +16,8 @@ # include "config.h" #endif +#include "ui/tool/multi-path-manipulator.h" + #include <glibmm/i18n.h> #include "live_effects/effect.h" @@ -36,6 +38,9 @@ #include "desktop.h" #include "shape-editor.h" #include "sp-ellipse.h" +#include "tools-switch.h" +#include "ui/tools/node-tool.h" +#include "ui/tools/tool-base.h" #include <algorithm> @@ -417,6 +422,15 @@ void SPLPEItem::addPathEffect(gchar *value, bool reset) // Apply the path effect sp_lpe_item_update_patheffect(this, true, true); + + //fix bug 1219324 + if (SP_ACTIVE_DESKTOP ) { + Inkscape::UI::Tools::ToolBase *ec = SP_ACTIVE_DESKTOP->event_context; + if (INK_IS_NODE_TOOL(ec)) { + tools_switch(SP_ACTIVE_DESKTOP, TOOLS_LPETOOL); //mhh + tools_switch(SP_ACTIVE_DESKTOP, TOOLS_NODES); + } + } } } diff --git a/src/sp-object.h b/src/sp-object.h index 9ce5629e6..e58f161d6 100644 --- a/src/sp-object.h +++ b/src/sp-object.h @@ -56,7 +56,7 @@ class SPObject; #include "util/forward-pointer-iterator.h" class SPCSSAttr; -struct SPStyle; +class SPStyle; namespace Inkscape { namespace XML { diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp index b3a331cba..61d35e6ff 100644 --- a/src/sp-shape.cpp +++ b/src/sp-shape.cpp @@ -77,7 +77,7 @@ void SPShape::build(SPDocument *document, Inkscape::XML::Node *repr) { SPLPEItem::build(document, repr); for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) { - sp_shape_set_marker (this, i, this->style->marker[i].value); + sp_shape_set_marker (this, i, this->style->marker_ptrs[i]->value); } } @@ -136,7 +136,7 @@ void SPShape::update(SPCtx* ctx, guint flags) { * match the style. */ for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) { - sp_shape_set_marker (this, i, this->style->marker[i].value); + sp_shape_set_marker (this, i, this->style->marker_ptrs[i]->value); } if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { @@ -723,7 +723,7 @@ Inkscape::DrawingItem* SPShape::show(Inkscape::Drawing &drawing, unsigned int /* * match the style. */ for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) { - sp_shape_set_marker (this, i, this->style->marker[i].value); + sp_shape_set_marker (this, i, this->style->marker_ptrs[i]->value); } if (this->hasMarkers ()) { diff --git a/src/style-enums.h b/src/style-enums.h index c6f9a1ea1..356029a40 100644 --- a/src/style-enums.h +++ b/src/style-enums.h @@ -15,7 +15,9 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ -/* SPTextStyle */ +/* SPFontStyle */ + +#include "display/canvas-bpath.h" enum SPCSSFontSize { SP_CSS_FONT_SIZE_XX_SMALL, @@ -215,6 +217,267 @@ enum SPTextRendering { SP_CSS_TEXT_RENDERING_GEOMETRICPRECISION }; + +struct SPStyleEnum { + gchar const *key; + gint value; +}; + +static SPStyleEnum const enum_fill_rule[] = { + {"nonzero", SP_WIND_RULE_NONZERO}, + {"evenodd", SP_WIND_RULE_EVENODD}, + {NULL, -1} +}; + +static SPStyleEnum const enum_stroke_linecap[] = { + {"butt", SP_STROKE_LINECAP_BUTT}, + {"round", SP_STROKE_LINECAP_ROUND}, + {"square", SP_STROKE_LINECAP_SQUARE}, + {NULL, -1} +}; + +static SPStyleEnum const enum_stroke_linejoin[] = { + {"miter", SP_STROKE_LINEJOIN_MITER}, + {"round", SP_STROKE_LINEJOIN_ROUND}, + {"bevel", SP_STROKE_LINEJOIN_BEVEL}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_style[] = { + {"normal", SP_CSS_FONT_STYLE_NORMAL}, + {"italic", SP_CSS_FONT_STYLE_ITALIC}, + {"oblique", SP_CSS_FONT_STYLE_OBLIQUE}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_size[] = { + {"xx-small", SP_CSS_FONT_SIZE_XX_SMALL}, + {"x-small", SP_CSS_FONT_SIZE_X_SMALL}, + {"small", SP_CSS_FONT_SIZE_SMALL}, + {"medium", SP_CSS_FONT_SIZE_MEDIUM}, + {"large", SP_CSS_FONT_SIZE_LARGE}, + {"x-large", SP_CSS_FONT_SIZE_X_LARGE}, + {"xx-large", SP_CSS_FONT_SIZE_XX_LARGE}, + {"smaller", SP_CSS_FONT_SIZE_SMALLER}, + {"larger", SP_CSS_FONT_SIZE_LARGER}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_variant[] = { + {"normal", SP_CSS_FONT_VARIANT_NORMAL}, + {"small-caps", SP_CSS_FONT_VARIANT_SMALL_CAPS}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_weight[] = { + {"100", SP_CSS_FONT_WEIGHT_100}, + {"200", SP_CSS_FONT_WEIGHT_200}, + {"300", SP_CSS_FONT_WEIGHT_300}, + {"400", SP_CSS_FONT_WEIGHT_400}, + {"500", SP_CSS_FONT_WEIGHT_500}, + {"600", SP_CSS_FONT_WEIGHT_600}, + {"700", SP_CSS_FONT_WEIGHT_700}, + {"800", SP_CSS_FONT_WEIGHT_800}, + {"900", SP_CSS_FONT_WEIGHT_900}, + {"normal", SP_CSS_FONT_WEIGHT_NORMAL}, + {"bold", SP_CSS_FONT_WEIGHT_BOLD}, + {"lighter", SP_CSS_FONT_WEIGHT_LIGHTER}, + {"bolder", SP_CSS_FONT_WEIGHT_BOLDER}, + {NULL, -1} +}; + +static SPStyleEnum const enum_font_stretch[] = { + {"ultra-condensed", SP_CSS_FONT_STRETCH_ULTRA_CONDENSED}, + {"extra-condensed", SP_CSS_FONT_STRETCH_EXTRA_CONDENSED}, + {"condensed", SP_CSS_FONT_STRETCH_CONDENSED}, + {"semi-condensed", SP_CSS_FONT_STRETCH_SEMI_CONDENSED}, + {"normal", SP_CSS_FONT_STRETCH_NORMAL}, + {"semi-expanded", SP_CSS_FONT_STRETCH_SEMI_EXPANDED}, + {"expanded", SP_CSS_FONT_STRETCH_EXPANDED}, + {"extra-expanded", SP_CSS_FONT_STRETCH_EXTRA_EXPANDED}, + {"ultra-expanded", SP_CSS_FONT_STRETCH_ULTRA_EXPANDED}, + {"narrower", SP_CSS_FONT_STRETCH_NARROWER}, + {"wider", SP_CSS_FONT_STRETCH_WIDER}, + {NULL, -1} +}; + +static SPStyleEnum const enum_text_align[] = { + {"start", SP_CSS_TEXT_ALIGN_START}, + {"end", SP_CSS_TEXT_ALIGN_END}, + {"left", SP_CSS_TEXT_ALIGN_LEFT}, + {"right", SP_CSS_TEXT_ALIGN_RIGHT}, + {"center", SP_CSS_TEXT_ALIGN_CENTER}, + {"justify", SP_CSS_TEXT_ALIGN_JUSTIFY}, + {NULL, -1} +}; + +static SPStyleEnum const enum_text_transform[] = { + {"capitalize", SP_CSS_TEXT_TRANSFORM_CAPITALIZE}, + {"uppercase", SP_CSS_TEXT_TRANSFORM_UPPERCASE}, + {"lowercase", SP_CSS_TEXT_TRANSFORM_LOWERCASE}, + {"none", SP_CSS_TEXT_TRANSFORM_NONE}, + {NULL, -1} +}; + +static SPStyleEnum const enum_text_anchor[] = { + {"start", SP_CSS_TEXT_ANCHOR_START}, + {"middle", SP_CSS_TEXT_ANCHOR_MIDDLE}, + {"end", SP_CSS_TEXT_ANCHOR_END}, + {NULL, -1} +}; + +static SPStyleEnum const enum_direction[] = { + {"ltr", SP_CSS_DIRECTION_LTR}, + {"rtl", SP_CSS_DIRECTION_RTL}, + {NULL, -1} +}; + +static SPStyleEnum const enum_block_progression[] = { + {"tb", SP_CSS_BLOCK_PROGRESSION_TB}, + {"rl", SP_CSS_BLOCK_PROGRESSION_RL}, + {"lr", SP_CSS_BLOCK_PROGRESSION_LR}, + {NULL, -1} +}; + +static SPStyleEnum const enum_writing_mode[] = { + /* Note that using the same enumerator for lr as lr-tb means we write as lr-tb even if the + * input file said lr. We prefer writing lr-tb on the grounds that the spec says the initial + * value is lr-tb rather than lr. + * + * ECMA scripts may be surprised to find tb-rl in DOM if they set the attribute to rl, so + * sharing enumerators for different strings may be a bug (once we support ecma script). + */ + {"lr-tb", SP_CSS_WRITING_MODE_LR_TB}, + {"rl-tb", SP_CSS_WRITING_MODE_RL_TB}, + {"tb-rl", SP_CSS_WRITING_MODE_TB_RL}, + {"lr", SP_CSS_WRITING_MODE_LR_TB}, + {"rl", SP_CSS_WRITING_MODE_RL_TB}, + {"tb", SP_CSS_WRITING_MODE_TB_RL}, + {NULL, -1} +}; + +static SPStyleEnum const enum_baseline_shift[] = { + {"baseline", SP_CSS_BASELINE_SHIFT_BASELINE}, + {"sub", SP_CSS_BASELINE_SHIFT_SUB}, + {"super", SP_CSS_BASELINE_SHIFT_SUPER}, + {NULL, -1} +}; + +static SPStyleEnum const enum_visibility[] = { + {"hidden", SP_CSS_VISIBILITY_HIDDEN}, + {"collapse", SP_CSS_VISIBILITY_COLLAPSE}, + {"visible", SP_CSS_VISIBILITY_VISIBLE}, + {NULL, -1} +}; + +static SPStyleEnum const enum_overflow[] = { + {"visible", SP_CSS_OVERFLOW_VISIBLE}, + {"hidden", SP_CSS_OVERFLOW_HIDDEN}, + {"scroll", SP_CSS_OVERFLOW_SCROLL}, + {"auto", SP_CSS_OVERFLOW_AUTO}, + {NULL, -1} +}; + +// CSS Compositing and Blending Level 1 +static SPStyleEnum const enum_isolation[] = { + {"auto", SP_CSS_ISOLATION_AUTO}, + {"isolate", SP_CSS_ISOLATION_ISOLATE}, + {NULL, -1} +}; + +static SPStyleEnum const enum_blend_mode[] = { + {"normal", SP_CSS_BLEND_NORMAL}, + {"multiply", SP_CSS_BLEND_MULTIPLY}, + {"screen", SP_CSS_BLEND_SCREEN}, + {"darken", SP_CSS_BLEND_DARKEN}, + {"lighten", SP_CSS_BLEND_LIGHTEN}, + {"overlay", SP_CSS_BLEND_OVERLAY}, + {"color-dodge", SP_CSS_BLEND_COLORDODGE}, + {"color-burn", SP_CSS_BLEND_COLORBURN}, + {"hard-light", SP_CSS_BLEND_HARDLIGHT}, + {"soft-light", SP_CSS_BLEND_SOFTLIGHT}, + {"difference", SP_CSS_BLEND_DIFFERENCE}, + {"exclusion", SP_CSS_BLEND_EXCLUSION}, + {"hue", SP_CSS_BLEND_HUE}, + {"saturation", SP_CSS_BLEND_SATURATION}, + {"color", SP_CSS_BLEND_COLOR}, + {"luminosity", SP_CSS_BLEND_LUMINOSITY}, + {NULL, -1} +}; + +static SPStyleEnum const enum_display[] = { + {"none", SP_CSS_DISPLAY_NONE}, + {"inline", SP_CSS_DISPLAY_INLINE}, + {"block", SP_CSS_DISPLAY_BLOCK}, + {"list-item", SP_CSS_DISPLAY_LIST_ITEM}, + {"run-in", SP_CSS_DISPLAY_RUN_IN}, + {"compact", SP_CSS_DISPLAY_COMPACT}, + {"marker", SP_CSS_DISPLAY_MARKER}, + {"table", SP_CSS_DISPLAY_TABLE}, + {"inline-table", SP_CSS_DISPLAY_INLINE_TABLE}, + {"table-row-group", SP_CSS_DISPLAY_TABLE_ROW_GROUP}, + {"table-header-group", SP_CSS_DISPLAY_TABLE_HEADER_GROUP}, + {"table-footer-group", SP_CSS_DISPLAY_TABLE_FOOTER_GROUP}, + {"table-row", SP_CSS_DISPLAY_TABLE_ROW}, + {"table-column-group", SP_CSS_DISPLAY_TABLE_COLUMN_GROUP}, + {"table-column", SP_CSS_DISPLAY_TABLE_COLUMN}, + {"table-cell", SP_CSS_DISPLAY_TABLE_CELL}, + {"table-caption", SP_CSS_DISPLAY_TABLE_CAPTION}, + {NULL, -1} +}; + +static SPStyleEnum const enum_shape_rendering[] = { + {"auto", SP_CSS_SHAPE_RENDERING_AUTO}, + {"optimizeSpeed", SP_CSS_SHAPE_RENDERING_OPTIMIZESPEED}, + {"crispEdges", SP_CSS_SHAPE_RENDERING_CRISPEDGES}, + {"geometricPrecision", SP_CSS_SHAPE_RENDERING_GEOMETRICPRECISION}, + {NULL, -1} +}; + +static SPStyleEnum const enum_color_rendering[] = { + {"auto", SP_CSS_COLOR_RENDERING_AUTO}, + {"optimizeSpeed", SP_CSS_COLOR_RENDERING_OPTIMIZESPEED}, + {"optimizeQuality", SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY}, + {NULL, -1} +}; + +static SPStyleEnum const enum_image_rendering[] = { + {"auto", SP_CSS_IMAGE_RENDERING_AUTO}, + {"optimizeSpeed", SP_CSS_IMAGE_RENDERING_OPTIMIZESPEED}, + {"optimizeQuality", SP_CSS_IMAGE_RENDERING_OPTIMIZEQUALITY}, + {"-inkscape-crisp-edges", SP_CSS_IMAGE_RENDERING_CRISPEDGES}, + {"-inkscape-pixelated", SP_CSS_IMAGE_RENDERING_PIXELATED}, + {NULL, -1} +}; + +static SPStyleEnum const enum_text_rendering[] = { + {"auto", SP_CSS_TEXT_RENDERING_AUTO}, + {"optimizeSpeed", SP_CSS_TEXT_RENDERING_OPTIMIZESPEED}, + {"optimizeLegibility", SP_CSS_TEXT_RENDERING_OPTIMIZELEGIBILITY}, + {"geometricPrecision", SP_CSS_TEXT_RENDERING_GEOMETRICPRECISION}, + {NULL, -1} +}; + +static SPStyleEnum const enum_enable_background[] = { + {"accumulate", SP_CSS_BACKGROUND_ACCUMULATE}, + {"new", SP_CSS_BACKGROUND_NEW}, + {NULL, -1} +}; + +static SPStyleEnum const enum_clip_rule[] = { + {"nonzero", SP_WIND_RULE_NONZERO}, + {"evenodd", SP_WIND_RULE_EVENODD}, + {NULL, -1} +}; + +static SPStyleEnum const enum_color_interpolation[] = { + {"auto", SP_CSS_COLOR_INTERPOLATION_AUTO}, + {"sRGB", SP_CSS_COLOR_INTERPOLATION_SRGB}, + {"linearRGB", SP_CSS_COLOR_INTERPOLATION_LINEARRGB}, + {NULL, -1} +}; + + #endif // SEEN_SP_STYLE_ENUMS_H diff --git a/src/style-internal.cpp b/src/style-internal.cpp new file mode 100644 index 000000000..b03e848ba --- /dev/null +++ b/src/style-internal.cpp @@ -0,0 +1,2535 @@ +/** + * @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 && &style->font_size ) { + computed = value * style->font_size.computed; + } else { + computed = value * style->font_size.font_size_default; + } + } else if (!strcmp(e, "ex")) { + /* ex square */ + unit = SP_CSS_UNIT_EX; + if( style && &style->font_size ) { + computed = value * style->font_size.computed * 0.5; // FIXME + } else { + computed = value * style->font_size.font_size_default * 0.5; + } + } else if (!strcmp(e, "%")) { + /* Percentage */ + unit = SP_CSS_UNIT_PERCENT; + value = value * 0.01; + } 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 ) { + 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 { + 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::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; + + g_free(value); + + if (!strcmp(str, "inherit")) { + set = TRUE; + inherit = TRUE; + value = NULL; + } else { + set = TRUE; + inherit = FALSE; + value = g_strdup(str); + } +} + + +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 ) { + // This is for compatibilty with the C version of code. + // This is incorrect as it puts single quotes around the + // entire string rather around the individule font names. + // This should be handled by the routines that extract + // out the font family names and reassembles them into a + // font fallback list. FIXME + return (name + ":" + css2_escape_quote(this->value) + ";"); + } else { + return (name + ":" + this->value + ";"); + } + } + } + } + return Glib::ustring(""); +} + +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 { + value.color = 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; + value.color = 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); + value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((this == &style->fill)? sp_style_fill_paint_server_ref_changed : 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; + value.color = style->color.value.color; + } 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; + value.color = style->color.value.color; + } else if( isNone() ) { + // + } else { + g_assert_not_reached(); + } + } else { + if( currentcolor ) { + // Update in case color value changed. + value.color = 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 && style->object) { + href = new SPFilterReference(style->object); + href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); + } + + 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)) + { + 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 due 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)); + } + } + 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; + + style->text_decoration_line.read( str ); + style->text_decoration_style.read( str ); + // 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? + const gchar *hstr = str; + + style->text_decoration_color.read( "currentColor" ); // Default value + style->text_decoration_color.set = false; + 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 + style->text_decoration_color.read( frag ); + } + + free(frag); + if( style->text_decoration_color.set ) break; + style->text_decoration_color.read( "currentColor" ); // Default value + if( *str == '\0' )break; + hstr = str + 1; + } + str++; + } +} + +// 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(""); +} + +// Done in SPITextDecorationLine +// void +// SPITextDecoration::cascade( const SPIBase* const parent ) { +// } + +// void +// SPITextDecoration::merge( const SPIBase* const parent ) { +// } + +// 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 : diff --git a/src/style-internal.h b/src/style-internal.h index 7d45f96f8..131b77b77 100644 --- a/src/style-internal.h +++ b/src/style-internal.h @@ -7,7 +7,9 @@ /* Authors: * Lauris Kaplinski <lauris@kaplinski.com> * Jon A. Cruz <jon@joncruz.org> + * Tavmjong Bah <tavmjong@free.fr> * + * Copyright (C) 2014 Tavmjong Bah * Copyright (C) 2010 Jon A. Cruz * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. @@ -15,21 +17,168 @@ * Released under GNU GPL, read the file 'COPYING' for more information */ +#include "style-enums.h" + #include "color.h" +#include "svg/svg-icc-color.h" #include "sp-marker-loc.h" #include "sp-filter.h" #include "sp-filter-reference.h" #include "sp-paint-server-reference.h" #include "uri.h" +#include "xml/repr.h" #include <vector> -/// Float type internal to SPStyle. -struct SPIFloat { - unsigned set : 1; - unsigned inherit : 1; - unsigned data : 30; +struct SPStyleEnum; + +static const unsigned SP_STYLE_FLAG_ALWAYS (1 << 2); +static const unsigned SP_STYLE_FLAG_IFSET (1 << 0); +static const unsigned SP_STYLE_FLAG_IFDIFF (1 << 1); + + +/* General comments: + * + * This code is derived from the original C style code in style.cpp. + * + * Overview: + * Style can be obtained (in order of precidence) [CHECK] + * 1. "style" property in an element (style="fill:red"). + * 2. Style sheet, internal or external (<style> rect {fill:red;}</style>). + * 3. Attributes in an element (fill="red"). + * 4. Parent's style. + * A later property overrides an earlier property. This is implemented by + * reading in the properties backwards. If a property is already set, it + * prevents an earlier property from being read. + * + * In order for cascading to work, each element in the tree must be read in from top to bottom + * (parent before child). At each step, if a style property is not explicitly set, the property + * value is taken from the parent. Some properties have "computed" values that depend on: + * the parent's value (e.g. "font-size:larger"), + * another property value ("stroke-width":1em"), or + * an external value ("stroke-width:5%"). + * + * To summarize: + * + * An explicitly set value (including 'inherit') has a 'true' "set" flag. + * The "value" is either explicitly set or inherited. + * The "computed" value (if present) is calculated from "value" and some other input. + * + * Functions: + * write(): Write a property and its value to a string. + * Flags: + * ALWAYS: Always write out property. + * IFSET: Write a property if 'set' flag is true, otherwise return empty string. + * IFDIFF: Write a property if computed values are different, otherwise return empty string, + * This is only used for text!! + * + * read(): Set a property value from a string. + * clear(): Set a property to its default value and set the 'set' flag to false. + * cascade(): Cascade the parent's property values to the child if the child's property + * is unset (and it allows inheriting) or the value is 'inherit'. + * Calculate computed values that depend on parent. + * This requires that the parent already be updated. + * merge(): Merge the property values of a child and a parent that is being deleted, + * attempting to preserve the style of the child. + * operator=: Assignment operator required due to use of templates (in original C code). + * operator==: True if computed values are equal. TO DO: DEFINE EXACTLY WHAT THIS MEANS + * operator!=: Inverse of operator==. + * + * + * Outside dependencies: + * + * The C structures that these classes are evolved from were designed to be embedded in to the + * style structure (i.e they are "internal" and thus have an "I" in the SPI prefix). However, + * they should be reasonably stand-alone and can provide some functionality outside of the style + * stucture (i.e. reading and writing style strings). Some properties do need access to other + * properties from the same object (e.g. SPILength sometimes needs to know font size) to + * calculate 'computed' values. Inheritence, of course, requires access to the parent object's + * style class. + * + * The only real outside dependancy is SPObject... which is needed in the cases of SPIPaint and + * SPIFilter for setting up the "href". (Currently, SPDocument is needed but this dependency + * should be removed as an "href" only needs the SPDocument for attaching an external document to + * the XML tree [see uri-references.cpp]. If SPDocument is really needed, it can be obtained from + * SPObject.) + * + */ + +/// Virtual base class for all SPStyle interal classes +class SPIBase { + + public: + SPIBase( Glib::ustring const &name, bool inherits = true ) + : name(name), inherits(inherits), set(false), inherit(false), style_att(false), style(NULL) {}; + virtual ~SPIBase() {}; + virtual void read( gchar const *str ) = 0; + virtual void readIfUnset( gchar const *str ) { if( !set ) read( str ); } + virtual void readAttribute( Inkscape::XML::Node *repr ) { readIfUnset( repr->attribute( name.c_str() ) ); } + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const = 0; + virtual void clear() { set = false, inherit = false; }; + virtual void cascade( const SPIBase* const parent ) {}; + virtual void merge( const SPIBase* const parent ) {}; // To do: Set to 0 + + virtual void setStylePointer( SPStyle *style_in ) { style = style_in; }; + + // Explicit assignment operator required due to templates. + SPIBase& operator=(const SPIBase& rhs) { + name = rhs.name; + inherits = rhs.inherits; + set = rhs.set; + inherit = rhs.inherit; + style_att = rhs.style_att; + return *this; + } + + // Check apples being compared to apples + virtual bool operator==(const SPIBase& rhs) { return (name == rhs.name); }; + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: + Glib::ustring name; // Make const + unsigned inherits : 1; // Property inherits by default from parent. + unsigned set : 1; // Property has been explicitly set (vs. inherited). + unsigned inherit : 1; // Property value set to 'inherit'. + unsigned style_att : 2; // Source (attribute, style attribute, style-sheet). NOT USED YET FIX ME + + // To do: make private after g_asserts removed + public: + SPStyle* style; // Used by SPIPaint, SPIFilter... to find values of other properties +}; + +/// Float type internal to SPStyle. (Only 'stroke-miterlimit') +class SPIFloat : public SPIBase { + + public: + SPIFloat() : SPIBase( "anonymous_float" ), value(0.0) {}; + SPIFloat( Glib::ustring name, float value_default = 0.0 ) + : SPIBase( name ), value(value_default), value_default(value_default) {}; + virtual ~SPIFloat() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIFloat& operator=(const SPIFloat& rhs) { + SPIBase::operator=(rhs); + value = rhs.value; + value_default = value_default; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: float value; + + private: + float value_default; }; /* @@ -60,44 +209,42 @@ static const unsigned SP_SCALE24_MAX = 0xff0000; /** Returns a scale24 for the product of two scale24 values. */ #define SP_SCALE24_MUL(_v1, _v2) unsigned((double)(_v1) * (_v2) / SP_SCALE24_MAX + .5) + /// 24 bit data type internal to SPStyle. -struct SPIScale24 { - unsigned set : 1; - unsigned inherit : 1; +// Used only for opacity, fill-opacity, stroke-opacity. +// Opacity does not inherit but stroke-opacity and fill-opacity do. +class SPIScale24 : public SPIBase { + + public: + SPIScale24() : SPIBase( "anonymous_scale24" ), value(0) {}; + SPIScale24( Glib::ustring name, unsigned value = 0, bool inherits = true ) + : SPIBase( name, inherits ), value(value), value_default(value) {}; + virtual ~SPIScale24() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); value = value_default; }; + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIScale24& operator=(const SPIScale24& rhs) { + SPIBase::operator=(rhs); + value = rhs.value; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + + // To do: make private + public: unsigned value : 24; -}; - -/// Int type internal to SPStyle. -struct SPIInt { - unsigned set : 1; - unsigned inherit : 1; - unsigned data : 30; - int value; -}; -/// Short type internal to SPStyle. -struct SPIShort { - unsigned set : 1; - unsigned inherit : 1; - unsigned data : 14; - int value : 16; -}; - -/// Enum type internal to SPStyle. -struct SPIEnum { - unsigned set : 1; - unsigned inherit : 1; - unsigned value : 8; - unsigned computed : 8; + private: + unsigned value_default : 24; }; -/// String type internal to SPStyle. -struct SPIString { - unsigned set : 1; - unsigned inherit : 1; - unsigned data : 30; - gchar *value; -}; enum SPCSSUnit { SP_CSS_UNIT_NONE, @@ -112,33 +259,232 @@ enum SPCSSUnit { SP_CSS_UNIT_PERCENT }; + /// Length type internal to SPStyle. -struct SPILength { - unsigned set : 1; - unsigned inherit : 1; +// Needs access to 'font-size' and 'font-family' for computed values. +// Used for 'stroke-width' 'stroke-dash-offset' ('none' not handled), text-indent +class SPILength : public SPIBase { + + public: + SPILength() : SPIBase( "anonymous_length" ), unit(SP_CSS_UNIT_NONE), value(0), computed(0) {}; + SPILength( Glib::ustring name, unsigned value = 0 ) + : SPIBase( name ), unit(SP_CSS_UNIT_NONE), value(value), computed(value), value_default(value) {}; + virtual ~SPILength() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); unit = SP_CSS_UNIT_NONE, value = value_default; computed = value_default; }; + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPILength& operator=(const SPILength& rhs) { + SPIBase::operator=(rhs); + unit = rhs.unit; + value = rhs.value; + computed = rhs.computed; + value_default = rhs.value_default; + return *this; + }; + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: unsigned unit : 4; float value; float computed; + + private: + float value_default; }; -#define SP_STYLE_FILL_SERVER(s) ((const_cast<SPStyle *> (s))->getFillPaintServer()) -#define SP_STYLE_STROKE_SERVER(s) ((const_cast<SPStyle *> (s))->getStrokePaintServer()) -/// Paint type internal to SPStyle. -struct SPIPaint { - unsigned int set : 1; //c++ bitfields are used here as opposed to bools to reduce memory consumption, see http://tinyurl.com/cswh6mq - unsigned int inherit : 1; - unsigned int currentcolor : 1; - unsigned int colorSet : 1; - unsigned int noneSet : 1; +/// Extended length type internal to SPStyle. +// Used for: line-height, letter-spacing, word-spacing +class SPILengthOrNormal : public SPILength { + + public: + SPILengthOrNormal() : SPILength( "anonymous_length" ), normal(true) {}; + SPILengthOrNormal( Glib::ustring name, unsigned value = 0 ) + : SPILength( name, value ), normal(true) {}; + virtual ~SPILengthOrNormal() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPILength::clear(); normal = true; }; + // virtual void cascade( const SPIBase* const parent ); // Use SPILength::cascade + virtual void merge( const SPIBase* const parent ); + + SPILengthOrNormal& operator=(const SPILengthOrNormal& rhs) { + SPILength::operator=(rhs); + normal = rhs.normal; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: + bool normal : 1; +}; + + +/// Enum type internal to SPStyle. +// Used for many properties. 'font-stretch' and 'font-weight' must be special cased. +class SPIEnum : public SPIBase { + + public: + SPIEnum() : + SPIBase( "anonymous_enum" ), enums( NULL ), value(0), computed(0) {}; + SPIEnum( Glib::ustring name, SPStyleEnum const *enums, unsigned value = 0, bool inherits = true ) : + SPIBase( name, inherits ), enums( enums ), value(value), computed(value), + value_default(value), computed_default(value) {}; + // Following is needed for font-weight + SPIEnum( Glib::ustring name, SPStyleEnum const *enums, SPCSSFontWeight value, SPCSSFontWeight computed ) : + SPIBase( name ), enums( enums ), value(value), computed(computed), + value_default(value), computed_default(computed) {}; + virtual ~SPIEnum() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); value = value_default, computed = computed_default; }; + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIEnum& operator=(const SPIEnum& rhs) { + SPIBase::operator=(rhs); + value = rhs.value; + computed = rhs.computed; + value_default = rhs.value_default; + computed_default = rhs.computed_default; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: + SPStyleEnum const *enums; + + unsigned value : 8; + unsigned computed: 8; + + private: + unsigned value_default : 8; + unsigned computed_default: 8; // for font-weight +}; + + +/// String type internal to SPStyle. +// Used for 'marker', ..., 'font', 'font-family', 'inkscape-font-specification' +class SPIString : public SPIBase { + + public: + SPIString() : + SPIBase( "anonymous_string" ), value(NULL) {}; + SPIString( Glib::ustring name ) : + SPIBase( name ) , value(NULL) {}; + virtual ~SPIString() { g_free(value); }; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); g_free( value ); value = NULL; }; + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIString& operator=(const SPIString& rhs) { + SPIBase::operator=(rhs); + value = rhs.value?g_strdup(rhs.value):NULL; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private, convert value to Glib::ustring + public: + gchar *value; +}; + +/// Color type interal to SPStyle, FIXME Add string value to store SVG named color. +class SPIColor : public SPIBase { + + public: + SPIColor() : SPIBase( "anonymous_color" ), currentcolor(false) { value.color.set(0); } + SPIColor( Glib::ustring name ) : SPIBase( name ), currentcolor(false) { value.color.set(0); } + virtual ~SPIColor() {} + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); value.color.set(0); } + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIColor& operator=(const SPIColor& rhs) { + SPIBase::operator=(rhs); + value.color = rhs.value.color; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); } + + void setColor( float r, float g, float b ) { value.color.set( r, g, b ); } + void setColor( guint32 val ) { value.color.set( val ); } + void setColor( SPColor const& color ) { value.color = color; } + + public: + bool currentcolor : 1; + // FIXME: remove structure and derive SPIPaint from this class. struct { - SPPaintServerReference *href; SPColor color; } value; +}; + - SPIPaint(); - bool isSet() const { return true; /* set || colorSet*/} +#define SP_STYLE_FILL_SERVER(s) ((const_cast<SPStyle *> (s))->getFillPaintServer()) +#define SP_STYLE_STROKE_SERVER(s) ((const_cast<SPStyle *> (s))->getStrokePaintServer()) + +/// Paint type internal to SPStyle. +class SPIPaint : public SPIBase { + + public: + SPIPaint() : SPIBase( "anonymous_paint" ), currentcolor(false), colorSet(false), noneSet(false) { + value.href = NULL; + clear(); + }; + SPIPaint( Glib::ustring name ) + : SPIBase( name ), currentcolor(false), colorSet(false), noneSet(false) { + value.href = NULL; + clear(); // Sets defaults + }; + virtual ~SPIPaint(); // Clear and delete href. + virtual void read( gchar const *str ); + virtual void read( gchar const *str, SPStyle &style, SPDocument *document = 0); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear(); + virtual void reset( bool init ); // Used internally when reading or cascading + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIPaint& operator=(const SPIPaint& rhs) { + SPIBase::operator=(rhs); + currentcolor = rhs.currentcolor; + colorSet = rhs.colorSet; + noneSet = rhs.noneSet; + value.color = rhs.value.color; + value.href = rhs.value.href; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + bool isSameType( SPIPaint const & other ) const {return (isPaintserver() == other.isPaintserver()) && (colorSet == other.colorSet) && (currentcolor == other.currentcolor);} bool isNoneSet() const {return noneSet;} @@ -147,22 +493,24 @@ struct SPIPaint { bool isColor() const {return colorSet && !isPaintserver();} bool isPaintserver() const {return (value.href) ? value.href->getObject():0;} - void clear(); - void setColor( float r, float g, float b ) {value.color.set( r, g, b ); colorSet = true;} void setColor( guint32 val ) {value.color.set( val ); colorSet = true;} void setColor( SPColor const& color ) {value.color = color; colorSet = true;} - void read( gchar const *str, SPStyle &tyle, SPDocument *document = 0); -}; -class SPIDashArray { - public: - unsigned set : 1; - unsigned inherit : 1; - std::vector<double> values; + + // To do: make private + public: + bool currentcolor : 1; + bool colorSet : 1; + bool noneSet : 1; + struct { + SPPaintServerReference *href; + SPColor color; + } value; }; + // SVG 2 enum SPPaintOrderLayer { SP_CSS_PAINT_ORDER_NORMAL, @@ -171,54 +519,225 @@ enum SPPaintOrderLayer { SP_CSS_PAINT_ORDER_MARKER }; +// Normal maybe should be moved out as is done in other classes. +// This could be replaced by a generic enum class where multiple keywords are allowed and +// where order matters (in contrast to 'text-decoration-line' where order does not matter). + +// Each layer represents a layer of paint which can be a fill, a stroke, or markers. const size_t PAINT_ORDER_LAYERS = 3; -struct SPIPaintOrder { - unsigned set : 1; - unsigned inherit : 1; + +/// Paint order type internal to SPStyle +class SPIPaintOrder : public SPIBase { + + public: + SPIPaintOrder() : SPIBase( "paint-order" ), value(NULL) { this->clear(); }; + virtual ~SPIPaintOrder() { g_free( value ); }; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { + SPIBase::clear(); + for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) { + layer[i] = SP_CSS_PAINT_ORDER_NORMAL; + layer_set[i] = false; + } + g_free(value); + value = NULL; + } + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIPaintOrder& operator=(const SPIPaintOrder& rhs) { + SPIBase::operator=(rhs); + for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) { + layer[i] = rhs.layer[i]; + layer_set[i] = rhs.layer_set[i]; + } + value = g_strdup(rhs.value); + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + + // To do: make private + public: SPPaintOrderLayer layer[PAINT_ORDER_LAYERS]; bool layer_set[PAINT_ORDER_LAYERS]; gchar *value; // Raw string }; + /// Filter type internal to SPStyle -struct SPIFilter { - unsigned set : 1; - unsigned inherit : 1; +class SPIDashArray : public SPIBase { + + public: + SPIDashArray() : SPIBase( "stroke-dasharray" ) {}; // Only one instance of SPIDashArray + virtual ~SPIDashArray() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); values.clear(); }; + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIDashArray& operator=(const SPIDashArray& rhs) { + SPIBase::operator=(rhs); + values = rhs.values; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + + // To do: make private, change double to SVGLength + public: + std::vector<double> values; +}; + +/// Filter type internal to SPStyle +class SPIFilter : public SPIBase { + + public: + SPIFilter() : SPIBase( "filter", false ), href(NULL) {}; + virtual ~SPIFilter(); + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear(); + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIFilter& operator=(const SPIFilter& rhs) { + SPIBase::operator=(rhs); + href = rhs.href; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: SPFilterReference *href; }; + + enum { SP_FONT_SIZE_LITERAL, SP_FONT_SIZE_LENGTH, SP_FONT_SIZE_PERCENTAGE }; -enum { - SP_BASELINE_SHIFT_LITERAL, - SP_BASELINE_SHIFT_LENGTH, - SP_BASELINE_SHIFT_PERCENTAGE -}; - - -#define SP_STYLE_FLAG_IFSET (1 << 0) -#define SP_STYLE_FLAG_IFDIFF (1 << 1) -#define SP_STYLE_FLAG_ALWAYS (1 << 2) - /// Fontsize type internal to SPStyle (also used by libnrtype/Layout-TNG-Input.cpp). -struct SPIFontSize { - unsigned set : 1; - unsigned inherit : 1; +class SPIFontSize : public SPIBase { + + public: + SPIFontSize() : SPIBase( "font-size" ) { this->clear(); }; + virtual ~SPIFontSize() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); type = SP_FONT_SIZE_LITERAL, unit = SP_CSS_UNIT_NONE, + literal = SP_CSS_FONT_SIZE_MEDIUM, value = 12.0, computed = 12.0; } + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIFontSize& operator=(const SPIFontSize& rhs) { + SPIBase::operator=(rhs); + type = rhs.type; + unit = rhs.unit; + literal = rhs.literal; + value = rhs.value; + computed = rhs.computed; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + public: + static float const font_size_default; + + // To do: make private + public: unsigned type : 2; unsigned unit : 4; - unsigned literal: 4; + unsigned literal : 4; float value; float computed; + + private: + double relative_fraction() const; + static float const font_size_table[]; }; -/// Baseline shift type internal to SPStyle. -struct SPIBaselineShift { - unsigned set : 1; - unsigned inherit : 1; + +/// Font type internal to SPStyle ('font' shorthand) +class SPIFont : public SPIBase { + + public: + SPIFont() : SPIBase( "font" ) {}; + virtual ~SPIFont() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { + SPIBase::clear(); + }; + virtual void cascade( const SPIBase* const parent ) {}; // Done in dependent properties + virtual void merge( const SPIBase* const parent ) {}; + + SPIFont& operator=(const SPIFont& rhs) { + SPIBase::operator=(rhs); + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; +}; + + +enum { + SP_BASELINE_SHIFT_LITERAL, + SP_BASELINE_SHIFT_LENGTH, + SP_BASELINE_SHIFT_PERCENTAGE +}; + +/// Baseline shift type internal to SPStyle. (This is actually just like SPIFontSize) +class SPIBaselineShift : public SPIBase { + + public: + SPIBaselineShift() : SPIBase( "baseline-shift", false ) { this->clear(); }; + virtual ~SPIBaselineShift() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); type=SP_BASELINE_SHIFT_LITERAL, unit=SP_CSS_UNIT_NONE, + literal = SP_CSS_BASELINE_SHIFT_BASELINE, value = 0.0, computed = 0.0; } + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPIBaselineShift& operator=(const SPIBaselineShift& rhs) { + SPIBase::operator=(rhs); + type = rhs.type; + unit = rhs.unit; + literal = rhs.literal; + value = rhs.value; + computed = rhs.computed; + return *this; + } + + // This is not used but we have it for completeness, it has not been tested. + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + bool isZero() const; + + // To do: make private + public: unsigned type : 2; unsigned unit : 4; unsigned literal: 2; @@ -227,38 +746,112 @@ struct SPIBaselineShift { }; // CSS 2. Changes in CSS 3, where description is for TextDecorationLine, NOT TextDecoration -/// Text decoration type internal to SPStyle. -struct SPITextDecorationLine { - unsigned set : 1; - unsigned inherit : 1; - unsigned underline : 1; - unsigned overline : 1; - unsigned line_through : 1; - unsigned blink : 1; // "Conforming user agents are not required to support this value." yay! +// See http://www.w3.org/TR/css-text-decor-3/ + +// CSS3 2.2 +/// Text decoration line type internal to SPStyle. THIS SHOULD BE A GENERIC CLASS +class SPITextDecorationLine : public SPIBase { + + public: + SPITextDecorationLine() : SPIBase( "text-decoration-line" ) { this->clear(); }; + virtual ~SPITextDecorationLine() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); underline = false, overline = false, line_through = false, blink = false; } + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPITextDecorationLine& operator=(const SPITextDecorationLine& rhs) { + SPIBase::operator=(rhs); + underline = rhs.underline; + overline = rhs.overline; + line_through = rhs.line_through; + blink = rhs.blink; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: + bool underline : 1; + bool overline : 1; + bool line_through : 1; + bool blink : 1; // "Conforming user agents are not required to support this value." yay! }; // CSS3 2.2 -/// Text decoration style type internal to SPStyle. -struct SPITextDecorationStyle { - unsigned set : 1; - unsigned inherit : 1; - unsigned solid : 1; - unsigned isdouble : 1; // cannot use "double" as it is a reserved keyword - unsigned dotted : 1; - unsigned dashed : 1; - unsigned wavy : 1; +/// Text decoration style type internal to SPStyle. THIS SHOULD JUST BE SPIEnum! +class SPITextDecorationStyle : public SPIBase { + + public: + SPITextDecorationStyle() : SPIBase( "text-decoration-style" ) { this->clear(); }; + virtual ~SPITextDecorationStyle() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { SPIBase::clear(); solid = true, isdouble = false, dotted = false, dashed = false, wavy = false; } + virtual void cascade( const SPIBase* const parent ); + virtual void merge( const SPIBase* const parent ); + + SPITextDecorationStyle& operator=(const SPITextDecorationStyle& rhs) { + SPIBase::operator=(rhs); + solid = rhs.solid; + isdouble = rhs.isdouble; + dotted = rhs.dotted; + dashed = rhs.dashed; + wavy = rhs.wavy; + return *this; + } + + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; + + // To do: make private + public: + bool solid : 1; + bool isdouble : 1; // cannot use "double" as it is a reserved keyword + bool dotted : 1; + bool dashed : 1; + bool wavy : 1; }; -/// Extended length type internal to SPStyle. -struct SPILengthOrNormal { - unsigned set : 1; - unsigned inherit : 1; - unsigned normal : 1; - unsigned unit : 4; - float value; - float computed; + + +// This class reads in both CSS2 and CSS3 'text-decoration' property. It passes the line, style, +// and color parts to the appropriate CSS3 long-hand classes for reading and storing values. When +// writing out data, we write all four properties, with 'text-decoration' being written out with +// the CSS2 format. This allows CSS1/CSS2 renderers to at least render lines, even if they are not +// the right style. (See http://www.w3.org/TR/css-text-decor-3/#text-decoration-property ) + +/// Text decoration type internal to SPStyle. +class SPITextDecoration: public SPIBase { + + public: + SPITextDecoration() : SPIBase( "text-decoration" ) {}; + virtual ~SPITextDecoration() {}; + virtual void read( gchar const *str ); + virtual const Glib::ustring write( guint const flags = SP_STYLE_FLAG_IFSET, + SPIBase const *const base = NULL ) const; + virtual void clear() { + SPIBase::clear(); + }; + virtual void cascade( const SPIBase* const parent ) {}; // Done in SPITextDecorationLine + virtual void merge( const SPIBase* const parent ) {}; // Done in SPITextDecorationLine + + SPITextDecoration& operator=(const SPITextDecoration& rhs) { + SPIBase::operator=(rhs); + return *this; + } + + // Use CSS2 value + virtual bool operator==(const SPIBase& rhs); + virtual bool operator!=(const SPIBase& rhs) { return !(*this == rhs); }; }; + // These are used to implement text_decoration. The values are not saved to or read from SVG file struct SPITextDecorationData { float phase_length; // length along text line,used for phase for dot/dash/wavy @@ -274,22 +867,6 @@ struct SPITextDecorationData { float line_through_position; }; -struct SPTextStyle; - -/// An SPTextStyle has a refcount, a font family, and a font name. -struct SPTextStyle { - int refcount; - - /* CSS font properties */ - SPIString font_family; - - /* Full font name, as font_factory::ConstructFontSpecification would give */ - SPIString font_specification; - - /** \todo fixme: The 'font' property is ugly, and not working (lauris) */ - SPIString font; -}; - #endif // SEEN_SP_STYLE_INTERNAL_H diff --git a/src/style-test.h b/src/style-test.h index c88c1c30a..2fe270336 100644 --- a/src/style-test.h +++ b/src/style-test.h @@ -48,6 +48,7 @@ public: // --------------------------------------------------------------- // --------------------------------------------------------------- + // Reading and writing style string void testOne() { struct TestCase { @@ -95,15 +96,33 @@ public: TestCase("overflow:visible"), // SPIEnum TestCase("overflow:auto"), // SPIEnum - // Not directly read + TestCase("color:#ff0000"), + TestCase("color:blue", "color:#0000ff"), + // TestCase("color:currentColor"), SVG 1.1 does not allow color value 'currentColor' + + // Font shorthand TestCase("font:bold 12px Arial", - "font-size:12px;font-style:normal;font-variant:normal;font-weight:bold;font-family:Arial"), - // line-height not read in - //TestCase("font:bold 12px/24px 'Times New Roman'", - // "font-size:12px;font-style:normal;font-variant:normal;font-weight:bold;line-height:24px;font-family:Times New Roman"), + "font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12px;line-height:normal;font-family:Arial"), + TestCase("font:bold 12px/24px 'Times New Roman'", + "font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12px;line-height:24px;font-family:\'\"Times New Roman\"\'"), + // From CSS 3 Fonts (examples): + TestCase("font: 12pt/14pt sans-serif", + "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:15px;line-height:14pt;font-family:sans-serif"), + TestCase("font: 80% sans-serif", + "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:80.00000119%;line-height:normal;font-family:sans-serif"), + TestCase("font: x-large/110% 'new century schoolbook', serif", + "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:x-large;line-height:110.00000238%;font-family:\'\"new century schoolbook\", serif\'"), + TestCase("font: bold italic large Palatino, serif", + "font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:large;line-height:normal;font-family:\'Palatino, serif\'"), + TestCase("font: normal small-caps 120%/120% fantasy", + "font-style:normal;font-variant:small-caps;font-weight:normal;font-stretch:normal;font-size:120.00000477%;line-height:120.00000477%;font-family:fantasy"), + TestCase("font: condensed oblique 12pt 'Helvetica Neue', serif;", + "font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:15px;line-height:normal;font-family:\'\"Helvetica Neue\", serif\'"), + TestCase("font-family:sans-serif"), // SPIString, text_private TestCase("font-family:Arial"), TestCase("font-variant:normal;font-stretch:normal;-inkscape-font-specification:Nimbus Roman No9 L Bold Italic"), + // Needs to be fixed (quotes should be around each font-family): TestCase("font-family:Georgia, 'Minion Web'","font-family:'Georgia, \"Minion Web\"'"), TestCase("font-size:12", "font-size:12px"), // SPIFontSize @@ -121,9 +140,31 @@ public: // Should be moved down TestCase("text-indent:12em"), // SPILength? TestCase("text-align:center"), // SPIEnum - TestCase("text-decoration: underline"), // SPITextDecoration - TestCase("text-decoration: underline wavy #0000ff"), // SPITextDecoration CSS3 - TestCase("text-decoration: overline double #ff0000"), + + // SPITextDecoration + // The default value for 'text-decoration-color' is 'currentColor', but + // we cannot set the default to that value yet. (We need to switch + // SPIPaint to SPIColor and then add the ability to set default.) + TestCase("text-decoration: underline", + "text-decoration: underline;text-decoration-line: underline;text-decoration-color:currentColor"), + TestCase("text-decoration: overline underline", + "text-decoration: underline overline;text-decoration-line: underline overline;text-decoration-color:currentColor"), + + TestCase("text-decoration: underline wavy #0000ff", + "text-decoration: underline;text-decoration-line: underline;text-decoration-style:wavy;text-decoration-color:#0000ff"), + TestCase("text-decoration: double overline underline #ff0000", + "text-decoration: underline overline;text-decoration-line: underline overline;text-decoration-style:double;text-decoration-color:#ff0000"), + + // SPITextDecorationLine + TestCase("text-decoration-line: underline", + "text-decoration: underline;text-decoration-line: underline"), + + // SPITextDecorationStyle + TestCase("text-decoration-style:solid"), + TestCase("text-decoration-style:dotted"), + + // SPITextDecorationColor + TestCase("text-decoration-color:#ff00ff"), // Should be moved up TestCase("line-height:24px"), // SPILengthOrNormal @@ -166,11 +207,13 @@ public: TestCase("paint-order:stroke"), // SPIPaintOrder TestCase("paint-order:normal"), TestCase("paint-order: markers stroke fill", "paint-order:markers stroke fill"), + #endif TestCase(0) }; for ( gint i = 0; cases[i].src; i++ ) { + // std::cout << "Test one: " << i << std::endl; SPStyle *style = sp_style_new(_doc); TS_ASSERT(style); if ( style ) { @@ -188,10 +231,10 @@ public: gchar *str0_set = sp_style_write_string( style, SP_STYLE_FLAG_IFSET ); //printf("<<%s>>\n", str0_set); if ( cases[i].dst ) { - //std::cout << " " << std::string(str0_set) << " " << std::string(cases[i].dst) << std::endl; + // std::cout << " " << std::string(str0_set) << " " << std::string(cases[i].dst) << std::endl; TS_ASSERT_EQUALS( std::string(str0_set), std::string(cases[i].dst) ); } else { - //std::cout << " " << std::string(str0_set) << " " << std::string(cases[i].src) << std::endl; + // std::cout << " " << std::string(str0_set) << " " << std::string(cases[i].src) << std::endl; TS_ASSERT_EQUALS( std::string(str0_set), std::string(cases[i].src) ); } @@ -201,6 +244,273 @@ public: } } + // Testing operator== + void testTwo() + { + struct TestCase { + TestCase(gchar const* src, gchar const* dst, bool match) : + src(src), dst(dst), match(match) {} + gchar const* src; + gchar const* dst; + bool match; + }; + + TestCase cases[] = { + + // SPIFloat + TestCase("stroke-miterlimit:4", "stroke-miterlimit:4", true ), + TestCase("stroke-miterlimit:4", "stroke-miterlimit:2", false), + TestCase("stroke-miterlimit:4", "", true ), // Default + + // SPIScale24 + TestCase("opacity:0.3", "opacity:0.3", true ), + TestCase("opacity:0.3", "opacity:0.6", false), + TestCase("opacity:1.0", "", true ), // Default + + // SPILength + TestCase("text-indent:3", "text-indent:3", true ), + TestCase("text-indent:6", "text-indent:3", false), + TestCase("text-indent:6px", "text-indent:3", false), + TestCase("text-indent:1px", "text-indent:12pc", false), + TestCase("text-indent:2ex", "text-indent:2ex", false), + + // SPILengthOrNormal + TestCase("letter-spacing:normal", "letter-spacing:normal", true ), + TestCase("letter-spacing:2", "letter-spacing:normal", false), + TestCase("letter-spacing:normal", "letter-spacing:2", false), + TestCase("letter-spacing:5px", "letter-spacing:5px", true ), + TestCase("letter-spacing:10px", "letter-spacing:5px", false), + TestCase("letter-spacing:10em", "letter-spacing:10em", false), + + // SPIEnum + TestCase("text-anchor:start", "text-anchor:start", true ), + TestCase("text-anchor:start", "text-anchor:middle", false), + TestCase("text-anchor:start", "", true ), // Default + TestCase("text-anchor:start", "text-anchor:junk", true ), // Bad value + + TestCase("font-weight:normal", "font-weight:400", true ), + TestCase("font-weight:bold", "font-weight:700", true ), + + + // SPIString and SPIFontString + TestCase("font-family:Arial", "font-family:Arial", true ), + TestCase("font-family:A B", "font-family:A B", true ), + TestCase("font-family:A B", "font-family:A C", false), + // Default is not set by class... value is NULL which cannot be compared + // TestCase("font-family:sans-serif", "", true ), // Default + + // SPIColor + TestCase("color:blue", "color:blue", true ), + TestCase("color:blue", "color:red", false), + TestCase("color:red", "color:#ff0000", true ), + + // SPIPaint + TestCase("fill:blue", "fill:blue", true ), + TestCase("fill:blue", "fill:red", false), + TestCase("fill:currentColor", "fill:currentColor", true ), + TestCase("fill:url(#xxx)", "fill:url(#xxx)", true ), + // Needs URL defined as in test 1 + //TestCase("fill:url(#xxx)", "fill:url(#yyy)", false), + + // SPIPaintOrder + TestCase("paint-order:markers", "paint-order:markers", true ), + TestCase("paint-order:markers", "paint-order:stroke", false), + //TestCase("paint-order:fill stroke markers", "", true ), // Default + TestCase("paint-order:normal", "paint-order:normal", true ), + //TestCase("paint-order:fill stroke markers", "paint-order:normal", true ), + + // SPIDashArray + TestCase("stroke-dasharray:0 1 2 3","stroke-dasharray:0 1 2 3",true ), + TestCase("stroke-dasharray:0 1", "stroke-dasharray:0 2", false), + + // SPIFilter + + // SPIFontSize + TestCase("font-size:12px", "font-size:12px", true ), + TestCase("font-size:12px", "font-size:24px", false), + TestCase("font-size:12ex", "font-size:24ex", false), + TestCase("font-size:medium", "font-size:medium", true ), + TestCase("font-size:medium", "font-size:large", false), + + // SPIBaselineShift + TestCase("baseline-shift:baseline", "baseline-shift:baseline", true ), + TestCase("baseline-shift:sub", "baseline-shift:sub", true ), + TestCase("baseline-shift:sub", "baseline-shift:super", false), + TestCase("baseline-shift:baseline", "baseline-shift:sub", false), + TestCase("baseline-shift:10px", "baseline-shift:10px", true ), + TestCase("baseline-shift:10px", "baseline-shift:12px", false), + + + // SPITextDecorationLine + TestCase("text-decoration-line:underline", "text-decoration-line:underline", true ), + TestCase("text-decoration-line:underline", "text-decoration-line:overline", false), + TestCase("text-decoration-line:underline overline", "text-decoration-line:underline overline", true ), + TestCase("text-decoration-line:none", "", true ), // Default + + + // SPITextDecorationStyle + TestCase("text-decoration-style:solid", "text-decoration-style:solid", true ), + TestCase("text-decoration-style:dotted", "text-decoration-style:solid", false), + TestCase("text-decoration-style:solid", "", true ), // Default + + // SPITextDecoration + TestCase("text-decoration:underline", "text-decoration:underline", true ), + TestCase("text-decoration:underline", "text-decoration:overline", false), + TestCase("text-decoration:underline overline","text-decoration:underline overline",true ), + TestCase("text-decoration:overline underline","text-decoration:underline overline",true ), + TestCase("text-decoration:none", "text-decoration-color:currentColor", true ), // Default + + + // Terminate + TestCase(0,0,0) + }; + for ( gint i = 0; cases[i].src; i++ ) { + // std::cout << "Test two: " << i << std::endl; + SPStyle *style_src = sp_style_new(_doc); + TS_ASSERT(style_src); + SPStyle *style_dst = sp_style_new(_doc); + TS_ASSERT(style_dst); + + if ( style_src && style_dst ) { + sp_style_merge_from_style_string( style_src, cases[i].src ); + sp_style_merge_from_style_string( style_dst, cases[i].dst ); + // std::cout << "Test:" << std::endl; + // std::cout << " C: |" << cases[i].src << "| |" << cases[i].dst << "|" << std::endl; + // std::cout << " S: |" << style_src->write( SP_STYLE_FLAG_IFSET, NULL ) << "| |" + // << style_dst->write( SP_STYLE_FLAG_IFSET, NULL ) << "|" <<std::endl; + TS_ASSERT( (*style_src == *style_dst) == cases[i].match ); + sp_style_unref(style_src); + sp_style_unref(style_dst); + // std::cout << "End Test\n" << std::endl; + } + } + } + + // Test of cascade + void testThree() + { + struct TestCase { + TestCase(gchar const* parent, gchar const* child, gchar const* result) : + parent(parent), child(child), result(result) {} + gchar const* parent; + gchar const* child; + gchar const* result; + }; + + TestCase cases[] = { + + // SPIFloat + TestCase("stroke-miterlimit:6", "stroke-miterlimit:2", "stroke-miterlimit:2" ), + TestCase("stroke-miterlimit:6", "", "stroke-miterlimit:6" ), + TestCase("", "stroke-miterlimit:2", "stroke-miterlimit:2" ), + + // SPIScale24 + TestCase("opacity:0.3", "opacity:0.3", "opacity:0.3" ), + TestCase("opacity:0.3", "opacity:0.6", "opacity:0.6" ), + // 'opacity' does not inherit + TestCase("opacity:0.3", "", "opacity:1.0" ), + TestCase("", "opacity:0.3", "opacity:0.3" ), + TestCase("opacity:0.5", "opacity:inherit", "opacity:0.5" ), + TestCase("", "", "opacity:1.0" ), + + // SPILength + TestCase("text-indent:3", "text-indent:3", "text-indent:3" ), + TestCase("text-indent:6", "text-indent:3", "text-indent:3" ), + TestCase("text-indent:6px", "text-indent:3", "text-indent:3" ), + TestCase("text-indent:1px", "text-indent:12pc", "text-indent:12pc" ), + // ex, em cannot be equal + //TestCase("text-indent:2ex", "text-indent:2ex", "text-indent:2ex" ), + TestCase("text-indent:3", "", "text-indent:3" ), + TestCase("text-indent:3", "text-indent:inherit", "text-indent:3" ), + + // SPILengthOrNormal + TestCase("letter-spacing:normal", "letter-spacing:normal", "letter-spacing:normal" ), + TestCase("letter-spacing:2", "letter-spacing:normal", "letter-spacing:normal" ), + TestCase("letter-spacing:normal", "letter-spacing:2", "letter-spacing:2" ), + TestCase("letter-spacing:5px", "letter-spacing:5px", "letter-spacing:5px" ), + TestCase("letter-spacing:10px", "letter-spacing:5px", "letter-spacing:5px" ), + // ex, em cannot be equal + // TestCase("letter-spacing:10em", "letter-spacing:10em", "letter-spacing:10em" ), + + // SPIEnum + TestCase("text-anchor:start", "text-anchor:start", "text-anchor:start" ), + TestCase("text-anchor:start", "text-anchor:middle", "text-anchor:middle" ), + TestCase("text-anchor:start", "", "text-anchor:start" ), + TestCase("text-anchor:start", "text-anchor:junk", "text-anchor:start" ), + TestCase("text-anchor:end", "text-anchor:inherit", "text-anchor:end" ), + + TestCase("font-weight:400", "font-weight:400", "font-weight:400" ), + TestCase("font-weight:400", "font-weight:700", "font-weight:700" ), + TestCase("font-weight:400", "font-weight:bolder", "font-weight:700" ), + TestCase("font-weight:700", "font-weight:bolder", "font-weight:900" ), + TestCase("font-weight:400", "font-weight:lighter", "font-weight:100" ), + TestCase("font-weight:200", "font-weight:lighter", "font-weight:100" ), + + TestCase("font-stretch:condensed","font-stretch:expanded", "font-stretch:expanded" ), + TestCase("font-stretch:condensed","font-stretch:wider", "font-stretch:semi-condensed" ), + + // SPIString and SPIFontString + + // SPIPaint + + // SPIPaintOrder + + // SPIDashArray + + // SPIFilter + + // SPIFontSize + + // SPIBaselineShift + + + // SPITextDecorationLine + TestCase("text-decoration-line:overline", "text-decoration-line:underline", + "text-decoration-line:underline" ), + + // SPITextDecorationStyle + + // SPITextDecoration + + // Terminate + TestCase(0,0,0) + }; + for ( gint i = 0; cases[i].parent; i++ ) { + // std::cout << "Test three: " << i << std::endl; + SPStyle *style_parent = sp_style_new(_doc); + TS_ASSERT(style_parent); + SPStyle *style_child = sp_style_new(_doc); + TS_ASSERT(style_child); + SPStyle *style_result = sp_style_new(_doc); + TS_ASSERT(style_result); + + if ( style_parent && style_child && style_result ) { + sp_style_merge_from_style_string( style_parent, cases[i].parent ); + sp_style_merge_from_style_string( style_child, cases[i].child ); + sp_style_merge_from_style_string( style_result, cases[i].result ); + // std::cout << "Test:" << std::endl; + // std::cout << " Input: "; + // std::cout << " Parent: " << cases[i].parent + // << " Child: " << cases[i].child + // << " Result: " << cases[i].result << std::endl; + // std::cout << " Write: "; + // std::cout << " Parent: " << style_parent->write( SP_STYLE_FLAG_IFSET ) + // << " Child: " << style_child->write( SP_STYLE_FLAG_IFSET ) + // << " Result: " << style_result->write( SP_STYLE_FLAG_IFSET ) << std::endl; + + //sp_style_merge_from_parent( style_child, style_parent ); + style_child->cascade( style_parent ); + + TS_ASSERT(*style_child == *style_result ); + + sp_style_unref(style_child); + sp_style_unref(style_parent); + sp_style_unref(style_result); + // std::cout << "End Test: *************\n" << std::endl; + } + } + } + }; diff --git a/src/style.cpp b/src/style.cpp index bc869b127..2d66284d4 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -14,6 +14,7 @@ * 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 */ @@ -24,6 +25,7 @@ #include <cstring> #include <string> +#include <algorithm> #include "libcroco/cr-sel-eng.h" #include "xml/croco-node-iface.h" @@ -65,925 +67,541 @@ using std::vector; struct SPStyleEnum; +int SPStyle::_count = 0; + /*######################### ## FORWARD DECLARATIONS #########################*/ -static void sp_style_clear(SPStyle *style); - -static void sp_style_merge_property(SPStyle *style, gint id, gchar const *val); - -static void sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent); -static void sp_style_merge_ifilter(SPStyle *style, SPIFilter const *parent); -static void sp_style_read_dash(SPStyle *style, gchar const *str); - -static SPTextStyle *sp_text_style_new(void); -static void sp_text_style_clear(SPTextStyle *ts); -static SPTextStyle *sp_text_style_unref(SPTextStyle *st); -static SPTextStyle *sp_text_style_duplicate_unset(SPTextStyle *st); -static guint sp_text_style_write(gchar *p, guint len, SPTextStyle const *st, guint flags = SP_STYLE_FLAG_IFSET); -static void sp_style_privatize_text(SPStyle *style); - -static void sp_style_read_ifloat(SPIFloat *val, gchar const *str); -static void sp_style_read_iscale24(SPIScale24 *val, gchar const *str); -static void sp_style_read_ienum(SPIEnum *val, gchar const *str, SPStyleEnum const *dict, bool can_explicitly_inherit); -static void sp_style_read_istring(SPIString *val, gchar const *str); -static void sp_style_read_ilength(SPILength *val, gchar const *str); -static void sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str); - -static void sp_style_read_ipaintorder(SPIPaintOrder *val, gchar const *str); - -static void sp_style_read_itextdecoration(SPITextDecorationLine *line, SPITextDecorationStyle *style, SPIPaint *color, gchar const *str); -static void sp_style_read_itextdecorationLine(SPITextDecorationLine *line, gchar const *str); -static void sp_style_read_itextdecorationStyle(SPITextDecorationStyle *style, gchar const *str); -static void sp_style_read_itextdecorationColor(SPIPaint *color, gchar const *str); - -static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document); -static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str); -static void sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str); -static void sp_style_read_ifilter(gchar const *str, SPStyle *style, SPDocument *document); - -static void sp_style_read_penum(SPIEnum *val, Inkscape::XML::Node *repr, gchar const *key, SPStyleEnum const *dict, bool can_explicitly_inherit); -static void sp_style_read_plength(SPILength *val, Inkscape::XML::Node *repr, gchar const *key); -static void sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const *key); -static void sp_style_read_pbaselineshift(SPIBaselineShift *val, Inkscape::XML::Node *repr, gchar const *key); -static void sp_style_read_pfloat(SPIFloat *val, Inkscape::XML::Node *repr, gchar const *key); - -static gint sp_style_write_ifloat(gchar *p, gint len, gchar const *key, SPIFloat const *val, SPIFloat const *base, guint flags); -static gint sp_style_write_iscale24(gchar *p, gint len, gchar const *key, SPIScale24 const *val, SPIScale24 const *base, guint flags); -static gint sp_style_write_ienum(gchar *p, gint len, gchar const *key, SPStyleEnum const *dict, SPIEnum const *val, SPIEnum const *base, guint flags); -static gint sp_style_write_istring(gchar *p, gint len, gchar const *key, SPIString const *val, SPIString const *base, guint flags); -static gint sp_style_write_ilength(gchar *p, gint len, gchar const *key, SPILength const *val, SPILength const *base, guint flags); -static gint sp_style_write_ipaint(gchar *b, gint len, gchar const *key, SPIPaint const *paint, SPIPaint const *base, guint flags); -static gint sp_style_write_ipaintorder(gchar *p, gint len, gchar const *key, SPIPaintOrder const *paint_order, SPIPaintOrder const *base, guint flags); -static gint sp_style_write_idasharray(gchar *p, gint const len, gchar const *const key, SPIDashArray const *const val, SPIDashArray const *const base, guint const flags); - -static gint sp_style_write_ifontsize(gchar *p, gint len, gchar const *key, SPIFontSize const *val, SPIFontSize const *base, guint flags); -static gint sp_style_write_ibaselineshift(gchar *p, gint len, gchar const *key, SPIBaselineShift const *val, SPIBaselineShift const *base, guint flags); -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, - SPITextDecorationLine const *const line, - SPITextDecorationStyle const *const style, - SPIPaint const *const color, - SPITextDecorationLine const *const baseLine, - SPITextDecorationStyle const *const baseStyle, - SPIPaint const *const baseColor, - guint const flags); -static gint sp_style_write_ifilter(gchar *b, gint len, gchar const *key, SPIFilter const *filter, SPIFilter const *base, guint flags); - -static void sp_style_filter_clear(SPStyle *style); - -#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));} - -#define SPS_READ_ILENGTH_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ilength((v), (s));} -#define SPS_READ_PLENGTH_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_plength((v), (r), (k));} - -#define SPS_READ_PFLOAT_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pfloat((v), (r), (k));} - -#define SPS_READ_IFONTSIZE_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ifontsize((v), (s));} -#define SPS_READ_PFONTSIZE_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pfontsize((v), (r), (k));} - -#define SPS_READ_IBASELINE_SHIFT_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ibaselineshift((v), (s));} -#define SPS_READ_PBASELINE_SHIFT_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pbaselineshift((v), (r), (k));} - -static void sp_style_merge_from_object_stylesheet(SPStyle *, SPObject const *); - -struct SPStyleEnum { - gchar const *key; - gint value; -}; - -static SPStyleEnum const enum_fill_rule[] = { - {"nonzero", SP_WIND_RULE_NONZERO}, - {"evenodd", SP_WIND_RULE_EVENODD}, - {NULL, -1} -}; - -static SPStyleEnum const enum_stroke_linecap[] = { - {"butt", SP_STROKE_LINECAP_BUTT}, - {"round", SP_STROKE_LINECAP_ROUND}, - {"square", SP_STROKE_LINECAP_SQUARE}, - {NULL, -1} -}; - -static SPStyleEnum const enum_stroke_linejoin[] = { - {"miter", SP_STROKE_LINEJOIN_MITER}, - {"round", SP_STROKE_LINEJOIN_ROUND}, - {"bevel", SP_STROKE_LINEJOIN_BEVEL}, - {NULL, -1} -}; - -static SPStyleEnum const enum_font_style[] = { - {"normal", SP_CSS_FONT_STYLE_NORMAL}, - {"italic", SP_CSS_FONT_STYLE_ITALIC}, - {"oblique", SP_CSS_FONT_STYLE_OBLIQUE}, - {NULL, -1} -}; - -static SPStyleEnum const enum_font_size[] = { - {"xx-small", SP_CSS_FONT_SIZE_XX_SMALL}, - {"x-small", SP_CSS_FONT_SIZE_X_SMALL}, - {"small", SP_CSS_FONT_SIZE_SMALL}, - {"medium", SP_CSS_FONT_SIZE_MEDIUM}, - {"large", SP_CSS_FONT_SIZE_LARGE}, - {"x-large", SP_CSS_FONT_SIZE_X_LARGE}, - {"xx-large", SP_CSS_FONT_SIZE_XX_LARGE}, - {"smaller", SP_CSS_FONT_SIZE_SMALLER}, - {"larger", SP_CSS_FONT_SIZE_LARGER}, - {NULL, -1} -}; - -static SPStyleEnum const enum_font_variant[] = { - {"normal", SP_CSS_FONT_VARIANT_NORMAL}, - {"small-caps", SP_CSS_FONT_VARIANT_SMALL_CAPS}, - {NULL, -1} -}; - -static SPStyleEnum const enum_font_weight[] = { - {"100", SP_CSS_FONT_WEIGHT_100}, - {"200", SP_CSS_FONT_WEIGHT_200}, - {"300", SP_CSS_FONT_WEIGHT_300}, - {"400", SP_CSS_FONT_WEIGHT_400}, - {"500", SP_CSS_FONT_WEIGHT_500}, - {"600", SP_CSS_FONT_WEIGHT_600}, - {"700", SP_CSS_FONT_WEIGHT_700}, - {"800", SP_CSS_FONT_WEIGHT_800}, - {"900", SP_CSS_FONT_WEIGHT_900}, - {"normal", SP_CSS_FONT_WEIGHT_NORMAL}, - {"bold", SP_CSS_FONT_WEIGHT_BOLD}, - {"lighter", SP_CSS_FONT_WEIGHT_LIGHTER}, - {"bolder", SP_CSS_FONT_WEIGHT_BOLDER}, - {NULL, -1} -}; - -static SPStyleEnum const enum_font_stretch[] = { - {"ultra-condensed", SP_CSS_FONT_STRETCH_ULTRA_CONDENSED}, - {"extra-condensed", SP_CSS_FONT_STRETCH_EXTRA_CONDENSED}, - {"condensed", SP_CSS_FONT_STRETCH_CONDENSED}, - {"semi-condensed", SP_CSS_FONT_STRETCH_SEMI_CONDENSED}, - {"normal", SP_CSS_FONT_STRETCH_NORMAL}, - {"semi-expanded", SP_CSS_FONT_STRETCH_SEMI_EXPANDED}, - {"expanded", SP_CSS_FONT_STRETCH_EXPANDED}, - {"extra-expanded", SP_CSS_FONT_STRETCH_EXTRA_EXPANDED}, - {"ultra-expanded", SP_CSS_FONT_STRETCH_ULTRA_EXPANDED}, - {"narrower", SP_CSS_FONT_STRETCH_NARROWER}, - {"wider", SP_CSS_FONT_STRETCH_WIDER}, - {NULL, -1} -}; - -static SPStyleEnum const enum_text_align[] = { - {"start", SP_CSS_TEXT_ALIGN_START}, - {"end", SP_CSS_TEXT_ALIGN_END}, - {"left", SP_CSS_TEXT_ALIGN_LEFT}, - {"right", SP_CSS_TEXT_ALIGN_RIGHT}, - {"center", SP_CSS_TEXT_ALIGN_CENTER}, - {"justify", SP_CSS_TEXT_ALIGN_JUSTIFY}, - {NULL, -1} -}; - -static SPStyleEnum const enum_text_transform[] = { - {"capitalize", SP_CSS_TEXT_TRANSFORM_CAPITALIZE}, - {"uppercase", SP_CSS_TEXT_TRANSFORM_UPPERCASE}, - {"lowercase", SP_CSS_TEXT_TRANSFORM_LOWERCASE}, - {"none", SP_CSS_TEXT_TRANSFORM_NONE}, - {NULL, -1} -}; - -static SPStyleEnum const enum_text_anchor[] = { - {"start", SP_CSS_TEXT_ANCHOR_START}, - {"middle", SP_CSS_TEXT_ANCHOR_MIDDLE}, - {"end", SP_CSS_TEXT_ANCHOR_END}, - {NULL, -1} -}; - -static SPStyleEnum const enum_direction[] = { - {"ltr", SP_CSS_DIRECTION_LTR}, - {"rtl", SP_CSS_DIRECTION_RTL}, - {NULL, -1} -}; - -static SPStyleEnum const enum_block_progression[] = { - {"tb", SP_CSS_BLOCK_PROGRESSION_TB}, - {"rl", SP_CSS_BLOCK_PROGRESSION_RL}, - {"lr", SP_CSS_BLOCK_PROGRESSION_LR}, - {NULL, -1} -}; - -static SPStyleEnum const enum_writing_mode[] = { - /* Note that using the same enumerator for lr as lr-tb means we write as lr-tb even if the - * input file said lr. We prefer writing lr-tb on the grounds that the spec says the initial - * value is lr-tb rather than lr. - * - * ECMA scripts may be surprised to find tb-rl in DOM if they set the attribute to rl, so - * sharing enumerators for different strings may be a bug (once we support ecma script). - */ - {"lr-tb", SP_CSS_WRITING_MODE_LR_TB}, - {"rl-tb", SP_CSS_WRITING_MODE_RL_TB}, - {"tb-rl", SP_CSS_WRITING_MODE_TB_RL}, - {"lr", SP_CSS_WRITING_MODE_LR_TB}, - {"rl", SP_CSS_WRITING_MODE_RL_TB}, - {"tb", SP_CSS_WRITING_MODE_TB_RL}, - {NULL, -1} -}; - -static SPStyleEnum const enum_baseline_shift[] = { - {"baseline", SP_CSS_BASELINE_SHIFT_BASELINE}, - {"sub", SP_CSS_BASELINE_SHIFT_SUB}, - {"super", SP_CSS_BASELINE_SHIFT_SUPER}, - {NULL, -1} -}; - -static SPStyleEnum const enum_visibility[] = { - {"hidden", SP_CSS_VISIBILITY_HIDDEN}, - {"collapse", SP_CSS_VISIBILITY_COLLAPSE}, - {"visible", SP_CSS_VISIBILITY_VISIBLE}, - {NULL, -1} -}; - -static SPStyleEnum const enum_overflow[] = { - {"visible", SP_CSS_OVERFLOW_VISIBLE}, - {"hidden", SP_CSS_OVERFLOW_HIDDEN}, - {"scroll", SP_CSS_OVERFLOW_SCROLL}, - {"auto", SP_CSS_OVERFLOW_AUTO}, - {NULL, -1} -}; - -// CSS Compositing and Blending Level 1 -static SPStyleEnum const enum_isolation[] = { - {"auto", SP_CSS_ISOLATION_AUTO}, - {"isolate", SP_CSS_ISOLATION_ISOLATE}, - {NULL, -1} -}; - -static SPStyleEnum const enum_blend_mode[] = { - {"normal", SP_CSS_BLEND_NORMAL}, - {"multiply", SP_CSS_BLEND_MULTIPLY}, - {"screen", SP_CSS_BLEND_SCREEN}, - {"darken", SP_CSS_BLEND_DARKEN}, - {"lighten", SP_CSS_BLEND_LIGHTEN}, - {"overlay", SP_CSS_BLEND_OVERLAY}, - {"color-dodge", SP_CSS_BLEND_COLORDODGE}, - {"color-burn", SP_CSS_BLEND_COLORBURN}, - {"hard-light", SP_CSS_BLEND_HARDLIGHT}, - {"soft-light", SP_CSS_BLEND_SOFTLIGHT}, - {"difference", SP_CSS_BLEND_DIFFERENCE}, - {"exclusion", SP_CSS_BLEND_EXCLUSION}, - {"hue", SP_CSS_BLEND_HUE}, - {"saturation", SP_CSS_BLEND_SATURATION}, - {"color", SP_CSS_BLEND_COLOR}, - {"luminosity", SP_CSS_BLEND_LUMINOSITY}, - {NULL, -1} -}; - -static SPStyleEnum const enum_display[] = { - {"none", SP_CSS_DISPLAY_NONE}, - {"inline", SP_CSS_DISPLAY_INLINE}, - {"block", SP_CSS_DISPLAY_BLOCK}, - {"list-item", SP_CSS_DISPLAY_LIST_ITEM}, - {"run-in", SP_CSS_DISPLAY_RUN_IN}, - {"compact", SP_CSS_DISPLAY_COMPACT}, - {"marker", SP_CSS_DISPLAY_MARKER}, - {"table", SP_CSS_DISPLAY_TABLE}, - {"inline-table", SP_CSS_DISPLAY_INLINE_TABLE}, - {"table-row-group", SP_CSS_DISPLAY_TABLE_ROW_GROUP}, - {"table-header-group", SP_CSS_DISPLAY_TABLE_HEADER_GROUP}, - {"table-footer-group", SP_CSS_DISPLAY_TABLE_FOOTER_GROUP}, - {"table-row", SP_CSS_DISPLAY_TABLE_ROW}, - {"table-column-group", SP_CSS_DISPLAY_TABLE_COLUMN_GROUP}, - {"table-column", SP_CSS_DISPLAY_TABLE_COLUMN}, - {"table-cell", SP_CSS_DISPLAY_TABLE_CELL}, - {"table-caption", SP_CSS_DISPLAY_TABLE_CAPTION}, - {NULL, -1} -}; - -static SPStyleEnum const enum_shape_rendering[] = { - {"auto", SP_CSS_SHAPE_RENDERING_AUTO}, - {"optimizeSpeed", SP_CSS_SHAPE_RENDERING_OPTIMIZESPEED}, - {"crispEdges", SP_CSS_SHAPE_RENDERING_CRISPEDGES}, - {"geometricPrecision", SP_CSS_SHAPE_RENDERING_GEOMETRICPRECISION}, - {NULL, -1} -}; - -static SPStyleEnum const enum_color_rendering[] = { - {"auto", SP_CSS_COLOR_RENDERING_AUTO}, - {"optimizeSpeed", SP_CSS_COLOR_RENDERING_OPTIMIZESPEED}, - {"optimizeQuality", SP_CSS_COLOR_RENDERING_OPTIMIZEQUALITY}, - {NULL, -1} -}; - -static SPStyleEnum const enum_image_rendering[] = { - {"auto", SP_CSS_IMAGE_RENDERING_AUTO}, - {"optimizeSpeed", SP_CSS_IMAGE_RENDERING_OPTIMIZESPEED}, - {"optimizeQuality", SP_CSS_IMAGE_RENDERING_OPTIMIZEQUALITY}, - {"-inkscape-crisp-edges", SP_CSS_IMAGE_RENDERING_CRISPEDGES}, - {"-inkscape-pixelated", SP_CSS_IMAGE_RENDERING_PIXELATED}, - {NULL, -1} -}; - -static SPStyleEnum const enum_text_rendering[] = { - {"auto", SP_CSS_TEXT_RENDERING_AUTO}, - {"optimizeSpeed", SP_CSS_TEXT_RENDERING_OPTIMIZESPEED}, - {"optimizeLegibility", SP_CSS_TEXT_RENDERING_OPTIMIZELEGIBILITY}, - {"geometricPrecision", SP_CSS_TEXT_RENDERING_GEOMETRICPRECISION}, - {NULL, -1} -}; - -static SPStyleEnum const enum_enable_background[] = { - {"accumulate", SP_CSS_BACKGROUND_ACCUMULATE}, - {"new", SP_CSS_BACKGROUND_NEW}, - {NULL, -1} -}; - -static SPStyleEnum const enum_clip_rule[] = { - {"nonzero", SP_WIND_RULE_NONZERO}, - {"evenodd", SP_WIND_RULE_EVENODD}, - {NULL, -1} -}; - -static SPStyleEnum const enum_color_interpolation[] = { - {"auto", SP_CSS_COLOR_INTERPOLATION_AUTO}, - {"sRGB", SP_CSS_COLOR_INTERPOLATION_SRGB}, - {"linearRGB", SP_CSS_COLOR_INTERPOLATION_LINEARRGB}, - {NULL, -1} -}; +void sp_style_filter_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style); +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); + +static void sp_style_object_release(SPObject *object, SPStyle *style); +static CRSelEng *sp_repr_sel_eng(); + + +//SPPropMap SPStyle::_propmap; + +// C++11 allows one constructor to call another... might be useful. The original C code +// had separate calls to create SPStyle, one with only SPDocument and the other with only +// SPObject as parameters. +SPStyle::SPStyle(SPDocument *document_in, SPObject *object_in) : + + // Unimplemented SVG 1.1: alignment-baseline, clip, clip-path, color-profile, cursor, + // dominant-baseline, flood-color, flood-opacity, font-size-adjust, + // glyph-orientation-horizontal, glyph-orientation-vertical, kerning, lighting-color, + // pointer-events, stop-color, stop-opacity, unicode-bidi + + // For enums: property( name, enumeration, default value , inherits = true ); + // For scale24: property( name, default value = 0, inherits = true ); + + // 'font', 'font-size', and 'font-family' must come first as other properties depend on them + // for calculated values (through 'em' and 'ex'). ('ex' is currently not read.) + // The following properties can depend on 'em' and 'ex': + // baseline-shift, kerning, letter-spacing, stroke-dash-offset, stroke-width, word-spacing, + // Non-SVG 1.1: text-indent, line-spacing + + // Hidden in SPIFontStyle: (to be refactored) + // font-family + // font-specification + + // Font related properties and 'font' shorthand + font_style( "font-style", enum_font_style, SP_CSS_FONT_STYLE_NORMAL ), + font_variant( "font-variant", enum_font_variant, SP_CSS_FONT_VARIANT_NORMAL ), + font_weight( "font-weight", enum_font_weight, SP_CSS_FONT_WEIGHT_NORMAL, SP_CSS_FONT_WEIGHT_400 ), + font_stretch( "font-stretch", enum_font_stretch, SP_CSS_FONT_STRETCH_NORMAL ), + font_size(), + line_height( "line-height", 1.0 ), // SPILengthOrNormal + font_family( "font-family" ), // SPIString + font(), // SPIFont + font_specification( "-inkscape-font-specification" ), // SPIString + + // Text related properties + text_indent( "text-indent", 0.0 ), // SPILength + text_align( "text-align", enum_text_align, SP_CSS_TEXT_ALIGN_START ), + text_decoration(), + text_decoration_line(), + text_decoration_style(), + text_decoration_color( "text-decoration-color" ), // SPIColor + + letter_spacing( "letter-spacing", 0.0 ), // SPILengthOrNormal + word_spacing( "word-spacing", 0.0 ), // SPILengthOrNormal + text_transform( "text-transform", enum_text_transform, SP_CSS_TEXT_TRANSFORM_NONE ), + + direction( "direction", enum_direction, SP_CSS_DIRECTION_LTR ), + block_progression("block-progression", enum_block_progression, SP_CSS_BLOCK_PROGRESSION_TB), + writing_mode( "writing-mode", enum_writing_mode, SP_CSS_WRITING_MODE_LR_TB ), + baseline_shift(), + text_anchor( "text-anchor", enum_text_anchor, SP_CSS_TEXT_ANCHOR_START ), + + // General visual properties + clip_rule( "clip-rule", enum_clip_rule, SP_WIND_RULE_NONZERO ), + display( "display", enum_display, SP_CSS_DISPLAY_INLINE, false ), + overflow( "overflow", enum_overflow, SP_CSS_OVERFLOW_VISIBLE, false ), + visibility( "visibility", enum_visibility, SP_CSS_VISIBILITY_VISIBLE ), + opacity( "opacity", SP_SCALE24_MAX, false ), + + isolation( "isolation", enum_isolation, SP_CSS_ISOLATION_AUTO ), + blend_mode( "blend_mode", enum_blend_mode, SP_CSS_BLEND_NORMAL ), + + paint_order(), // SPIPaintOrder + + // Color properties + color( "color" ), // SPIColor + color_interpolation( "color-interpolation", enum_color_interpolation, SP_CSS_COLOR_INTERPOLATION_SRGB), + color_interpolation_filters("color-interpolation-filters", enum_color_interpolation, SP_CSS_COLOR_INTERPOLATION_LINEARRGB), + + // Fill properties + fill( "fill" ), // SPIPaint + fill_opacity( "fill-opacity", SP_SCALE24_MAX ), + fill_rule( "fill-rule", enum_fill_rule, SP_WIND_RULE_NONZERO ), + + // Stroke properites + stroke( "stroke" ), // SPIPaint + stroke_width( "stroke-width", 1.0 ), // SPILength + stroke_linecap( "stroke-linecap", enum_stroke_linecap, SP_STROKE_LINECAP_BUTT ), + stroke_linejoin( "stroke-linejoin", enum_stroke_linejoin, SP_STROKE_LINEJOIN_MITER ), + stroke_miterlimit("stroke-miterlimit", 4 ), // SPIFloat (only use of float!) + stroke_dasharray(), // SPIDashArray + stroke_dashoffset("stroke-dashoffset", 0.0 ), // SPILength for now + + stroke_opacity( "stroke-opacity", SP_SCALE24_MAX ), + + marker( "marker" ), // SPIString + marker_start( "marker-start" ), // SPIString + marker_mid( "marker-mid" ), // SPIString + marker_end( "marker-end" ), // SPIString + + // Filter properties + filter(), + filter_blend_mode("filter-blend-mode", enum_blend_mode, SP_CSS_BLEND_NORMAL), + filter_gaussianBlur_deviation( "filter-gaussianBlur-deviation", 0.0 ), // SPILength + enable_background("enable-background", enum_enable_background, SP_CSS_BACKGROUND_ACCUMULATE, false), + + // Rendering hint properties + color_rendering( "color-rendering", enum_color_rendering, SP_CSS_COLOR_RENDERING_AUTO), + image_rendering( "image-rendering", enum_image_rendering, SP_CSS_IMAGE_RENDERING_AUTO), + shape_rendering( "shape-rendering", enum_shape_rendering, SP_CSS_SHAPE_RENDERING_AUTO), + text_rendering( "text-rendering", enum_text_rendering, SP_CSS_TEXT_RENDERING_AUTO ) + +{ + // std::cout << "SPStyle::SPStyle( SPDocument ): Entrance: (" << _count << ")" << std::endl; + // std::cout << " Document: " << (document_in?"present":"null") << std::endl; + // std::cout << " Object: " + // << (object_in?(object_in->getId()?object_in->getId():"id null"):"object null") << std::endl; + + // static bool first = true; + // if( first ) { + // std::cout << "Size of SPStyle: " << sizeof(SPStyle) << std::endl; + // std::cout << " SPIBase: " << sizeof(SPIBase) << std::endl; + // std::cout << " SPIFloat: " << sizeof(SPIFloat) << std::endl; + // std::cout << " SPIScale24: " << sizeof(SPIScale24) << std::endl; + // std::cout << " SPILength: " << sizeof(SPILength) << std::endl; + // std::cout << " SPILengthOrNormal: " << sizeof(SPILengthOrNormal) << std::endl; + // std::cout << " SPIColor: " << sizeof(SPIColor) << std::endl; + // std::cout << " SPIPaint: " << sizeof(SPIPaint) << std::endl; + // std::cout << " SPITextDecorationLine" << sizeof(SPITextDecorationLine) << std::endl; + // std::cout << " Glib::ustring:" << sizeof(Glib::ustring) << std::endl; + // std::cout << " SPColor: " << sizeof(SPColor) << std::endl; + // first = false; + // } + + ++_count; // Poor man's memory leak detector + + _refcount = 1; + + cloned = false; + + object = object_in; + if( object ) { + g_assert( SP_IS_OBJECT(object) ); + document = object->document; + release_connection = + object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_style_object_release), this)); + + cloned = object->cloned; -/** - * Release callback. - */ -static void -sp_style_object_release(SPObject *object, SPStyle *style) -{ - (void)object; // TODO - style->object = NULL; -} + } else { + document = document_in; + } + + new (&release_connection) sigc::connection(); + new (&filter_modified_connection) sigc::connection(); + new (&fill_ps_modified_connection) sigc::connection(); + new (&stroke_ps_modified_connection) sigc::connection(); + + // 'font' shorthand requires access to included properties. + font.setStylePointer( this ); + + // Properties that depend on 'font-size' for calculating lengths. + baseline_shift.setStylePointer( this ); + text_indent.setStylePointer( this ); + line_height.setStylePointer( this ); + letter_spacing.setStylePointer( this ); + word_spacing.setStylePointer( this ); + stroke_width.setStylePointer( this ); + stroke_dashoffset.setStylePointer( this ); + + // Properties that depend on 'color' + text_decoration_color.setStylePointer( this ); + fill.setStylePointer( this ); + stroke.setStylePointer( this ); + // color.setStylePointer( this ); // Doen't need reference to self + + // 'text_decoration' shorthand requires access to included properties. + text_decoration.setStylePointer( this ); + + // SPIPaint, SPIFilter needs access to 'this' (SPStyle) + // for setting up signals... 'fill', 'stroke' already done + filter.setStylePointer( this ); + + // Used to iterate over markers + marker_ptrs[SP_MARKER_LOC] = ▮ + marker_ptrs[SP_MARKER_LOC_START] = &marker_start; + marker_ptrs[SP_MARKER_LOC_MID] = &marker_mid; + marker_ptrs[SP_MARKER_LOC_END] = &marker_end; + + + // This might be too resource hungary... but for now it possible to loop over properties + + // 'color' must be before 'fill', 'stroke', 'text-decoration-color', ... + _properties.push_back( &color ); + + // 'font-size'/'font' must be before properties that need to know em, ex size (SPILength, + // SPILenghtOrNormal) + _properties.push_back( &font_style ); + _properties.push_back( &font_variant ); + _properties.push_back( &font_weight ); + _properties.push_back( &font_stretch ); + _properties.push_back( &font_size ); + _properties.push_back( &line_height ); + _properties.push_back( &font_family ); + _properties.push_back( &font ); + _properties.push_back( &font_specification ); + + _properties.push_back( &text_indent ); + _properties.push_back( &text_align ); + + _properties.push_back( &text_decoration ); + _properties.push_back( &text_decoration_line ); + _properties.push_back( &text_decoration_style ); + _properties.push_back( &text_decoration_color ); + + _properties.push_back( &letter_spacing ); + _properties.push_back( &word_spacing ); + _properties.push_back( &text_transform ); + + _properties.push_back( &direction ); + _properties.push_back( &block_progression ); + _properties.push_back( &writing_mode ); + _properties.push_back( &baseline_shift ); + _properties.push_back( &text_anchor ); + + _properties.push_back( &clip_rule ); + _properties.push_back( &display ); + _properties.push_back( &overflow ); + _properties.push_back( &visibility ); + _properties.push_back( &opacity ); + + _properties.push_back( &isolation ); + _properties.push_back( &blend_mode ); + + _properties.push_back( &color_interpolation ); + _properties.push_back( &color_interpolation_filters ); + + _properties.push_back( &fill ); + _properties.push_back( &fill_opacity ); + _properties.push_back( &fill_rule ); + + _properties.push_back( &stroke ); + _properties.push_back( &stroke_width ); + _properties.push_back( &stroke_linecap ); + _properties.push_back( &stroke_linejoin ); + _properties.push_back( &stroke_miterlimit ); + _properties.push_back( &stroke_dasharray ); + _properties.push_back( &stroke_dashoffset ); + _properties.push_back( &stroke_opacity ); + + _properties.push_back( &marker ); + _properties.push_back( &marker_start ); + _properties.push_back( &marker_mid ); + _properties.push_back( &marker_end ); + + _properties.push_back( &paint_order ); + + _properties.push_back( &filter ); + _properties.push_back( &filter_blend_mode ); + _properties.push_back( &filter_gaussianBlur_deviation ); + + _properties.push_back( &color_rendering ); + _properties.push_back( &image_rendering ); + _properties.push_back( &shape_rendering ); + _properties.push_back( &text_rendering ); + + _properties.push_back( &enable_background ); -/** - * Emit style modified signal on style's object if the filter changed. - */ -static void -sp_style_filter_ref_modified(SPObject *obj, guint flags, SPStyle *style) -{ - (void)flags; // TODO - SPFilter *filter=static_cast<SPFilter *>(obj); - if (style->getFilter() == filter) - { - if (style->object) { - style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); - } - } -} + // MAP ------------------------------------------- -/** - * Gets called when the filter is (re)attached to the style - */ -static void -sp_style_filter_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) -{ - if (old_ref) { - style->filter_modified_connection.disconnect(); - } - if ( SP_IS_FILTER(ref)) - { - style->filter_modified_connection = - ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_filter_ref_modified), style)); - } + // if( _propmap.size() == 0 ) { - sp_style_filter_ref_modified(ref, 0, style); -} + // // 'color' must be before 'fill', 'stroke', 'text-decoration-color', ... + // _propmap.insert( std::make_pair( color.name, reinterpret_cast<SPIBasePtr>(&SPStyle::color ) ) ); -/** - * Emit style modified signal on style's object if server is style's fill - * or stroke paint server. - */ -static void -sp_style_paint_server_ref_modified(SPObject *obj, guint flags, SPStyle *style) -{ - (void)flags; // TODO - SPPaintServer *server = static_cast<SPPaintServer *>(obj); + // // 'font-size' must be before properties that need to know em, ex size (SPILength, SPILenghtOrNormal) + // _propmap.insert( std::make_pair( font_style.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_style ) ) ); + // _propmap.insert( std::make_pair( font_variant.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_variant ) ) ); + // _propmap.insert( std::make_pair( font_weight.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_weight ) ) ); + // _propmap.insert( std::make_pair( font_stretch.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_stretch ) ) ); + // _propmap.insert( std::make_pair( font_size.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_size ) ) ); + // _propmap.insert( std::make_pair( line_height.name, reinterpret_cast<SPIBasePtr>(&SPStyle::line_height ) ) ); + // _propmap.insert( std::make_pair( font_family.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_family ) ) ); + // _propmap.insert( std::make_pair( font.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font ) ) ); + // _propmap.insert( std::make_pair( font_specification.name, reinterpret_cast<SPIBasePtr>(&SPStyle::font_specification ) ) ); - if ((style->fill.isPaintserver()) - && style->getFillPaintServer() == server) - { - if (style->object) { - /** \todo - * 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 - * flag is only available downstreams. - */ - style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); - } - } else if ((style->stroke.isPaintserver()) - && style->getStrokePaintServer() == server) - { - if (style->object) { - /// \todo fixme: - style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); - } - } else if (server) { - g_assert_not_reached(); - } -} + // _propmap.insert( std::make_pair( text_indent.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_indent ) ) ); + // _propmap.insert( std::make_pair( text_align.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_align ) ) ); -/** - * Gets called when the paintserver is (re)attached to the style - */ -static void -sp_style_fill_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) -{ - if (old_ref) { - style->fill_ps_modified_connection.disconnect(); - } - if (SP_IS_PAINT_SERVER(ref)) { - style->fill_ps_modified_connection = - ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); - } + // _propmap.insert( std::make_pair( text_decoration.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_decoration ) ) ); + // _propmap.insert( std::make_pair( text_decoration_line.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_decoration_line ) ) ); + // _propmap.insert( std::make_pair( text_decoration_style.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_decoration_style ) ) ); + // _propmap.insert( std::make_pair( text_decoration_color.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_decoration_color ) ) ); - sp_style_paint_server_ref_modified(ref, 0, style); -} + // _propmap.insert( std::make_pair( letter_spacing.name, reinterpret_cast<SPIBasePtr>(&SPStyle::letter_spacing ) ) ); + // _propmap.insert( std::make_pair( word_spacing.name, reinterpret_cast<SPIBasePtr>(&SPStyle::word_spacing ) ) ); + // _propmap.insert( std::make_pair( text_transform.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_transform ) ) ); -/** - * Gets called when the paintserver is (re)attached to the style - */ -static void -sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) -{ - if (old_ref) { - style->stroke_ps_modified_connection.disconnect(); - } - if (SP_IS_PAINT_SERVER(ref)) { - style->stroke_ps_modified_connection = - ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); - } + // _propmap.insert( std::make_pair( direction.name, reinterpret_cast<SPIBasePtr>(&SPStyle::direction ) ) ); + // _propmap.insert( std::make_pair( block_progression.name, reinterpret_cast<SPIBasePtr>(&SPStyle::block_progression ) ) ); + // _propmap.insert( std::make_pair( writing_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::writing_mode ) ) ); + // _propmap.insert( std::make_pair( baseline_shift.name, reinterpret_cast<SPIBasePtr>(&SPStyle::baseline_shift ) ) ); + // _propmap.insert( std::make_pair( text_anchor.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_anchor ) ) ); - sp_style_paint_server_ref_modified(ref, 0, style); -} + // _propmap.insert( std::make_pair( clip_rule.name, reinterpret_cast<SPIBasePtr>(&SPStyle::clip_rule ) ) ); + // _propmap.insert( std::make_pair( display.name, reinterpret_cast<SPIBasePtr>(&SPStyle::display ) ) ); + // _propmap.insert( std::make_pair( overflow.name, reinterpret_cast<SPIBasePtr>(&SPStyle::overflow ) ) ); + // _propmap.insert( std::make_pair( visibility.name, reinterpret_cast<SPIBasePtr>(&SPStyle::visibility ) ) ); + // _propmap.insert( std::make_pair( opacity.name, reinterpret_cast<SPIBasePtr>(&SPStyle::opacity ) ) ); -/** - * Returns a new SPStyle object with settings as per sp_style_clear(). - */ -SPStyle * -sp_style_new(SPDocument *document) -{ - SPStyle *const style = g_new0(SPStyle, 1); + // _propmap.insert( std::make_pair( isolation.name, reinterpret_cast<SPIBasePtr>(&SPStyle::isolation ) ) ); + // _propmap.insert( std::make_pair( blend_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::blend_mode ) ) ); - style->refcount = 1; - style->object = NULL; - style->document = document; - style->text = sp_text_style_new(); - style->text_private = TRUE; + // _propmap.insert( std::make_pair( color_interpolation.name, reinterpret_cast<SPIBasePtr>(&SPStyle::color_interpolation ) ) ); + // _propmap.insert( std::make_pair( color_interpolation_filters.name, reinterpret_cast<SPIBasePtr>(&SPStyle::color_interpolation_filters ) ) ); - sp_style_clear(style); + // _propmap.insert( std::make_pair( fill.name, reinterpret_cast<SPIBasePtr>(&SPStyle::fill ) ) ); + // _propmap.insert( std::make_pair( fill_opacity.name, reinterpret_cast<SPIBasePtr>(&SPStyle::fill_opacity ) ) ); + // _propmap.insert( std::make_pair( fill_rule.name, reinterpret_cast<SPIBasePtr>(&SPStyle::fill_rule ) ) ); - style->cloned = false; + // _propmap.insert( std::make_pair( stroke.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke ) ) ); + // _propmap.insert( std::make_pair( stroke_width.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke_width ) ) ); + // _propmap.insert( std::make_pair( stroke_linecap.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke_linecap ) ) ); + // _propmap.insert( std::make_pair( stroke_linejoin.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke_linejoin ) ) ); + // _propmap.insert( std::make_pair( stroke_miterlimit.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke_miterlimit ) ) ); + // _propmap.insert( std::make_pair( stroke_dasharray.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke_dasharray ) ) ); + // _propmap.insert( std::make_pair( stroke_dashoffset.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke_dashoffset ) ) ); + // _propmap.insert( std::make_pair( stroke_opacity.name, reinterpret_cast<SPIBasePtr>(&SPStyle::stroke_opacity ) ) ); - new (&style->release_connection) sigc::connection(); - new (&style->filter_modified_connection) sigc::connection(); - new (&style->fill_ps_modified_connection) sigc::connection(); - new (&style->stroke_ps_modified_connection) sigc::connection(); + // _propmap.insert( std::make_pair( marker.name, reinterpret_cast<SPIBasePtr>(&SPStyle::marker ) ) ); + // _propmap.insert( std::make_pair( marker_start.name, reinterpret_cast<SPIBasePtr>(&SPStyle::marker_start ) ) ); + // _propmap.insert( std::make_pair( marker_mid.name, reinterpret_cast<SPIBasePtr>(&SPStyle::marker_mid ) ) ); + // _propmap.insert( std::make_pair( marker_end.name, reinterpret_cast<SPIBasePtr>(&SPStyle::marker_end ) ) ); - return style; + // _propmap.insert( std::make_pair( paint_order.name, reinterpret_cast<SPIBasePtr>(&SPStyle::paint_order ) ) ); + + // _propmap.insert( std::make_pair( filter.name, reinterpret_cast<SPIBasePtr>(&SPStyle::filter ) ) ); + // _propmap.insert( std::make_pair( filter_blend_mode.name, reinterpret_cast<SPIBasePtr>(&SPStyle::filter_blend_mode ) ) ); + // _propmap.insert( std::make_pair( filter_gaussianBlur_deviation.name, reinterpret_cast<SPIBasePtr>(&SPStyle::filter_gaussianBlur_deviation ) ) ); + + // _propmap.insert( std::make_pair( color_rendering.name, reinterpret_cast<SPIBasePtr>(&SPStyle::color_rendering ) ) ); + // _propmap.insert( std::make_pair( image_rendering.name, reinterpret_cast<SPIBasePtr>(&SPStyle::image_rendering ) ) ); + // _propmap.insert( std::make_pair( shape_rendering.name, reinterpret_cast<SPIBasePtr>(&SPStyle::shape_rendering ) ) ); + // _propmap.insert( std::make_pair( text_rendering.name, reinterpret_cast<SPIBasePtr>(&SPStyle::text_rendering ) ) ); + + // _propmap.insert( std::make_pair( enable_background.name, reinterpret_cast<SPIBasePtr>(&SPStyle::enable_background ) ) ); + + // } } +SPStyle::~SPStyle() { -/** - * Creates a new SPStyle object, and attaches it to the specified SPObject. - */ -SPStyle * -sp_style_new_from_object(SPObject *object) -{ - g_return_val_if_fail(object != NULL, NULL); - g_return_val_if_fail(SP_IS_OBJECT(object), NULL); + // std::cout << "SPStyle::~SPStyle" << std::endl; + --_count; // Poor man's memory leak detector. - SPStyle *style = sp_style_new( object->document ); - style->object = object; - style->release_connection = object->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_style_object_release), style)); + // Remove connections + release_connection.disconnect(); + release_connection.~connection(); - if (object->cloned) { - style->cloned = true; + // The following shoud be moved into SPIPaint and SPIFilter + if (fill.value.href) { + fill_ps_modified_connection.disconnect(); } - return style; -} + if (stroke.value.href) { + stroke_ps_modified_connection.disconnect(); + } + if (filter.href) { + filter_modified_connection.disconnect(); + } -/** - * Increase refcount of style. - */ -SPStyle * -sp_style_ref(SPStyle *style) -{ - g_return_val_if_fail(style != NULL, NULL); - g_return_val_if_fail(style->refcount > 0, NULL); + filter_modified_connection.~connection(); + fill_ps_modified_connection.~connection(); + stroke_ps_modified_connection.~connection(); - style->refcount += 1; + _properties.clear(); + //_propmap.clear(); - return style; + // std::cout << "SPStyle::~SPstyle(): Exit\n" << std::endl; } +// Used in SPStyle::clear() +void clear_property( SPIBase* p ) { + p->clear(); +} -/** - * Decrease refcount of style with possible destruction. - */ -SPStyle * -sp_style_unref(SPStyle *style) -{ - g_return_val_if_fail(style != NULL, NULL); - g_return_val_if_fail(style->refcount > 0, NULL); - - style->refcount -= 1; - - if (style->refcount < 1) { - style->release_connection.disconnect(); - style->release_connection.~connection(); - if (style->text) sp_text_style_unref(style->text); - - if (style->fill.value.href) { - style->fill_ps_modified_connection.disconnect(); - delete style->fill.value.href; - style->fill.value.href = NULL; - } - if (style->stroke.value.href) { - style->stroke_ps_modified_connection.disconnect(); - delete style->stroke.value.href; - style->stroke.value.href = NULL; - } - if (style->filter.href) { - style->filter_modified_connection.disconnect(); - delete style->filter.href; - style->filter.href = NULL; - } - - style->filter_modified_connection.~connection(); - style->fill_ps_modified_connection.~connection(); - style->stroke_ps_modified_connection.~connection(); - - style->fill.clear(); - style->stroke.clear(); - sp_style_filter_clear(style); - - style->stroke_dasharray.values.clear(); - - for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) { - if (style->marker[i].value) { - g_free(style->marker[i].value); - style->marker[i].value = NULL; - } - } - g_free(style); - return NULL; +// Matches void sp_style_clear(); +void +SPStyle::clear() { + + for_each( _properties.begin(), _properties.end(), clear_property ); + // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { + // (this->*(i->second)).clear(); + // } + + // Release connection to object, created in sp_style_new_from_object() + release_connection.disconnect(); + + // href->detach() called in fill->clear()... + fill_ps_modified_connection.disconnect(); + if (fill.value.href) { + delete fill.value.href; + fill.value.href = NULL; } - return style; + stroke_ps_modified_connection.disconnect(); + if (stroke.value.href) { + delete stroke.value.href; + stroke.value.href = NULL; + } + filter_modified_connection.disconnect(); + if (filter.href) { + delete filter.href; + filter.href = NULL; + } + + if (document) { + filter.href = new SPFilterReference(document); + filter.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), this)); + + fill.value.href = new SPPaintServerReference(document); + fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), this)); + + stroke.value.href = new SPPaintServerReference(document); + stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), this)); + } + + cloned = false; + } -/** - * Reads the various style parameters for an object from repr. - */ -static void -sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) -{ - g_assert(style != NULL); +// Matches void sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr) +void +SPStyle::read( SPObject *object, Inkscape::XML::Node *repr ) { + + // std::cout << "SPstyle::read( SPObject, Inkscape::XML::Node ): Entrance: " + // << (object?(object->getId()?object->getId():"id null"):"object null") << " " + // << (repr?(repr->name()?repr->name():"no name"):"repr null") + // << std::endl; g_assert(repr != NULL); g_assert(!object || (object->getRepr() == repr)); - sp_style_clear(style); + // // Uncomment to verify that we don't need to call clear. + // std::cout << " Creating temp style for testing" << std::endl; + // SPStyle *temp = new SPStyle(); + // if( !(*temp == *this ) ) std::cout << "SPStyle::read: Need to clear" << std::endl; + // delete temp; + + clear(); // FIXME, If this isn't here, gradient editing stops working. Why? if (object && object->cloned) { - style->cloned = true; + cloned = true; } /* 1. Style attribute */ + // std::cout << " MERGING STYLE ATTRIBUTE" << std::endl; gchar const *val = repr->attribute("style"); - if (val != NULL && *val) { - sp_style_merge_from_style_string(style, val); + if( val != NULL && *val ) { + _mergeString( val ); } + /* 2 Style sheet */ + // std::cout << " MERGING OBJECT STYLESHEET" << std::endl; if (object) { - sp_style_merge_from_object_stylesheet(style, object); + _mergeObjectStylesheet( object ); } else { - /** \todo No stylesheet information. Find out under what circumstances - * this occurs, and handle accordingly. (If we really wanted to, we - * could probably get stylesheets by going through repr->doc.) - */ + // std::cerr << "SPStyle::read: No object! Can not read style sheet" << std::endl; } - /* 2. Presentation attributes */ - /* Attributes are only read in if not already set in a style sheet or style attribute above. */ - - /* CSS2 */ - SPS_READ_PENUM_IF_UNSET(&style->visibility, repr, "visibility", enum_visibility, true); - SPS_READ_PENUM_IF_UNSET(&style->display, repr, "display", enum_display, true); - SPS_READ_PENUM_IF_UNSET(&style->overflow, repr, "overflow", enum_overflow, true); - - /* CSS Compositing and Blending Level 1 */ - SPS_READ_PENUM_IF_UNSET(&style->isolation, repr, "isolation", enum_isolation, true); - SPS_READ_PENUM_IF_UNSET(&style->blend_mode, repr, "mix_blend_mode", enum_blend_mode, true); - - /* Font */ - SPS_READ_PFONTSIZE_IF_UNSET(&style->font_size, repr, "font-size"); - SPS_READ_PENUM_IF_UNSET(&style->font_style, repr, "font-style", enum_font_style, true); - SPS_READ_PENUM_IF_UNSET(&style->font_variant, repr, "font-variant", enum_font_variant, true); - SPS_READ_PENUM_IF_UNSET(&style->font_weight, repr, "font-weight", enum_font_weight, true); - SPS_READ_PENUM_IF_UNSET(&style->font_stretch, repr, "font-stretch", enum_font_stretch, true); - /* Text (css2 chapter 16) */ - SPS_READ_PLENGTH_IF_UNSET(&style->text_indent, repr, "text-indent"); - SPS_READ_PENUM_IF_UNSET(&style->text_align, repr, "text-align", enum_text_align, true); - if (!style->text_decoration_line.set) { - // assume it uses either text-decoration or text-decoration-line, but not both - if ((val = repr->attribute("text-decoration")) || (val = repr->attribute("text-decoration-line"))) { - sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, val); - } - } - if (!style->line_height.set) { - val = repr->attribute("line-height"); - if (val) { - sp_style_read_ilengthornormal(&style->line_height, val); - } - } - if (!style->letter_spacing.set) { - val = repr->attribute("letter-spacing"); - if (val) { - sp_style_read_ilengthornormal(&style->letter_spacing, val); - } - } - if (!style->word_spacing.set) { - val = repr->attribute("word-spacing"); - if (val) { - sp_style_read_ilengthornormal(&style->word_spacing, val); - } - } - SPS_READ_PENUM_IF_UNSET(&style->text_transform, repr, "text-transform", enum_text_transform, true); - SPS_READ_PENUM_IF_UNSET(&style->direction, repr, "direction", enum_direction, true); - SPS_READ_PENUM_IF_UNSET(&style->block_progression, repr, "block_progression", enum_block_progression, true); - - /* SVG */ - SPS_READ_PENUM_IF_UNSET(&style->writing_mode, repr, "writing-mode", - enum_writing_mode, true); - SPS_READ_PENUM_IF_UNSET(&style->text_anchor, repr, "text-anchor", - enum_text_anchor, true); - SPS_READ_PBASELINE_SHIFT_IF_UNSET(&style->baseline_shift, repr, "baseline-shift"); - - /* opacity */ - if (!style->opacity.set) { - val = repr->attribute("opacity"); - if (val) { - sp_style_read_iscale24(&style->opacity, val); - } - } - /* color */ - if (!style->color.set) { - val = repr->attribute("color"); - if (val) { - sp_style_read_icolor(&style->color, val, style, ( object - ? object->document - : NULL )); - } - } - /* color interpolation */ - SPS_READ_PENUM_IF_UNSET(&style->color_interpolation, repr, "color-interpolation", enum_color_interpolation, true); - /* color interpolation filters*/ - SPS_READ_PENUM_IF_UNSET(&style->color_interpolation_filters, repr, "color-interpolation-filters", enum_color_interpolation, true); - /* fill */ - if (!style->fill.set) { - val = repr->attribute("fill"); - if (val) { - style->fill.read( val, *style, (object) ? object->document : NULL ); - } - } - /* fill-opacity */ - if (!style->fill_opacity.set) { - val = repr->attribute("fill-opacity"); - if (val) { - sp_style_read_iscale24(&style->fill_opacity, val); - } - } - /* fill-rule */ - SPS_READ_PENUM_IF_UNSET(&style->fill_rule, repr, "fill-rule", enum_fill_rule, true); - /* stroke */ - if (!style->stroke.set) { - val = repr->attribute("stroke"); - if (val) { - style->stroke.read( val, *style, (object) ? object->document : NULL ); - } - } - SPS_READ_PLENGTH_IF_UNSET(&style->stroke_width, repr, "stroke-width"); - SPS_READ_PENUM_IF_UNSET(&style->stroke_linecap, repr, "stroke-linecap", enum_stroke_linecap, true); - SPS_READ_PENUM_IF_UNSET(&style->stroke_linejoin, repr, "stroke-linejoin", enum_stroke_linejoin, true); - SPS_READ_PFLOAT_IF_UNSET(&style->stroke_miterlimit, repr, "stroke-miterlimit"); - - /* markers */ - if (!style->marker[SP_MARKER_LOC].set) { - val = repr->attribute("marker"); - if (val) { - sp_style_read_istring(&style->marker[SP_MARKER_LOC], val); - } - } - if (!style->marker[SP_MARKER_LOC_START].set) { - val = repr->attribute("marker-start"); - if (val) { - sp_style_read_istring(&style->marker[SP_MARKER_LOC_START], val); - } - } - if (!style->marker[SP_MARKER_LOC_MID].set) { - val = repr->attribute("marker-mid"); - if (val) { - sp_style_read_istring(&style->marker[SP_MARKER_LOC_MID], val); - } - } - if (!style->marker[SP_MARKER_LOC_END].set) { - val = repr->attribute("marker-end"); - if (val) { - sp_style_read_istring(&style->marker[SP_MARKER_LOC_END], val); - } + /* 3 Presentation attributes */ + // std::cout << " MERGING PRESENTATION ATTRIBUTES" << std::endl; + for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { + _properties[i]->readAttribute( repr ); } + // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { + // (this->*(i->second)).readAttribute( repr ); + // } - /* stroke-opacity */ - if (!style->stroke_opacity.set) { - val = repr->attribute("stroke-opacity"); - if (val) { - sp_style_read_iscale24(&style->stroke_opacity, val); - } - } - if (!style->stroke_dasharray.set) { - val = repr->attribute("stroke-dasharray"); - if (val) { - sp_style_read_dash(style, val); - } - } - SPS_READ_PLENGTH_IF_UNSET(&style->stroke_width, repr, "stroke-dashoffset"); - - /* paint-order */ - if (!style->paint_order.set) { - val = repr->attribute("paint-order"); - if (val) { - sp_style_read_ipaintorder(&style->paint_order, val); - } else { - style->paint_order.layer[0] = SP_CSS_PAINT_ORDER_NORMAL; - } - } - - /* -inkscape-font-specification */ - if (!style->text_private || !style->text->font_specification.set) { - val = repr->attribute("-inkscape-font-specification"); - if (val) { - if (!style->text_private) sp_style_privatize_text(style); - gchar *val_unquoted = attribute_unquote(val); - sp_style_read_istring(&style->text->font_specification, val_unquoted); - if (val_unquoted) g_free (val_unquoted); - } - } - - /* font-family */ - if (!style->text_private || !style->text->font_family.set) { - val = repr->attribute("font-family"); - if (val) { - if (!style->text_private) sp_style_privatize_text(style); - gchar *val_unquoted = attribute_unquote(val); - sp_style_read_istring(&style->text->font_family, val_unquoted); - if (val_unquoted) g_free (val_unquoted); - } - } - - /* filter effects */ - if (!style->filter.set) { - val = repr->attribute("filter"); - if (val) { - sp_style_read_ifilter(val, style, (object) ? object->document : NULL); - } - } - SPS_READ_PENUM_IF_UNSET(&style->enable_background, repr, - "enable-background", enum_enable_background, true); - - /* clip-rule */ - SPS_READ_PENUM_IF_UNSET(&style->clip_rule, repr, "clip-rule", enum_clip_rule, true); - - /* color_rendering, image_rendering, shape_rendering, text_rendering */ - SPS_READ_PENUM_IF_UNSET(&style->color_rendering, repr, "color-rendering", enum_color_rendering, true); - SPS_READ_PENUM_IF_UNSET(&style->image_rendering, repr, "image-rendering", enum_image_rendering, true); - SPS_READ_PENUM_IF_UNSET(&style->shape_rendering, repr, "shape-rendering", enum_shape_rendering, true); - SPS_READ_PENUM_IF_UNSET(&style->text_rendering, repr, "text-rendering", enum_text_rendering, true); - - /* 3. Merge from parent */ - if (object) { - if (object->parent) { - sp_style_merge_from_parent(style, object->parent->style); + /* 4 Cascade from parent */ + // std::cout << " CASCADING FROM PARENT" << std::endl; + if( object ) { + if( object->parent ) { + cascade( object->parent->style ); } } else { - if (repr->parent()) { - /// \todo fixme: This is not the prettiest thing (Lauris) - SPStyle *parent = sp_style_new(NULL); - sp_style_read(parent, NULL, repr->parent()); - sp_style_merge_from_parent(style, parent); - sp_style_unref(parent); + // When does this happen? + // std::cout << "SPStyle::read(): reading via repr->parent()" << std::endl; + if( repr->parent() ) { + SPStyle *parent = new SPStyle(); + parent->read( NULL, repr->parent() ); + cascade( parent ); + delete parent; } } } - -/** - * 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) - */ +// Matches void sp_style_read_from_object(SPStyle *style, SPObject *object); void -sp_style_read_from_object(SPStyle *style, SPObject *object) -{ - g_return_if_fail(style != NULL); +SPStyle::readFromObject( SPObject *object ) { + + // std::cout << "SPStyle::readFromObject: "<< (object->getId()?object->getId():"null")<< std::endl; + g_return_if_fail(object != NULL); g_return_if_fail(SP_IS_OBJECT(object)); Inkscape::XML::Node *repr = object->getRepr(); g_return_if_fail(repr != NULL); - sp_style_read(style, object, repr); + read( object, repr ); } - -/** - * Read style properties from preferences. - * @param style The style to write to - * @param path Preferences directory from which the style should be read - */ +// Matches sp_style_merge_property(SPStyle *style, gint id, gchar const *val) void -sp_style_read_from_prefs(SPStyle *style, Glib::ustring const &path) -{ - g_return_if_fail(style != NULL); - g_return_if_fail(path != ""); - - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - - // not optimal: we reconstruct the node based on the prefs, then pass it to - // sp_style_read for actual processing. - Inkscape::XML::SimpleDocument *tempdoc = new Inkscape::XML::SimpleDocument; - Inkscape::XML::Node *tempnode = tempdoc->createElement("temp"); - - std::vector<Inkscape::Preferences::Entry> attrs = prefs->getAllEntries(path); - for (std::vector<Inkscape::Preferences::Entry>::iterator i = attrs.begin(); i != attrs.end(); ++i) { - tempnode->setAttribute(i->getEntryName().data(), i->getString().data()); - } - - sp_style_read(style, NULL, tempnode); - - Inkscape::GC::release(tempnode); - Inkscape::GC::release(tempdoc); - delete tempdoc; -} - +SPStyle::readIfUnset( gint id, gchar const *val ) { - -/** - * - */ -static void -sp_style_privatize_text(SPStyle *style) -{ - SPTextStyle *text = style->text; - style->text = sp_text_style_duplicate_unset(style->text); - sp_text_style_unref(text); - style->text_private = TRUE; -} - - -/** - * 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. - * - * \pre val != NULL. - */ -static void -sp_style_merge_property(SPStyle *style, gint id, gchar const *val) -{ + // std::cout << "SPStyle::readIfUnset: Entrance: " << (val?val:"null") << std::endl; + // To Do: If it is not too slow, use std::map instead of std::vector inorder to remove switch() + // (looking up SP_PROP_xxxx already uses a hash). g_return_if_fail(val != NULL); switch (id) { case SP_PROP_INKSCAPE_FONT_SPEC: - if (!style->text_private) sp_style_privatize_text(style); - if (!style->text->font_specification.set) { - gchar *val_unquoted = attribute_unquote(val); - sp_style_read_istring(&style->text->font_specification, val_unquoted); - if (val_unquoted) g_free (val_unquoted); - } + font_specification.readIfUnset( val ); break; - /* CSS2 */ - /* Font */ case SP_PROP_FONT_FAMILY: - if (!style->text_private) sp_style_privatize_text(style); - if (!style->text->font_family.set) { - gchar *val_unquoted = attribute_unquote(val); - sp_style_read_istring(&style->text->font_family, val_unquoted); - if (val_unquoted) g_free (val_unquoted); - } + font_family.readIfUnset( val ); break; case SP_PROP_FONT_SIZE: - SPS_READ_IFONTSIZE_IF_UNSET(&style->font_size, val); + font_size.readIfUnset( val ); break; case SP_PROP_FONT_SIZE_ADJUST: if (strcmp(val, "none") != 0) { @@ -991,220 +609,71 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) } break; case SP_PROP_FONT_STYLE: - SPS_READ_IENUM_IF_UNSET(&style->font_style, val, enum_font_style, true); + font_style.readIfUnset( val ); break; case SP_PROP_FONT_VARIANT: - SPS_READ_IENUM_IF_UNSET(&style->font_variant, val, enum_font_variant, true); + font_variant.readIfUnset( val ); break; case SP_PROP_FONT_WEIGHT: - SPS_READ_IENUM_IF_UNSET(&style->font_weight, val, enum_font_weight, true); + font_weight.readIfUnset( val ); break; case SP_PROP_FONT_STRETCH: - SPS_READ_IENUM_IF_UNSET(&style->font_stretch, val, enum_font_stretch, true); + font_stretch.readIfUnset( val ); break; case SP_PROP_FONT: - if (!style->text_private) sp_style_privatize_text(style); - if (!style->text->font.set) { - g_free(style->text->font.value); - style->text->font.value = g_strdup(val); - style->text->font.set = TRUE; - style->text->font.inherit = (val && !strcmp(val, "inherit")); - - // Break string into white space separated tokens - std::stringstream os( val ); - Glib::ustring param; - - while (os >> param) { - - // CSS is case insensitive but we're comparing against lowercase strings - Glib::ustring lparam = param.lowercase(); - - if (lparam == "/") { - - os >> param; - // Eat the line-height for the moment as it is not an SVG property. - // lparam = param.lowercase(); - // sp_style_read_ilengthornormal(&style->line_height, lparam); - - } else { - - // Skip if "normal" as that is the default (and we don't know which attribute it applies to). - if (lparam == "normal") continue; - - // Check each property in turn - - // font-style - SPIEnum test_style; - test_style.set = FALSE; - - // Read once to see if param is valid style. If valid, .set will be TRUE. - sp_style_read_ienum(&test_style, lparam.c_str(), enum_font_style, true); - - // If valid style parameter - if (test_style.set) { - - // If not previously set - if (!style->font_style.set) { - style->font_style.set = TRUE; - style->font_style.inherit = test_style.inherit; - style->font_style.value = test_style.value; - style->font_style.computed = test_style.computed; - } - continue; // Next parameter. - } - - // font-variant (small-caps) - SPIEnum test_variant; - test_variant.set = FALSE; - sp_style_read_ienum(&test_variant, lparam.c_str(), enum_font_variant, true); - - // If valid variant parameter - if (test_variant.set) { - - // If not previously set - if (!style->font_variant.set) { - style->font_variant.set = TRUE; - style->font_variant.inherit = test_variant.inherit; - style->font_variant.value = test_variant.value; - style->font_variant.computed = test_variant.computed; - } - continue; // Next parameter. - } - - // font-weight - SPIEnum test_weight; - test_weight.set = FALSE; - sp_style_read_ienum(&test_weight, lparam.c_str(), enum_font_weight, true); - - // If valid weight parameter - if (test_weight.set) { - - // If not previously set - if (!style->font_weight.set) { - style->font_weight.set = TRUE; - style->font_weight.inherit = test_weight.inherit; - style->font_weight.value = test_weight.value; - style->font_weight.computed = test_weight.computed; - } - continue; // Next parameter - } - - // Font-size - SPIFontSize test_size; - test_size.set = FALSE; - - // Read once to see if param is valid size. - sp_style_read_ifontsize( &test_size, lparam.c_str() ); - - // If valid size parameter - if (test_size.set) { - - // If not previously set - if (!style->font_size.set) { - style->font_size.set = TRUE; - style->font_size.inherit = test_size.inherit; - style->font_size.unit = test_size.unit; - style->font_size.value = test_size.value; - style->font_size.computed = test_size.computed; - style->font_size.type = test_size.type; - style->font_size.literal = test_size.literal; - } - continue; - } - - // No valid property value found. - break; - } - } // params - - // The rest must be font-family... - std::string val_s = val; - std::string family = val_s.substr( val_s.find( param ) ); - - if (!style->text_private) sp_style_privatize_text(style); - if (!style->text->font_family.set) { - gchar *val_unquoted = attribute_unquote( family.c_str() ); - sp_style_read_istring(&style->text->font_family, val_unquoted); - if (val_unquoted) g_free (val_unquoted); - } - - // Set all properties to their default values per CSS 2.1 spec if not already set - SPS_READ_IFONTSIZE_IF_UNSET(&style->font_size, "medium" ); - SPS_READ_IENUM_IF_UNSET(&style->font_style, "normal", enum_font_style, true); - SPS_READ_IENUM_IF_UNSET(&style->font_variant, "normal", enum_font_variant, true); - SPS_READ_IENUM_IF_UNSET(&style->font_weight, "normal", enum_font_weight, true); - // Line height is not an SVG property but Inkscape uses it for multi-line text. - // sp_style_read_ilengthornormal(&style->line_height, "normal"); - - } - + font.readIfUnset( val ); break; /* Text */ case SP_PROP_TEXT_INDENT: - SPS_READ_ILENGTH_IF_UNSET(&style->text_indent, val); + text_indent.readIfUnset( val ); break; case SP_PROP_TEXT_ALIGN: - SPS_READ_IENUM_IF_UNSET(&style->text_align, val, enum_text_align, true); + text_align.readIfUnset( val ); break; case SP_PROP_TEXT_DECORATION: - if (!style->text_decoration_line.set) { - sp_style_read_itextdecoration(&style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, val); - } + text_decoration.readIfUnset( val ); break; case SP_PROP_TEXT_DECORATION_LINE: - if (!style->text_decoration_line.set) { - sp_style_read_itextdecorationLine(&style->text_decoration_line, val); - } + text_decoration_line.readIfUnset( val ); break; case SP_PROP_TEXT_DECORATION_STYLE: - if (!style->text_decoration_style.set) { - sp_style_read_itextdecorationStyle(&style->text_decoration_style, val); - } + text_decoration_style.readIfUnset( val ); break; case SP_PROP_TEXT_DECORATION_COLOR: - if (!style->text_decoration_color.set) { - sp_style_read_itextdecorationColor(&style->text_decoration_color, val); - } + text_decoration_color.readIfUnset( val ); break; case SP_PROP_LINE_HEIGHT: - if (!style->line_height.set) { - sp_style_read_ilengthornormal(&style->line_height, val); - } + line_height.readIfUnset( val ); break; case SP_PROP_LETTER_SPACING: - if (!style->letter_spacing.set) { - sp_style_read_ilengthornormal(&style->letter_spacing, val); - } + letter_spacing.readIfUnset( val ); break; case SP_PROP_WORD_SPACING: - if (!style->word_spacing.set) { - sp_style_read_ilengthornormal(&style->word_spacing, val); - } + word_spacing.readIfUnset( val ); break; case SP_PROP_TEXT_TRANSFORM: - SPS_READ_IENUM_IF_UNSET(&style->text_transform, val, enum_text_transform, true); + text_transform.readIfUnset( val ); break; /* Text (css3) */ case SP_PROP_DIRECTION: - SPS_READ_IENUM_IF_UNSET(&style->direction, val, enum_direction, true); + direction.readIfUnset( val ); break; case SP_PROP_BLOCK_PROGRESSION: - SPS_READ_IENUM_IF_UNSET(&style->block_progression, val, enum_block_progression, true); + block_progression.readIfUnset( val ); break; case SP_PROP_WRITING_MODE: - SPS_READ_IENUM_IF_UNSET(&style->writing_mode, val, enum_writing_mode, true); + writing_mode.readIfUnset( val ); break; case SP_PROP_TEXT_ANCHOR: - SPS_READ_IENUM_IF_UNSET(&style->text_anchor, val, enum_text_anchor, true); + text_anchor.readIfUnset( val ); break; case SP_PROP_BASELINE_SHIFT: - SPS_READ_IBASELINE_SHIFT_IF_UNSET(&style->baseline_shift, val); + baseline_shift.readIfUnset( val ); break; - case SP_PROP_TEXT_RENDERING: { - SPS_READ_IENUM_IF_UNSET(&style->text_rendering, val, enum_text_rendering, true); + case SP_PROP_TEXT_RENDERING: + text_rendering.readIfUnset( val ); break; - } case SP_PROP_ALIGNMENT_BASELINE: g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val); break; @@ -1225,31 +694,25 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) g_warning("Unimplemented style property SP_PROP_CLIP: value: %s", val); break; case SP_PROP_COLOR: - if (!style->color.set) { - sp_style_read_icolor(&style->color, val, style, (style->object) ? style->object->document : NULL); - } + color.readIfUnset( val ); break; case SP_PROP_CURSOR: g_warning("Unimplemented style property SP_PROP_CURSOR: value: %s", val); break; case SP_PROP_DISPLAY: - SPS_READ_IENUM_IF_UNSET(&style->display, val, enum_display, true); + display.readIfUnset( val ); break; case SP_PROP_OVERFLOW: - /** \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); + overflow.readIfUnset( val ); break; case SP_PROP_VISIBILITY: - SPS_READ_IENUM_IF_UNSET(&style->visibility, val, enum_visibility, true); + visibility.readIfUnset( val ); break; case SP_PROP_ISOLATION: - SPS_READ_IENUM_IF_UNSET(&style->isolation, val, enum_isolation, true); + isolation.readIfUnset( val ); break; case SP_PROP_BLEND_MODE: - SPS_READ_IENUM_IF_UNSET(&style->blend_mode, val, enum_blend_mode, true); + blend_mode.readIfUnset( val ); break; /* SVG */ @@ -1264,12 +727,10 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) g_warning("attribute 'clip-path' given as CSS"); //XML Tree being directly used here. - style->object->getRepr()->setAttribute("clip-path", val); + this->object->getRepr()->setAttribute("clip-path", val); break; case SP_PROP_CLIP_RULE: - if (!style->clip_rule.set) { - sp_style_read_ienum(&style->clip_rule, val, enum_clip_rule, true); - } + clip_rule.readIfUnset( val ); break; case SP_PROP_MASK: /** \todo @@ -1278,22 +739,17 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) g_warning("attribute 'mask' given as CSS"); //XML Tree being directly used here. - style->object->getRepr()->setAttribute("mask", val); + this->object->getRepr()->setAttribute("mask", val); break; case SP_PROP_OPACITY: - if (!style->opacity.set) { - sp_style_read_iscale24(&style->opacity, val); - } + opacity.readIfUnset( val ); break; case SP_PROP_ENABLE_BACKGROUND: - SPS_READ_IENUM_IF_UNSET(&style->enable_background, val, - enum_enable_background, true); + enable_background.readIfUnset( val ); break; /* Filter */ case SP_PROP_FILTER: - if (!style->filter.set && !style->filter.inherit) { - sp_style_read_ifilter(val, style, (style->object) ? style->object->document : NULL); - } + if( !filter.inherit ) filter.readIfUnset( val ); break; case SP_PROP_FLOOD_COLOR: g_warning("Unimplemented style property SP_PROP_FLOOD_COLOR: value: %s", val); @@ -1318,139 +774,186 @@ sp_style_merge_property(SPStyle *style, gint id, gchar const *val) /* Paint */ case SP_PROP_COLOR_INTERPOLATION: // We read it but issue warning - SPS_READ_IENUM_IF_UNSET(&style->color_interpolation, val, enum_color_interpolation, true); - if( style->color_interpolation.value != SP_CSS_COLOR_INTERPOLATION_SRGB ) { + color_interpolation.readIfUnset( val ); + if( color_interpolation.value != SP_CSS_COLOR_INTERPOLATION_SRGB ) { g_warning("Inkscape currently only supports color-interpolation = sRGB"); } break; case SP_PROP_COLOR_INTERPOLATION_FILTERS: - SPS_READ_IENUM_IF_UNSET(&style->color_interpolation_filters, val, enum_color_interpolation, true); + color_interpolation_filters.readIfUnset( val ); break; case SP_PROP_COLOR_PROFILE: g_warning("Unimplemented style property SP_PROP_COLOR_PROFILE: value: %s", val); break; - case SP_PROP_COLOR_RENDERING: { - SPS_READ_IENUM_IF_UNSET(&style->color_rendering, val, enum_color_rendering, true); + case SP_PROP_COLOR_RENDERING: + color_rendering.readIfUnset( val ); break; - } case SP_PROP_FILL: - if (!style->fill.set) { - style->fill.read( val, *style, (style->object) ? style->object->document : NULL ); - } + fill.readIfUnset( val ); break; case SP_PROP_FILL_OPACITY: - if (!style->fill_opacity.set) { - sp_style_read_iscale24(&style->fill_opacity, val); - } + fill_opacity.readIfUnset( val ); break; case SP_PROP_FILL_RULE: - if (!style->fill_rule.set) { - sp_style_read_ienum(&style->fill_rule, val, enum_fill_rule, true); - } + fill_rule.readIfUnset( val ); break; - case SP_PROP_IMAGE_RENDERING: { - SPS_READ_IENUM_IF_UNSET(&style->image_rendering, val, enum_image_rendering, true); + case SP_PROP_IMAGE_RENDERING: + image_rendering.readIfUnset( val ); break; - } case SP_PROP_MARKER: - /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - /* style->marker[SP_MARKER_LOC] = g_quark_from_string(val); */ - if (!style->marker[SP_MARKER_LOC].set) { - g_free(style->marker[SP_MARKER_LOC].value); - style->marker[SP_MARKER_LOC].value = g_strdup(val); - style->marker[SP_MARKER_LOC].set = TRUE; - style->marker[SP_MARKER_LOC].inherit = (val && !strcmp(val, "inherit")); - } + /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ + marker.readIfUnset( val ); break; - case SP_PROP_MARKER_START: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - if (!style->marker[SP_MARKER_LOC_START].set) { - g_free(style->marker[SP_MARKER_LOC_START].value); - style->marker[SP_MARKER_LOC_START].value = g_strdup(val); - style->marker[SP_MARKER_LOC_START].set = TRUE; - style->marker[SP_MARKER_LOC_START].inherit = (val && !strcmp(val, "inherit")); - } + marker_start.readIfUnset( val ); break; case SP_PROP_MARKER_MID: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - if (!style->marker[SP_MARKER_LOC_MID].set) { - g_free(style->marker[SP_MARKER_LOC_MID].value); - style->marker[SP_MARKER_LOC_MID].value = g_strdup(val); - style->marker[SP_MARKER_LOC_MID].set = TRUE; - style->marker[SP_MARKER_LOC_MID].inherit = (val && !strcmp(val, "inherit")); - } + marker_mid.readIfUnset( val ); break; case SP_PROP_MARKER_END: /* TODO: Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */ - if (!style->marker[SP_MARKER_LOC_END].set) { - g_free(style->marker[SP_MARKER_LOC_END].value); - style->marker[SP_MARKER_LOC_END].value = g_strdup(val); - style->marker[SP_MARKER_LOC_END].set = TRUE; - style->marker[SP_MARKER_LOC_END].inherit = (val && !strcmp(val, "inherit")); - } + marker_end.readIfUnset( val ); break; - - case SP_PROP_SHAPE_RENDERING: { - SPS_READ_IENUM_IF_UNSET(&style->shape_rendering, val, enum_shape_rendering, true); + case SP_PROP_SHAPE_RENDERING: + shape_rendering.readIfUnset( val ); break; - } - case SP_PROP_STROKE: - if (!style->stroke.set) { - style->stroke.read( val, *style, (style->object) ? style->object->document : NULL ); - } + stroke.readIfUnset( val ); break; case SP_PROP_STROKE_WIDTH: - SPS_READ_ILENGTH_IF_UNSET(&style->stroke_width, val); + stroke_width.readIfUnset( val ); break; case SP_PROP_STROKE_DASHARRAY: - if (!style->stroke_dasharray.set) { - sp_style_read_dash(style, val); - } + stroke_dasharray.readIfUnset( val ); break; case SP_PROP_STROKE_DASHOFFSET: - SPS_READ_ILENGTH_IF_UNSET(&style->stroke_dashoffset, val); + stroke_dashoffset.readIfUnset( val ); break; case SP_PROP_STROKE_LINECAP: - if (!style->stroke_linecap.set) { - sp_style_read_ienum(&style->stroke_linecap, val, enum_stroke_linecap, true); - } + stroke_linecap.readIfUnset( val ); break; case SP_PROP_STROKE_LINEJOIN: - if (!style->stroke_linejoin.set) { - sp_style_read_ienum(&style->stroke_linejoin, val, enum_stroke_linejoin, true); - } + stroke_linejoin.readIfUnset( val ); break; case SP_PROP_STROKE_MITERLIMIT: - if (!style->stroke_miterlimit.set) { - sp_style_read_ifloat(&style->stroke_miterlimit, val); - } + stroke_miterlimit.readIfUnset( val ); break; case SP_PROP_STROKE_OPACITY: - if (!style->stroke_opacity.set) { - sp_style_read_iscale24(&style->stroke_opacity, val); - } + stroke_opacity.readIfUnset( val ); break; case SP_PROP_PAINT_ORDER: - if (!style->paint_order.set) { - sp_style_read_ipaintorder(&style->paint_order, val); - } + paint_order.readIfUnset( val ); break; - default: - g_warning("Invalid style property id: %d value: %s", id, val); + g_warning("SPIStyle::readIfUnset(): Invalid style property id: %d value: %s", id, val); break; } } -static void -sp_style_merge_style_from_decl(SPStyle *const style, CRDeclaration const *const decl) -{ - /** \todo Ensure that property is lcased, as per - * http://www.w3.org/TR/REC-CSS2/syndata.html#q4. - * Should probably be done in libcroco. - */ +Glib::ustring +SPStyle::write( guint const flags, SPStyle const *const base ) const { + + // std::cout << "SPStyle::write" << std::endl; + + Glib::ustring style_string; + for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { + if( base != NULL ) { + style_string += _properties[i]->write( flags, base->_properties[i] ); + } else { + style_string += _properties[i]->write( flags, NULL ); + } + } + // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { + // if( base != NULL ) { + // style_string += (this->*(i->second)).write( flags, &(base->*(i->second)) ); + // } else { + // style_string += (this->*(i->second)).write( flags, NULL ); + // } + // } + + // Remove trailing ';' + if( style_string.size() > 0 ) { + style_string.erase( style_string.size() - 1 ); + } + return style_string; +} + +// Corresponds to sp_style_merge_from_parent() +void +SPStyle::cascade( SPStyle const *const parent ) { + // std::cout << "SPStyle::cascade" << std::endl; + for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { + _properties[i]->cascade( parent->_properties[i] ); + } + // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { + // (this->*(i->second)).cascade( &(parent->*(i->second)) ); + // } +} + +// Corresponds to sp_style_merge_from_dying_parent() +void +SPStyle::merge( SPStyle const *const parent ) { + // std::cout << "SPStyle::merge" << std::endl; + for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { + _properties[i]->merge( parent->_properties[i] ); + } + // for(SPPropMap::iterator i = _propmap.begin(); i != _propmap.end(); ++i ) { + // (this->*(i->second)).cascade( &(parent->*(i->second)) ); + // } +} + +// Mostly for unit testing +bool +SPStyle::operator==(const SPStyle& rhs) { + + // Uncomment for testing + // for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { + // if( *_properties[i] != *rhs._properties[i]) + // std::cout << _properties[i]->name << ": " + // << _properties[i]->write(SP_STYLE_FLAG_ALWAYS,NULL) << " " + // << rhs._properties[i]->write(SP_STYLE_FLAG_ALWAYS,NULL) + // << (*_properties[i] == *rhs._properties[i]) << std::endl; + // } + + for(std::vector<SPIBase*>::size_type i = 0; i != _properties.size(); ++i) { + if( *_properties[i] != *rhs._properties[i]) return false; + } + return true; +} + +void +SPStyle::_mergeString( gchar const *const p ) { + + // std::cout << "SPStyle::_mergeString: " << (p?p:"null") << std::endl; + CRDeclaration *const decl_list + = cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8); + if (decl_list) { + _mergeDeclList( decl_list ); + cr_declaration_destroy(decl_list); + } +} + +void +SPStyle::_mergeDeclList( CRDeclaration const *const decl_list ) { + + // std::cout << "SPStyle::_mergeDeclList" << std::endl; + + // In reverse order, as later declarations to take precedence over earlier ones. + // (Properties are only set if not previously set. See: + // Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.) + if (decl_list->next) { + _mergeDeclList( decl_list->next ); + } + _mergeDecl( decl_list ); +} + +void +SPStyle::_mergeDecl( CRDeclaration const *const decl ) { + + // std::cout << "SPStyle::_mergeDecl" << std::endl; + unsigned const prop_idx = sp_attribute_lookup(decl->property->stryng->str); if (prop_idx != SP_ATTR_INVALID) { /** \todo @@ -1460,71 +963,30 @@ sp_style_merge_style_from_decl(SPStyle *const style, CRDeclaration const *const */ guchar *const str_value_unsigned = cr_term_to_string(decl->value); gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned); - sp_style_merge_property(style, prop_idx, str_value); + readIfUnset( prop_idx, str_value ); g_free(str_value); } } -static void -sp_style_merge_from_props(SPStyle *const style, CRPropList *const props) -{ -#if 0 /* forwards */ - for (CRPropList const *cur = props; cur; cur = cr_prop_list_get_next(cur)) { - CRDeclaration *decl = NULL; - cr_prop_list_get_decl(cur, &decl); - sp_style_merge_style_from_decl(style, decl); - } -#else /* in reverse order, as we need later declarations to take precedence over earlier ones. */ +void +SPStyle::_mergeProps( CRPropList *const props ) { + + // std::cout << "SPStyle::_mergeProps" << std::endl; + + // In reverse order, as later declarations to take precedence over earlier ones. if (props) { - sp_style_merge_from_props(style, cr_prop_list_get_next(props)); + _mergeProps( cr_prop_list_get_next( props ) ); CRDeclaration *decl = NULL; cr_prop_list_get_decl(props, &decl); - sp_style_merge_style_from_decl(style, decl); + _mergeDecl( decl ); } -#endif -} - -/** - * \pre decl_list != NULL - */ -static void -sp_style_merge_from_decl_list(SPStyle *const style, CRDeclaration const *const decl_list) -{ - // read the decls from end to start, using head recursion, so that latter declarations override - // (Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.) - // because sp_style_merge_style_from_decl only sets properties that are unset - if (decl_list->next) { - sp_style_merge_from_decl_list(style, decl_list->next); - } - sp_style_merge_style_from_decl(style, decl_list); } -static CRSelEng * -sp_repr_sel_eng() -{ - CRSelEng *const ret = cr_sel_eng_new(); - cr_sel_eng_set_node_iface(ret, &Inkscape::XML::croco_node_iface); - - /** \todo - * 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. - * 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: - * :hover, :active, :focus, :visited, :link. - */ +void +SPStyle::_mergeObjectStylesheet( SPObject const *const object ) { - g_assert(ret); - return ret; -} + // std::cout << "SPStyle::_mergeObjectStylesheet: " << (object->getId()?object->getId():"null") << std::endl; -static void -sp_style_merge_from_object_stylesheet(SPStyle *const style, SPObject const *const object) -{ static CRSelEng *sel_eng = NULL; if (!sel_eng) { sel_eng = sp_repr_sel_eng(); @@ -1540,586 +1002,319 @@ sp_style_merge_from_object_stylesheet(SPStyle *const style, SPObject const *cons g_return_if_fail(status == CR_OK); /// \todo Check what errors can occur, and handle them properly. if (props) { - sp_style_merge_from_props(style, props); + _mergeProps(props); cr_prop_list_destroy(props); } } +// Internal /** - * Parses a style="..." string and merges it with an existing SPStyle. + * Release callback. */ -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" - * 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 - * attribute value? - */ - - CRDeclaration *const decl_list - = cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8); - if (decl_list) { - sp_style_merge_from_decl_list(style, decl_list); - cr_declaration_destroy(decl_list); - } -} - -/** Indexed by SP_CSS_FONT_SIZE_blah. */ -static float const font_size_table[] = {6.0, 8.0, 10.0, 12.0, 14.0, 18.0, 24.0}; - static void -sp_style_merge_font_size_from_parent(SPIFontSize &child, SPIFontSize const &parent) +sp_style_object_release(SPObject *object, SPStyle *style) { - /* 'font-size' */ - if (!child.set || child.inherit) { - /* Inherit the computed value. Reference: http://www.w3.org/TR/SVG11/styling.html#Inheritance */ - 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 - * user or screen coordinates (Lauris) - */ - if (child.literal < SP_CSS_FONT_SIZE_SMALLER) { - child.computed = font_size_table[child.literal]; - } else if (child.literal == SP_CSS_FONT_SIZE_SMALLER) { - child.computed = parent.computed / 1.2; - } else if (child.literal == SP_CSS_FONT_SIZE_LARGER) { - child.computed = parent.computed * 1.2; - } else { - /* Illegal value */ - } - } else if (child.type == SP_FONT_SIZE_PERCENTAGE) { - /* Unlike most other lengths, percentage for font size is relative to parent computed value - * rather than viewport. */ - child.computed = parent.computed * child.value; - } else if (child.type == SP_FONT_SIZE_LENGTH) { - switch (child.unit) { - case SP_CSS_UNIT_EM: - /* Relative to parent font size */ - child.computed = parent.computed * child.value; - break; - case SP_CSS_UNIT_EX: - /* Relative to parent font size */ - child.computed = parent.computed * child.value * 0.5; /* Hack */ - break; - default: - /* No change */ - break; - } - } + (void)object; // TODO + style->object = NULL; } -// Some shifts are defined relative to parent. +// Internal +/** + * Emit style modified signal on style's object if the filter changed. + */ static void -sp_style_merge_baseline_shift_from_parent(SPIBaselineShift &child, SPIBaselineShift const &parent, - SPIFontSize const &pfont_size) +sp_style_filter_ref_modified(SPObject *obj, guint flags, SPStyle *style) { - /* 'baseline-shift' */ - if (!child.set || child.inherit) { - /* Inherit the computed value. Reference: http://www.w3.org/TR/SVG11/styling.html#Inheritance */ - child.computed = parent.computed; // Does this make sense (applying a shift a second time)? - } else if (child.type == SP_BASELINE_SHIFT_LITERAL) { - if( child.literal == SP_CSS_BASELINE_SHIFT_BASELINE ) { - child.computed = 0; // No change - } else if (child.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. - child.computed = -0.2 * pfont_size.computed; - } else if (child.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. - child.computed = 0.4 * pfont_size.computed; - } else { - /* Illegal value */ - } - } else if (child.type == SP_BASELINE_SHIFT_PERCENTAGE) { - // Percentage for baseline shift is relative to computed "line-height" - // which is just font-size (see SVG1.1 'font'). - child.computed = pfont_size.computed * child.value; - } else if (child.type == SP_BASELINE_SHIFT_LENGTH) { - switch (child.unit) { - case SP_CSS_UNIT_EM: - child.computed = child.value * pfont_size.computed; - break; - case SP_CSS_UNIT_EX: - child.computed = child.value * 0.5 * pfont_size.computed; - break; - default: - /* No change */ - break; + (void)flags; // TODO + SPFilter *filter=static_cast<SPFilter *>(obj); + if (style->getFilter() == filter) + { + if (style->object) { + style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); } } - // baseline-shifts are relative to parent baseline - child.computed += parent.computed; } +// Internal /** - * Sets computed values in \a style, which may involve inheriting from (or in some other way - * calculating from) corresponding computed values of \a parent. - * - * References: http://www.w3.org/TR/SVG11/propidx.html shows what properties inherit by default. - * http://www.w3.org/TR/SVG11/styling.html#Inheritance gives general rules as to what it means to - * inherit a value. http://www.w3.org/TR/REC-CSS2/cascade.html#computed-value is more precise - * about what the computed value is (not obvious for lengths). - * - * \pre \a parent's computed values are already up-to-date. + * Gets called when the filter is (re)attached to the style */ void -sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent) +sp_style_filter_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) { - 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 - * (i.e., by assuming parent has initial values). - */ - if (!parent) - return; - - /* CSS2 */ - /* Font */ - sp_style_merge_font_size_from_parent(style->font_size, parent->font_size); - - /* 'font-style' */ - if (!style->font_style.set || style->font_style.inherit) { - style->font_style.computed = parent->font_style.computed; - } - - /* 'font-variant' */ - if (!style->font_variant.set || style->font_variant.inherit) { - style->font_variant.computed = parent->font_variant.computed; - } - - /* 'font-weight' */ - if (!style->font_weight.set || style->font_weight.inherit) { - 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 - * present. - */ - style->font_weight.computed = SP_CSS_FONT_WEIGHT_400; - } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) { - style->font_weight.computed = SP_CSS_FONT_WEIGHT_700; - } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_LIGHTER) { - unsigned const parent_val = parent->font_weight.computed; - g_assert(SP_CSS_FONT_WEIGHT_100 == 0); - // 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 - style->font_weight.computed = (parent_val <= SP_CSS_FONT_WEIGHT_100 + 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); - g_assert(style->font_weight.computed <= (unsigned) SP_CSS_FONT_WEIGHT_900); - } - - /* 'font-stretch' */ - if (!style->font_stretch.set || style->font_stretch.inherit) { - style->font_stretch.computed = parent->font_stretch.computed; - } 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); - 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); - g_assert(style->font_stretch.computed <= (unsigned) SP_CSS_FONT_STRETCH_ULTRA_EXPANDED); - } - - /* text (css2) */ - if (!style->text_indent.set || style->text_indent.inherit) { - style->text_indent.computed = parent->text_indent.computed; - } - - if (!style->text_align.set || style->text_align.inherit) { - style->text_align.computed = parent->text_align.computed; - } - - if (!style->text_decoration_line.set || style->text_decoration_line.inherit) { - style->text_decoration_line.underline = parent->text_decoration_line.underline; - style->text_decoration_line.overline = parent->text_decoration_line.overline; - style->text_decoration_line.line_through = parent->text_decoration_line.line_through; - style->text_decoration_line.blink = parent->text_decoration_line.blink; - } - - if (!style->text_decoration_style.set || style->text_decoration_style.inherit) { - style->text_decoration_style.solid = parent->text_decoration_style.solid; - style->text_decoration_style.isdouble = parent->text_decoration_style.isdouble; - style->text_decoration_style.dotted = parent->text_decoration_style.dotted; - style->text_decoration_style.dashed = parent->text_decoration_style.dashed; - style->text_decoration_style.wavy = parent->text_decoration_style.wavy; + if (old_ref) { + style->filter_modified_connection.disconnect(); } - - if (!style->text_decoration_color.set || style->text_decoration_color.inherit) { - sp_style_merge_ipaint(style, &style->text_decoration_color, &parent->text_decoration_color); + if ( SP_IS_FILTER(ref)) + { + style->filter_modified_connection = + ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_filter_ref_modified), style)); } - if (!style->line_height.set || style->line_height.inherit) { - style->line_height.value = parent->line_height.value; - style->line_height.computed = parent->line_height.computed; - style->line_height.normal = parent->line_height.normal; - } + sp_style_filter_ref_modified(ref, 0, style); +} - if (!style->letter_spacing.set || style->letter_spacing.inherit) { - style->letter_spacing.value = parent->letter_spacing.value; - style->letter_spacing.computed = parent->letter_spacing.computed; - style->letter_spacing.normal = parent->letter_spacing.normal; - } +/** + * Emit style modified signal on style's object if server is style's fill + * or stroke paint server. + */ +static void +sp_style_paint_server_ref_modified(SPObject *obj, guint flags, SPStyle *style) +{ + (void)flags; // TODO + SPPaintServer *server = static_cast<SPPaintServer *>(obj); - if (!style->word_spacing.set || style->word_spacing.inherit) { - style->word_spacing.value = parent->word_spacing.value; - style->word_spacing.computed = parent->word_spacing.computed; - style->word_spacing.normal = parent->word_spacing.normal; + if ((style->fill.isPaintserver()) + && style->getFillPaintServer() == server) + { + if (style->object) { + /** \todo + * 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 + * flag is only available downstreams. + */ + style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } + } else if ((style->stroke.isPaintserver()) + && style->getStrokePaintServer() == server) + { + if (style->object) { + /// \todo fixme: + style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } + } else if (server) { + g_assert_not_reached(); } +} - if (!style->text_transform.set || style->text_transform.inherit) { - style->text_transform.computed = parent->text_transform.computed; +/** + * Gets called when the paintserver is (re)attached to the style + */ +void +sp_style_fill_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) +{ + if (old_ref) { + style->fill_ps_modified_connection.disconnect(); } - - if (!style->direction.set || style->direction.inherit) { - style->direction.computed = parent->direction.computed; + if (SP_IS_PAINT_SERVER(ref)) { + style->fill_ps_modified_connection = + ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } - if (!style->block_progression.set || style->block_progression.inherit) { - style->block_progression.computed = parent->block_progression.computed; - } + sp_style_paint_server_ref_modified(ref, 0, style); +} - if (!style->writing_mode.set || style->writing_mode.inherit) { - style->writing_mode.computed = parent->writing_mode.computed; +/** + * Gets called when the paintserver is (re)attached to the style + */ +void +sp_style_stroke_paint_server_ref_changed(SPObject *old_ref, SPObject *ref, SPStyle *style) +{ + if (old_ref) { + style->stroke_ps_modified_connection.disconnect(); } - - if (!style->text_anchor.set || style->text_anchor.inherit) { - style->text_anchor.computed = parent->text_anchor.computed; + if (SP_IS_PAINT_SERVER(ref)) { + style->stroke_ps_modified_connection = + ref->connectModified(sigc::bind(sigc::ptr_fun(&sp_style_paint_server_ref_modified), style)); } - /* Baseline Shift... Some shifts are relative to parent. */ - sp_style_merge_baseline_shift_from_parent(style->baseline_shift, parent->baseline_shift, - parent->font_size); + sp_style_paint_server_ref_modified(ref, 0, style); +} - if (style->opacity.inherit) { - style->opacity.value = parent->opacity.value; - } +// Called in: desktop-style.cpp, gradient-chemistry.cpp, sp-object.cpp, sp-stop.cpp, style.cpp +// text-editing.cpp, libnrtype/font-lister.cpp, widgets/dash-selector.cpp, widgets/fill-style.cpp, +// widgets/stroke-style.cpp, widgets/text-toolbar.cpp, ui/dialog/glyphs.cpp, ui/dialog/swatches.cpp, +// ui/dialog/swatches.cpp, ui/dialog/text-edit.cpp. ui/tools/freehand-base.cpp, +// ui/widget/object-composite-settings.cpp, ui/widget/selected-style.cpp, ui/widget/style-swatch.cpp +/** + * Returns a new SPStyle object with default settings. + */ +SPStyle * +sp_style_new(SPDocument *document) +{ + SPStyle *const style = new SPStyle( document ); + return style; +} - /* Color */ - if (!style->color.set || style->color.inherit) { - sp_style_merge_ipaint(style, &style->color, &parent->color); - } - if (!style->color_interpolation.set || style->color_interpolation.inherit) { - style->color_interpolation.computed = parent->color_interpolation.computed; - } - if (!style->color_interpolation_filters.set || style->color_interpolation_filters.inherit) { - style->color_interpolation_filters.computed = parent->color_interpolation_filters.computed; - } +// Called in: sp-object.cpp +/** + * Creates a new SPStyle object, and attaches it to the specified SPObject. + */ +SPStyle * +sp_style_new_from_object(SPObject *object) +{ + g_return_val_if_fail(object != NULL, NULL); + g_return_val_if_fail(SP_IS_OBJECT(object), NULL); + SPStyle *const style = new SPStyle( NULL, object ); + return style; +} - /* Fill */ - if (!style->fill.set || style->fill.inherit || style->fill.currentcolor) { - sp_style_merge_ipaint(style, &style->fill, &parent->fill); - } +// Called in display/drawing-item.cpp, display/nr-filter-primitive.cpp, libnrtype/Layout-TNG-Input.cpp +/** + * Increase refcount of style. + */ +SPStyle * +sp_style_ref(SPStyle *style) +{ + g_return_val_if_fail(style != NULL, NULL); - if (!style->fill_opacity.set || style->fill_opacity.inherit) { - style->fill_opacity.value = parent->fill_opacity.value; - } + style->ref(); // Increase ref count - if (!style->fill_rule.set || style->fill_rule.inherit) { - style->fill_rule.computed = parent->fill_rule.computed; - } + return style; +} - /* Stroke */ - if (!style->stroke.set || style->stroke.inherit || style->stroke.currentcolor) { - sp_style_merge_ipaint(style, &style->stroke, &parent->stroke); +// Called in style.cpp, desktop-style.cpp, sp-object.cpp, sp-stop.cpp, text-editing.cpp +// display/drawing-group.cpp, ... +/** + * Decrease refcount of style with possible destruction. + */ +SPStyle * +sp_style_unref(SPStyle *style) +{ + g_return_val_if_fail(style != NULL, NULL); + if (style->unref() < 1) { + delete style; + return NULL; } + return style; +} - if (!style->stroke_width.set || style->stroke_width.inherit) { - style->stroke_width.computed = parent->stroke_width.computed; - } else { - /* Update computed value for any change in font inherited from parent. */ - double const em = style->font_size.computed; - if (style->stroke_width.unit == SP_CSS_UNIT_EM) { - style->stroke_width.computed = style->stroke_width.value * em; - } else if (style->stroke_width.unit == SP_CSS_UNIT_EX) { - double const ex = em * 0.5; // fixme: Get x height from libnrtype or pango. - style->stroke_width.computed = style->stroke_width.value * ex; - } - } - if (!style->stroke_linecap.set || style->stroke_linecap.inherit) { - style->stroke_linecap.computed = parent->stroke_linecap.computed; - } - if (!style->stroke_linejoin.set || style->stroke_linejoin.inherit) { - style->stroke_linejoin.computed = parent->stroke_linejoin.computed; - } +// Called in: sp-clippath.cpp, sp-item.cpp (suspicious), sp-object.cpp, sp-style-elem.cpp +/** + * 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) + */ +void +sp_style_read_from_object(SPStyle *style, SPObject *object) +{ + // std::cout << "sp_style_read_from_object: " << (object->getId()?object->getId():"null") << std::endl; + g_return_if_fail(style != NULL); + g_return_if_fail(object != NULL); + g_return_if_fail(SP_IS_OBJECT(object)); - if (!style->stroke_miterlimit.set || style->stroke_miterlimit.inherit) { - style->stroke_miterlimit.value = parent->stroke_miterlimit.value; - } + Inkscape::XML::Node *repr = object->getRepr(); + g_return_if_fail(repr != NULL); - if (!style->stroke_dasharray.set || style->stroke_dasharray.inherit) { - style->stroke_dasharray.values = parent->stroke_dasharray.values; - } + style->read( object, repr ); +} - if (!style->stroke_dashoffset.set || style->stroke_dashoffset.inherit) { - style->stroke_dashoffset.value = parent->stroke_dashoffset.value; - } +// Called in: libnrtype/font-lister.cpp, widgets/dash-selector.cpp, widgets/text-toolbar.cpp, +// ui/dialog/text-edit.cpp +// Why is this called when draging a gradient handle? +/** + * Read style properties from preferences. + * @param style The style to write to + * @param path Preferences directory from which the style should be read + */ +void +sp_style_read_from_prefs(SPStyle *style, Glib::ustring const &path) +{ + g_return_if_fail(style != NULL); + g_return_if_fail(path != ""); - if (!style->stroke_opacity.set || style->stroke_opacity.inherit) { - style->stroke_opacity.value = parent->stroke_opacity.value; - } + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - if (!style->paint_order.set || style->paint_order.inherit) { - g_free(style->paint_order.value); - style->paint_order.value = g_strdup(parent->paint_order.value); - for (unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i) { - style->paint_order.layer[i] = parent->paint_order.layer[i]; - style->paint_order.layer_set[i] = parent->paint_order.layer_set[i]; - } - } + // not optimal: we reconstruct the node based on the prefs, then pass it to + // sp_style_read for actual processing. + Inkscape::XML::SimpleDocument *tempdoc = new Inkscape::XML::SimpleDocument; + Inkscape::XML::Node *tempnode = tempdoc->createElement("prefs"); - if (style->text && parent->text) { - if (!style->text->font_family.set || style->text->font_family.inherit) { - g_free(style->text->font_family.value); - style->text->font_family.value = g_strdup(parent->text->font_family.value); - } + std::vector<Inkscape::Preferences::Entry> attrs = prefs->getAllEntries(path); + for (std::vector<Inkscape::Preferences::Entry>::iterator i = attrs.begin(); i != attrs.end(); ++i) { + tempnode->setAttribute(i->getEntryName().data(), i->getString().data()); } - if (style->text && parent->text) { - if (!style->text->font_specification.set || style->text->font_specification.inherit) { - g_free(style->text->font_specification.value); - style->text->font_specification.value = g_strdup(parent->text->font_specification.value); - } - } + style->read( NULL, tempnode ); - /* Markers - Free the old value and make copy of the new */ - for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) { - if (!style->marker[i].set || style->marker[i].inherit) { - g_free(style->marker[i].value); - style->marker[i].value = g_strdup(parent->marker[i].value); - } - } + Inkscape::GC::release(tempnode); + Inkscape::GC::release(tempdoc); + delete tempdoc; +} - /* Filter effects */ - if (style->filter.inherit) { - sp_style_merge_ifilter(style, &parent->filter); - } - if(style->enable_background.inherit) { - style->enable_background.value = parent->enable_background.value; - } +static CRSelEng * +sp_repr_sel_eng() +{ + CRSelEng *const ret = cr_sel_eng_new(); + cr_sel_eng_set_node_iface(ret, &Inkscape::XML::croco_node_iface); - /* Clipping */ - if (!style->clip_rule.set || style->clip_rule.inherit) { - style->clip_rule.computed = parent->clip_rule.computed; - } + /** \todo + * 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. + * 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: + * :hover, :active, :focus, :visited, :link. + */ - /* Rendering */ - if (!style->color_rendering.set || style->color_rendering.inherit) { - style->color_rendering.computed = parent->color_rendering.computed; - } - if (!style->image_rendering.set || style->image_rendering.inherit) { - style->image_rendering.computed = parent->image_rendering.computed; - } - if (!style->shape_rendering.set || style->shape_rendering.inherit) { - style->shape_rendering.computed = parent->shape_rendering.computed; - } - if (!style->text_rendering.set || style->text_rendering.inherit) { - style->text_rendering.computed = parent->text_rendering.computed; - } + g_assert(ret); + return ret; } -template <typename T> -static void -sp_style_merge_prop_from_dying_parent(T &child, T const &parent) -{ - if ( ( !(child.set) || child.inherit ) - && parent.set && !(parent.inherit) ) - { - child = parent; - } -} +// Called in text-editting.cpp, ui/tools/frehand-base.cpp, ui/widget/style-swatch.cpp /** - * Copy SPIString from parent to child. + * Parses a style="..." string and merges it with an existing SPStyle. */ -static void -sp_style_merge_string_prop_from_dying_parent(SPIString &child, SPIString const &parent) -{ - if ( ( !(child.set) || child.inherit ) - && parent.set && !(parent.inherit) ) - { - g_free(child.value); - child.value = g_strdup(parent.value); - child.set = parent.set; - child.inherit = parent.inherit; - } -} - -static void -sp_style_merge_paint_prop_from_dying_parent(SPStyle *style, - SPIPaint &child, SPIPaint const &parent) +void +sp_style_merge_from_style_string(SPStyle *const style, gchar const *const p) { - /** \todo - * I haven't given this much attention. See comments below about - * currentColor, colorProfile, and relative URIs. + // std::cout << "sp_style_merge_from_style_string: " << (p?p:"null") <<std::endl; + /* + * 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 + * attribute value? */ - if (!child.set || child.inherit) { - sp_style_merge_ipaint(style, &child, &parent); - child.set = parent.set; - child.inherit = parent.inherit; - } + style->_mergeString( p ); } -static void -sp_style_merge_rel_enum_prop_from_dying_parent(SPIEnum &child, SPIEnum const &parent, - unsigned const max_computed_val, - unsigned const smaller_val) -{ - /* We assume that min computed val is 0, contiguous up to max_computed_val, - then zero or more other absolute values, then smaller_val then larger_val. */ - unsigned const min_computed_val = 0; - unsigned const larger_val = smaller_val + 1; - g_return_if_fail(min_computed_val < max_computed_val); - g_return_if_fail(max_computed_val < smaller_val); - - if (parent.set && !parent.inherit) { - if (!child.set || child.inherit) { - child.value = parent.value; - child.set = parent.set; // i.e. true - child.inherit = parent.inherit; // i.e. false - } else if (child.value < smaller_val) { - /* Child has absolute value: leave as is. */ - } else if ( ( child.value == smaller_val - && parent.value == larger_val ) - || ( parent.value == smaller_val - && child.value == larger_val ) ) - { - 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 - * 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. - */ - } 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 - * 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 - * 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 - * newly-unlinked clone. - */ - } else { - unsigned const parent_val = parent.computed; - child.value = ( child.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(child.value <= max_computed_val); - child.inherit = false; - g_assert(child.set); - } - } -} - -template <typename LengthT> -static void -sp_style_merge_length_prop_from_dying_parent(LengthT &child, LengthT const &parent, - double const parent_child_em_ratio) -{ - if ( ( !(child.set) || child.inherit ) - && parent.set && !(parent.inherit) ) - { - child = parent; - switch (parent.unit) { - case SP_CSS_UNIT_EM: - case SP_CSS_UNIT_EX: - child.value *= parent_child_em_ratio; - /** \todo - * fixme: Have separate ex ratio parameter. - * Get x height from libnrtype or pango. - */ - if (!IS_FINITE(child.value)) { - child.value = child.computed; - child.unit = SP_CSS_UNIT_NONE; - } - break; - - default: - break; - } - } -} +/** Indexed by SP_CSS_FONT_SIZE_blah. These seem a bit small */ +static float const font_size_table[] = {6.0, 8.0, 10.0, 12.0, 14.0, 18.0, 24.0}; -static double -get_relative_font_size_frac(SPIFontSize const &font_size) +// Called in sp-object.cpp, sp-tref.cpp, sp-use.cpp +/** + * Sets computed values in \a style, which may involve inheriting from (or in some other way + * calculating from) corresponding computed values of \a parent. + * + * References: http://www.w3.org/TR/SVG11/propidx.html shows what properties inherit by default. + * http://www.w3.org/TR/SVG11/styling.html#Inheritance gives general rules as to what it means to + * inherit a value. http://www.w3.org/TR/REC-CSS2/cascade.html#computed-value is more precise + * about what the computed value is (not obvious for lengths). + * + * \pre \a parent's computed values are already up-to-date. + */ +void +sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent) { - switch (font_size.type) { - case SP_FONT_SIZE_LITERAL: { - switch (font_size.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 font_size.value; - - case SP_FONT_SIZE_LENGTH: { - switch (font_size.unit ) { - case SP_CSS_UNIT_EM: - return font_size.value; + // std::cout << "sp_style_merge_from_parent" << std::endl; + g_return_if_fail(style != NULL); - case SP_CSS_UNIT_EX: - return font_size.value * 0.5; + if (!parent) + return; - default: - g_assert_not_reached(); - } - } - } - g_assert_not_reached(); + style->cascade( parent ); + return; } +// Called in: sp-use.cpp, sp-tref.cpp, sp-item.cpp /** * Combine \a style and \a parent style specifications into a single style specification that * preserves (as much as possible) the effect of the existing \a style being a child of \a parent. @@ -2140,370 +1335,16 @@ get_relative_font_size_frac(SPIFontSize const &font_size) void sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const parent) { - /** \note - * The general rule for each property is as follows: - * - * If style is set to an absolute value, then leave it as is. - * - * Otherwise (i.e. if style has a relative value): - * - * If parent is set to an absolute value, then set style to the computed value. - * - * Otherwise, calculate the combined relative value (e.g. multiplying the two percentages). - */ - - /* We do font-size first, to ensure that em size is up-to-date. */ - /** \todo - * fixme: We'll need to have more font-related things up the top once - * we're getting x-height from pango or libnrtype. - */ - - /* Some things that allow relative specifications. */ - { - /* font-size. Note that we update the computed font-size of style, - to assist in em calculations later in this function. */ - - if (parent->font_size.set && !parent->font_size.inherit) { - /* Parent has defined font-size */ - - if (!style->font_size.set || style->font_size.inherit) { - /* font_size inherits the computed value, so we can use the parent value - * verbatim. */ - style->font_size = parent->font_size; - - } else if ( style->font_size.type == SP_FONT_SIZE_LENGTH && - style->font_size.unit != SP_CSS_UNIT_EM && - style->font_size.unit != SP_CSS_UNIT_EX ) { - - /* Child already has absolute size (stored in computed value), so do nothing. */ - - } else if ( style->font_size.type == SP_FONT_SIZE_LITERAL - && style->font_size.literal < SP_CSS_FONT_SIZE_SMALLER ) { - /* Child already has absolute size, but we ensure that the computed value - is up-to-date. */ - unsigned const ix = style->font_size.literal; - g_assert(ix < G_N_ELEMENTS(font_size_table)); - style->font_size.computed = font_size_table[ix]; - - } else { - /* Child has relative size. */ - double const child_frac(get_relative_font_size_frac(style->font_size)); - style->font_size.set = true; - style->font_size.inherit = false; - style->font_size.computed = parent->font_size.computed * child_frac; - - if ( ( parent->font_size.type == SP_FONT_SIZE_LITERAL - && parent->font_size.literal < SP_CSS_FONT_SIZE_SMALLER ) || - ( parent->font_size.type == SP_FONT_SIZE_LENGTH && - parent->font_size.unit != SP_CSS_UNIT_EM && - parent->font_size.unit != SP_CSS_UNIT_EX ) ) { - - /* Absolute value. */ - style->font_size.type = SP_FONT_SIZE_LENGTH; - /* .value is unused for non ex/em SP_FONT_SIZE_LENGTH. */ - - } else { - /* Relative value. */ - - double const parent_frac(get_relative_font_size_frac(parent->font_size)); - if( style->font_size.type == SP_FONT_SIZE_LENGTH ) { - /* Value in terms of ex/em */ - style->font_size.value *= parent_frac; - } else { - style->font_size.value = parent_frac * child_frac; - style->font_size.type = SP_FONT_SIZE_PERCENTAGE; - } - } - } - } - - /* 'font-stretch' */ - sp_style_merge_rel_enum_prop_from_dying_parent(style->font_stretch, - parent->font_stretch, - SP_CSS_FONT_STRETCH_ULTRA_EXPANDED, - SP_CSS_FONT_STRETCH_NARROWER); - - /* font-weight */ - sp_style_merge_rel_enum_prop_from_dying_parent(style->font_weight, - parent->font_weight, - SP_CSS_FONT_WEIGHT_900, - SP_CSS_FONT_WEIGHT_LIGHTER); - } - - - /* Enum values that don't have any relative settings (other than `inherit'). */ - { - SPIEnum SPStyle::*const fields[] = { - &SPStyle::blend_mode, - &SPStyle::clip_rule, - &SPStyle::color_interpolation, - &SPStyle::color_interpolation_filters, - &SPStyle::color_rendering, - &SPStyle::direction, - &SPStyle::fill_rule, - &SPStyle::font_style, - &SPStyle::font_variant, - &SPStyle::image_rendering, - &SPStyle::isolation, - //nyi: SPStyle::pointer_events, - &SPStyle::shape_rendering, - &SPStyle::stroke_linecap, - &SPStyle::stroke_linejoin, - &SPStyle::text_anchor, - &SPStyle::text_rendering, - &SPStyle::visibility, - &SPStyle::writing_mode - }; - - for (unsigned i = 0; i < G_N_ELEMENTS(fields); ++i) { - SPIEnum SPStyle::*const fld = fields[i]; - sp_style_merge_prop_from_dying_parent<SPIEnum>(style->*fld, parent->*fld); - } - } - - /* A few other simple inheritance properties. */ - { - sp_style_merge_prop_from_dying_parent<SPIScale24>(style->fill_opacity, parent->fill_opacity); - sp_style_merge_prop_from_dying_parent<SPIScale24>(style->stroke_opacity, parent->stroke_opacity); - sp_style_merge_prop_from_dying_parent<SPIFloat>(style->stroke_miterlimit, parent->stroke_miterlimit); - - /** \todo - * We currently treat text-decoration as if it were a simple inherited - * property (fixme). This code may need changing once we do the - * special fill/stroke inheritance mentioned by the spec. - */ - sp_style_merge_prop_from_dying_parent<SPITextDecorationLine>( style->text_decoration_line, parent->text_decoration_line); - sp_style_merge_prop_from_dying_parent<SPITextDecorationStyle>(style->text_decoration_style, parent->text_decoration_style); - sp_style_merge_paint_prop_from_dying_parent(style,style->text_decoration_color, parent->text_decoration_color); - - //nyi: font-size-adjust, // <number> | none | inherit - //nyi: glyph-orientation-horizontal, - //nyi: glyph-orientation-vertical, - } - - /* Properties that involve length but are easy in other respects. */ - { - /* The difficulty with lengths is that font-relative units need adjusting if the font - * varies between parent & child. - * - * Lengths specified in the existing child can stay as they are: its computed font - * specification should stay unchanged, so em & ex lengths should continue to mean the same - * size. - * - * Lengths specified in the dying parent in em or ex need to be scaled according to the - * ratio of em or ex size between parent & child. - */ - double const parent_child_em_ratio = parent->font_size.computed / style->font_size.computed; - - SPILength SPStyle::*const lfields[] = { - &SPStyle::stroke_width, - &SPStyle::text_indent - }; - for (unsigned i = 0; i < G_N_ELEMENTS(lfields); ++i) { - SPILength SPStyle::*fld = lfields[i]; - sp_style_merge_length_prop_from_dying_parent<SPILength>(style->*fld, - parent->*fld, - parent_child_em_ratio); - } - - SPILengthOrNormal SPStyle::*const nfields[] = { - &SPStyle::letter_spacing, - &SPStyle::line_height, - &SPStyle::word_spacing - }; - for (unsigned i = 0; i < G_N_ELEMENTS(nfields); ++i) { - SPILengthOrNormal SPStyle::*fld = nfields[i]; - sp_style_merge_length_prop_from_dying_parent<SPILengthOrNormal>(style->*fld, - parent->*fld, - parent_child_em_ratio); - } - - //nyi: &SPStyle::kerning: length or `auto' - - /* fixme: Move stroke-dash and stroke-dash-offset here once they - can accept units. */ - } - - /* Properties that involve a URI but are easy in other respects. */ - { - /** \todo - * Could cause problems if original object was in another document - * and it used a relative URL. (At the time of writing, we don't - * allow hrefs to other documents, so this isn't a problem yet.) - * Paint properties also allow URIs. - */ - //nyi: cursor, // may involve change in effect, but we can't do much better - //nyi: color-profile, - - // Markers (marker-start, marker-mid, marker-end). - for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) { - sp_style_merge_string_prop_from_dying_parent(style->marker[i], parent->marker[i]); - } - } - - /* CSS2 */ - /* Font */ - - if (style->text && parent->text) { - sp_style_merge_string_prop_from_dying_parent(style->text->font_specification, - parent->text->font_specification); - - sp_style_merge_string_prop_from_dying_parent(style->text->font_family, - parent->text->font_family); - } - - - /* Properties that don't inherit by default. Most of these need special handling. */ - { - /* - * opacity's effect is cumulative; we set the new value to the combined effect. The - * default value for opacity is 1.0, not inherit. (Note that stroke-opacity and - * fill-opacity are quite different from opacity, and don't need any special handling.) - * - * Cases: - * - parent & child were each previously unset, in which case the effective - * opacity value is 1.0, and style should remain unset. - * - parent was previously unset (so computed opacity value of 1.0) - * and child was set to inherit. The merged child should - * get a value of 1.0, and shouldn't inherit (lest the new parent - * has a different opacity value). Given that opacity's default - * value is 1.0 (rather than inherit), we might as well have the - * merged child's opacity be unset. - * - parent was previously unset (so opacity 1.0), and child was set to a number. - * The merged child should retain its existing settings (though it doesn't matter - * if we make it unset if that number was 1.0). - * - parent was inherit and child was unset. Merged child should be set to inherit. - * - parent was inherit and child was inherit. (We can't in general reproduce this - * effect (short of introducing a new group), but setting opacity to inherit is rare.) - * If the inherited value was strictly between 0.0 and 1.0 (exclusive) then the merged - * child's value should be set to the product of the two, i.e. the square of the - * inherited value, and should not be marked as inherit. (This decision assumes that it - * is more important to retain the effective opacity than to retain the inheriting - * effect, and assumes that the inheriting effect either isn't important enough to create - * a group or isn't common enough to bother maintaining the code to create a group.) If - * the inherited value was 0.0 or 1.0, then marking the merged child as inherit comes - * closer to maintaining the effect. - * - parent was inherit and child was set to a numerical value. If the child's value - * was 1.0, then the merged child should have the same settings as the parent. - * If the child's value was 0, then the merged child should also be set to 0. - * If the child's value was anything else, then we do the same as for the inherit/inherit - * case above: have the merged child set to the product of the two opacities and not - * marked as inherit, for the same reasons as for that case. - * - parent was set to a value, and child was unset. The merged child should have - * parent's settings. - * - parent was set to a value, and child was inherit. The merged child should - * be set to the product, i.e. the square of the parent's value. - * - parent & child are each set to a value. The merged child should be set to the - * product. - */ - if ( !style->opacity.set - || ( !style->opacity.inherit - && style->opacity.value == SP_SCALE24_MAX ) ) - { - style->opacity = parent->opacity; - } else { - /* Ensure that style's computed value is up-to-date. */ - if (style->opacity.inherit) { - style->opacity.value = parent->opacity.value; - } - - /* Multiplication of opacities occurs even if a child's opacity is set to inherit. */ - style->opacity.value = SP_SCALE24_MUL(style->opacity.value, - parent->opacity.value); - - style->opacity.inherit = (parent->opacity.inherit - && style->opacity.inherit - && (parent->opacity.value == 0 || - parent->opacity.value == SP_SCALE24_MAX)); - style->opacity.set = ( style->opacity.inherit - || style->opacity.value < SP_SCALE24_MAX ); - } - - /* display is in principle similar to opacity, but implementation is easier. */ - if ( parent->display.set && !parent->display.inherit - && parent->display.value == SP_CSS_DISPLAY_NONE ) { - style->display.value = SP_CSS_DISPLAY_NONE; - style->display.set = true; - style->display.inherit = false; - } else if (style->display.inherit) { - style->display.value = parent->display.value; - style->display.set = parent->display.set; - style->display.inherit = parent->display.inherit; - } else { - /* Leave as is. (display doesn't inherit by default.) */ - } - - /* enable-background - this is rather complicated, because - * it is valid only when applied to container elements. - * Let's check a simple case anyhow. */ - if (parent->enable_background.set - && !parent->enable_background.inherit - && style->enable_background.inherit) - { - style->enable_background.set = true; - style->enable_background.inherit = false; - style->enable_background.value = parent->enable_background.value; - } - - if (!style->filter.set || style->filter.inherit) - { - sp_style_merge_ifilter(style, &parent->filter); - + // std::cout << "sp_style_merge_from_dying_parent" << std::endl; + style->merge( parent ); } - /** \todo - * fixme: Check that we correctly handle all properties that don't - * inherit by default (as shown in - * http://www.w3.org/TR/SVG11/propidx.html for most SVG 1.1 properties). - */ - } - - /* SPIPaint properties (including color). */ - { - /** \todo - * Think about the issues involved if specified as currentColor or - * if specified relative to colorProfile, and if the currentColor or - * colorProfile differs between parent \& child. See also comments - * elsewhere in this function about URIs. - */ - SPIPaint SPStyle::*const fields[] = { - &SPStyle::color, - &SPStyle::fill, - &SPStyle::stroke - }; - for (unsigned i = 0; i < G_N_ELEMENTS(fields); ++i) { - SPIPaint SPStyle::*const fld = fields[i]; - sp_style_merge_paint_prop_from_dying_parent(style, style->*fld, parent->*fld); - } - } - - /* Things from SVG 1.2 or CSS3. */ - { - /* Note: If we ever support setting string values for text-align then we'd need strdup - * handling here. */ - sp_style_merge_prop_from_dying_parent<SPIEnum>(style->text_align, parent->text_align); - - sp_style_merge_prop_from_dying_parent<SPIEnum>(style->text_transform, parent->text_transform); - sp_style_merge_prop_from_dying_parent<SPIEnum>(style->block_progression, parent->block_progression); - } - - /* Note: this will need length handling once dasharray supports units. */ - if ( ( !style->stroke_dasharray.set || style->stroke_dasharray.inherit ) - && parent->stroke_dasharray.set && !parent->stroke_dasharray.inherit ) - { - style->stroke_dasharray.values = parent->stroke_dasharray.values; - style->stroke_dasharray.set = parent->stroke_dasharray.set; - style->stroke_dasharray.inherit = parent->stroke_dasharray.inherit; - } - { - sp_style_merge_prop_from_dying_parent<SPILength>(style->stroke_dashoffset, parent->stroke_dashoffset); - } -} - - -static void +// The following functions should be incorporated into SPIPaint. FIXME +// Called in: style.cpp, style-internal.cpp +void sp_style_set_ipaint_to_uri(SPStyle *style, SPIPaint *paint, const Inkscape::URI *uri, SPDocument *document) { + // std::cout << "sp_style_set_ipaint_to_uri: Entrance: " << uri << " " << (void*)document << std::endl; // it may be that this style's SPIPaint has not yet created its URIReference; // now that we have a document, we can create it here if (!paint->value.href && document) { @@ -2525,7 +1366,8 @@ sp_style_set_ipaint_to_uri(SPStyle *style, SPIPaint *paint, const Inkscape::URI } } -static void +// Called in: style.cpp, style-internal.cpp +void sp_style_set_ipaint_to_uri_string (SPStyle *style, SPIPaint *paint, const gchar *uri) { try { @@ -2536,12 +1378,14 @@ sp_style_set_ipaint_to_uri_string (SPStyle *style, SPIPaint *paint, const gchar } } +// Called in: desktop-style.cpp void sp_style_set_to_uri_string (SPStyle *style, bool isfill, const gchar *uri) { sp_style_set_ipaint_to_uri_string (style, isfill? &style->fill : &style->stroke, uri); } +// Called in: widgets/font-selector.cpp, widgets/text-toolbar.cpp, ui/dialog/text-edit.cpp gchar const * sp_style_get_css_unit_string(int unit) { @@ -2564,6 +1408,7 @@ sp_style_get_css_unit_string(int unit) return "px"; } +// Called in: style-internal.cpp, widgets/text-toolbar.cpp, ui/dialog/text-edit.cpp /* * Convert a size in pixels into another CSS unit size */ @@ -2592,6 +1437,7 @@ sp_style_css_size_px_to_units(double size, int unit) return unit_size; } +// Called in: widgets/text-toolbar.cpp, ui/dialog/text-edit.cpp /* * Convert a size in a CSS unit size to pixels */ @@ -2604,82 +1450,8 @@ sp_style_css_size_units_to_px(double size, int unit) //g_message("sp_style_css_size_units_to_px %f %d = %f px", size, unit, out); return size * (size / sp_style_css_size_px_to_units(size, unit));; } -/** - * - */ -static void -sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent) -{ - if ((paint->set && paint->currentcolor) || parent->currentcolor) { - bool isset = paint->set; - paint->clear(); - paint->set = isset; - paint->currentcolor = TRUE; - paint->setColor(style->color.value.color); - return; - } - - paint->clear(); - if ( parent->isPaintserver() ) { - if (parent->value.href) { - sp_style_set_ipaint_to_uri(style, paint, parent->value.href->getURI(), parent->value.href->getOwnerDocument()); - } else { - g_warning("Expected paint server not found."); - } - } else if ( parent->isColor() ) { - paint->setColor( parent->value.color ); - } else if ( parent->isNoneSet() ) { - paint->noneSet = TRUE; - } else if ( parent->isNone() ) { - // - } else { - g_assert_not_reached(); - } -} - - -/** - * Merge filter style from parent. - * Filter effects do not inherit by default - */ -static void -sp_style_merge_ifilter(SPStyle *style, SPIFilter const *parent) -{ - // FIXME: - // instead of just copying over, we need to _really merge_ the two filters by combining their - // filter primitives - - sp_style_filter_clear(style); - style->filter.set = parent->set; - style->filter.inherit = parent->inherit; - - if (style->filter.href){ - if (style->filter.href->getObject()){ - style->filter.href->detach(); - } - } - else{ - // it may be that this style has not yet created its SPFilterReference - if (style->object){ - if (style->object->document) { - style->filter.href = new SPFilterReference(style->object->document); - style->filter.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); - } - } - } - - if (style->filter.href && parent->href){ - if (parent->href->getObject()) { - try { - style->filter.href->attach(*parent->href->getURI()); - } catch (Inkscape::BadURIException &e) { - g_warning("%s", e.what()); - style->filter.href->detach(); - } - } - } -} +// Called in style.cpp, text-editing.cpp /** * 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 @@ -2702,146 +1474,11 @@ sp_style_write_string(SPStyle const *const style, guint const flags) (flags == SP_STYLE_FLAG_ALWAYS) ), NULL); - gchar c[BMAX]; - gchar *p = c; - *p = '\0'; - - p += sp_style_write_ifontsize(p, c + BMAX - p, "font-size", &style->font_size, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "font-style", enum_font_style, &style->font_style, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "font-variant", enum_font_variant, &style->font_variant, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "font-weight", enum_font_weight, &style->font_weight, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "font-stretch", enum_font_stretch, &style->font_stretch, NULL, flags); - - /* Text */ - p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &style->text_indent, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &style->text_align, NULL, flags); - p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", - &style->text_decoration_line, &style->text_decoration_style, &style->text_decoration_color, - NULL, NULL, NULL, flags); - p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &style->line_height, NULL, flags); - p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &style->letter_spacing, NULL, flags); - p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &style->word_spacing, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "text-transform", enum_text_transform, &style->text_transform, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "direction", enum_direction, &style->direction, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "block-progression", enum_block_progression, &style->block_progression, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &style->writing_mode, NULL, flags); - - p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &style->text_anchor, NULL, flags); - p += sp_style_write_ibaselineshift(p, c + BMAX - p, "baseline-shift", &style->baseline_shift, NULL, flags); - - - /// \todo fixme: Per type methods need default flag too (lauris) - - if (style->opacity.value != SP_SCALE24_MAX) { - p += sp_style_write_iscale24(p, c + BMAX - p, "opacity", &style->opacity, NULL, flags); - } - - if (!style->color.noneSet) { // CSS does not permit "none" for color - p += sp_style_write_ipaint(p, c + BMAX - p, "color", &style->color, NULL, flags); - } - p += sp_style_write_ienum(p, c + BMAX - p, "color-interpolation", enum_color_interpolation, &style->color_interpolation, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "color-interpolation-filters", enum_color_interpolation, &style->color_interpolation_filters, NULL, flags); - - - p += sp_style_write_ipaint(p, c + BMAX - p, "fill", &style->fill, NULL, flags); - // if fill:none, skip writing fill properties - if (!style->fill.noneSet) { - p += sp_style_write_iscale24(p, c + BMAX - p, "fill-opacity", &style->fill_opacity, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "fill-rule", enum_fill_rule, &style->fill_rule, NULL, flags); - } - - p += sp_style_write_ipaint(p, c + BMAX - p, "stroke", &style->stroke, NULL, flags); - - // stroke width affects markers, so write it if there's stroke OR any markers - if (!style->stroke.noneSet || - style->marker[SP_MARKER_LOC].set || - style->marker[SP_MARKER_LOC_START].set || - style->marker[SP_MARKER_LOC_MID].set || - style->marker[SP_MARKER_LOC_END].set) { - p += sp_style_write_ilength(p, c + BMAX - p, "stroke-width", &style->stroke_width, NULL, flags); - } - - // if stroke:none, skip writing stroke properties - if (!style->stroke.noneSet) { - p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linecap", enum_stroke_linecap, &style->stroke_linecap, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linejoin", enum_stroke_linejoin, &style->stroke_linejoin, NULL, flags); - p += sp_style_write_ifloat(p, c + BMAX - p, "stroke-miterlimit", &style->stroke_miterlimit, NULL, flags); - p += sp_style_write_iscale24(p, c + BMAX - p, "stroke-opacity", &style->stroke_opacity, NULL, flags); - p += sp_style_write_idasharray(p, c + BMAX - p, "stroke-dasharray", &style->stroke_dasharray, NULL, flags); - p += sp_style_write_ilength(p, c + BMAX - p, "stroke-dashoffset", &style->stroke_dashoffset, NULL, flags); - } - - if (style->paint_order.set) { - p += sp_style_write_ipaintorder(p, c + BMAX - p, "paint-order", &style->paint_order, NULL, flags); - } else if (flags == SP_STYLE_FLAG_ALWAYS) { - p += g_snprintf(p, c + BMAX - p, "paint-order:normal;"); - } - - bool marker_none = false; - gchar *master = style->marker[SP_MARKER_LOC].value; - if (style->marker[SP_MARKER_LOC].set) { - p += g_snprintf(p, c + BMAX - p, "marker:%s;", style->marker[SP_MARKER_LOC].value); - } else if (flags == SP_STYLE_FLAG_ALWAYS) { - p += g_snprintf(p, c + BMAX - p, "marker:none;"); - marker_none = true; - } - if (style->marker[SP_MARKER_LOC_START].set - && (!master || strcmp(master, style->marker[SP_MARKER_LOC_START].value))) { - p += g_snprintf(p, c + BMAX - p, "marker-start:%s;", style->marker[SP_MARKER_LOC_START].value); - } else if (flags == SP_STYLE_FLAG_ALWAYS && !marker_none) { - p += g_snprintf(p, c + BMAX - p, "marker-start:none;"); - } - if (style->marker[SP_MARKER_LOC_MID].set - && (!master || strcmp(master, style->marker[SP_MARKER_LOC_MID].value))) { - p += g_snprintf(p, c + BMAX - p, "marker-mid:%s;", style->marker[SP_MARKER_LOC_MID].value); - } else if (flags == SP_STYLE_FLAG_ALWAYS && !marker_none) { - p += g_snprintf(p, c + BMAX - p, "marker-mid:none;"); - } - if (style->marker[SP_MARKER_LOC_END].set - && (!master || strcmp(master, style->marker[SP_MARKER_LOC_END].value))) { - p += g_snprintf(p, c + BMAX - p, "marker-end:%s;", style->marker[SP_MARKER_LOC_END].value); - } else if (flags == SP_STYLE_FLAG_ALWAYS && !marker_none) { - p += g_snprintf(p, c + BMAX - p, "marker-end:none;"); - } - - p += sp_style_write_ienum(p, c + BMAX - p, "visibility", enum_visibility, &style->visibility, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "display", enum_display, &style->display, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "overflow", enum_overflow, &style->overflow, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "isolation", enum_isolation, &style->isolation, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "mix-blend-mode", enum_blend_mode, &style->blend_mode, NULL, flags); - - /* filter: */ - p += sp_style_write_ifilter(p, c + BMAX - p, "filter", &style->filter, NULL, flags); - - p += sp_style_write_ienum(p, c + BMAX - p, "enable-background", enum_enable_background, &style->enable_background, NULL, flags); - - /* clipping */ - p += sp_style_write_ienum(p, c + BMAX - p, "clip-rule", enum_clip_rule, &style->clip_rule, NULL, flags); - - /* rendering */ - p += sp_style_write_ienum(p, c + BMAX - p, "color-rendering", enum_color_rendering, &style->color_rendering, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "image-rendering", enum_image_rendering, &style->image_rendering, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "shape-rendering", enum_shape_rendering, &style->shape_rendering, NULL, flags); - p += sp_style_write_ienum(p, c + BMAX - p, "text-rendering", enum_text_rendering, &style->text_rendering, NULL, flags); - - /* fixme: */ - p += sp_text_style_write(p, c + BMAX - p, style->text, flags); - - /* Get rid of trailing `;'. */ - if (p != c) { - --p; - if (*p == ';') { - *p = '\0'; - } - } - - return g_strdup(c); + return g_strdup( style->write( flags ).c_str() ); } -#define STYLE_BUF_MAX - - +// Called in style.cpp, path-chemistry, NOT in text-editting.cpp (because of bug) /** * Dumps style to CSS string, see sp_style_write_string() * @@ -2855,2016 +1492,7 @@ sp_style_write_difference(SPStyle const *const from, SPStyle const *const to) g_return_val_if_fail(from != NULL, NULL); g_return_val_if_fail(to != NULL, NULL); - gchar c[BMAX], *p = c; - *p = '\0'; - - p += sp_style_write_ifontsize(p, c + BMAX - p, "font-size", &from->font_size, &to->font_size, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "font-style", enum_font_style, &from->font_style, &to->font_style, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "font-variant", enum_font_variant, &from->font_variant, &to->font_variant, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "font-weight", enum_font_weight, &from->font_weight, &to->font_weight, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "font-stretch", enum_font_stretch, &from->font_stretch, &to->font_stretch, SP_STYLE_FLAG_IFDIFF); - - /* Text */ - p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &from->text_indent, &to->text_indent, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &from->text_align, &to->text_align, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", - &from->text_decoration_line, &from->text_decoration_style, &from->text_decoration_color, - &to->text_decoration_line, &to->text_decoration_style, &to->text_decoration_color, - SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &from->line_height, &to->line_height, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &from->letter_spacing, &to->letter_spacing, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &from->word_spacing, &to->word_spacing, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "text-transform", enum_text_transform, &from->text_transform, &to->text_transform, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "direction", enum_direction, &from->direction, &to->direction, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "block-progression", enum_block_progression, &from->block_progression, &to->block_progression, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &from->writing_mode, &to->writing_mode, SP_STYLE_FLAG_IFDIFF); - - p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &from->text_anchor, &to->text_anchor, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ibaselineshift(p, c + BMAX - p, "baseline-shift", &from->baseline_shift, &to->baseline_shift, SP_STYLE_FLAG_IFDIFF); - - /// \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); - } - - if (!from->color.noneSet) { // CSS does not permit "none" for color - p += sp_style_write_ipaint(p, c + BMAX - p, "color", &from->color, &to->color, SP_STYLE_FLAG_IFSET); - } - p += sp_style_write_ienum(p, c + BMAX - p, "color-interpolation", enum_color_interpolation, &from->color_interpolation, &to->color_interpolation, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "color-interpolation-filters", enum_color_interpolation, &from->color_interpolation_filters, &to->color_interpolation_filters, SP_STYLE_FLAG_IFDIFF); - - p += sp_style_write_ipaint(p, c + BMAX - p, "fill", &from->fill, &to->fill, SP_STYLE_FLAG_IFDIFF); - // if fill:none, skip writing fill properties - if (!from->fill.noneSet) { - p += sp_style_write_iscale24(p, c + BMAX - p, "fill-opacity", &from->fill_opacity, &to->fill_opacity, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "fill-rule", enum_fill_rule, &from->fill_rule, &to->fill_rule, SP_STYLE_FLAG_IFDIFF); - } - - p += sp_style_write_ipaint(p, c + BMAX - p, "stroke", &from->stroke, &to->stroke, SP_STYLE_FLAG_IFDIFF); - - // stroke width affects markers, so write it if there's stroke OR any markers - if (!from->stroke.noneSet || - from->marker[SP_MARKER_LOC].set || - from->marker[SP_MARKER_LOC_START].set || - from->marker[SP_MARKER_LOC_MID].set || - from->marker[SP_MARKER_LOC_END].set) { - p += sp_style_write_ilength(p, c + BMAX - p, "stroke-width", &from->stroke_width, &to->stroke_width, SP_STYLE_FLAG_IFDIFF); - } - - // if stroke:none, skip writing stroke properties - if (!from->stroke.noneSet) { - p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linecap", enum_stroke_linecap, - &from->stroke_linecap, &to->stroke_linecap, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linejoin", enum_stroke_linejoin, - &from->stroke_linejoin, &to->stroke_linejoin, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ifloat(p, c + BMAX - p, "stroke-miterlimit", - &from->stroke_miterlimit, &to->stroke_miterlimit, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_idasharray(p, c + BMAX - p, "stroke-dasharray", - &from->stroke_dasharray, &to->stroke_dasharray, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ilength(p, c + BMAX - p, "stroke-dashoffset", &from->stroke_dashoffset, &to->stroke_dashoffset, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_iscale24(p, c + BMAX - p, "stroke-opacity", &from->stroke_opacity, &to->stroke_opacity, SP_STYLE_FLAG_IFDIFF); - } - - /* paint-order */ - if( from->paint_order.set) { - p += sp_style_write_ipaintorder(p, c + BMAX - p, "paint-order", &from->paint_order, &to->paint_order, SP_STYLE_FLAG_IFDIFF); - } - - /* markers */ - gchar *master = from->marker[SP_MARKER_LOC].value; - if (master != NULL) { - p += g_snprintf(p, c + BMAX - p, "marker:%s;", master); - } - if (from->marker[SP_MARKER_LOC_START].value != NULL && (!master || strcmp(master, from->marker[SP_MARKER_LOC_START].value))) { - p += g_snprintf(p, c + BMAX - p, "marker-start:%s;", from->marker[SP_MARKER_LOC_START].value); - } - if (from->marker[SP_MARKER_LOC_MID].value != NULL && (!master || strcmp(master, from->marker[SP_MARKER_LOC_MID].value))) { - p += g_snprintf(p, c + BMAX - p, "marker-mid:%s;", from->marker[SP_MARKER_LOC_MID].value); - } - if (from->marker[SP_MARKER_LOC_END].value != NULL && (!master || strcmp(master, from->marker[SP_MARKER_LOC_END].value))) { - p += g_snprintf(p, c + BMAX - p, "marker-end:%s;", from->marker[SP_MARKER_LOC_END].value); - } - - p += sp_style_write_ienum(p, c + BMAX - p, "visibility", enum_visibility, &from->visibility, &to->visibility, SP_STYLE_FLAG_IFSET); - p += sp_style_write_ienum(p, c + BMAX - p, "display", enum_display, &from->display, &to->display, SP_STYLE_FLAG_IFSET); - p += sp_style_write_ienum(p, c + BMAX - p, "overflow", enum_overflow, &from->overflow, &to->overflow, SP_STYLE_FLAG_IFSET); - p += sp_style_write_ienum(p, c + BMAX - p, "isolation", enum_isolation, &from->isolation, &to->isolation, SP_STYLE_FLAG_IFSET); - p += sp_style_write_ienum(p, c + BMAX - p, "mix-blend-mode", enum_blend_mode, &from->blend_mode, &to->blend_mode, SP_STYLE_FLAG_IFSET); - - /* filter: */ - p += sp_style_write_ifilter(p, c + BMAX - p, "filter", &from->filter, &to->filter, SP_STYLE_FLAG_IFDIFF); - - p += sp_style_write_ienum(p, c + BMAX - p, "enable-background", enum_enable_background, &from->enable_background, &to->enable_background, SP_STYLE_FLAG_IFSET); - - p += sp_text_style_write(p, c + BMAX - p, from->text, SP_STYLE_FLAG_IFDIFF); - - p += sp_style_write_ienum(p, c + BMAX - p, "clip-rule", enum_clip_rule, &from->clip_rule, &to->clip_rule, SP_STYLE_FLAG_IFDIFF); - - /* rendering */ - p += sp_style_write_ienum(p, c + BMAX - p, "color-rendering", enum_color_rendering, &from->color_rendering, &to->color_rendering, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "image-rendering", enum_image_rendering, &from->image_rendering, &to->image_rendering, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "shape-rendering", enum_shape_rendering, &from->shape_rendering, &to->shape_rendering, SP_STYLE_FLAG_IFDIFF); - p += sp_style_write_ienum(p, c + BMAX - p, "text-rendering", enum_text_rendering, &from->text_rendering, &to->text_rendering, SP_STYLE_FLAG_IFDIFF); - - /** \todo - * The reason we use IFSET rather than IFDIFF is the belief that the IFDIFF - * flag is mainly only for attributes that don't handle explicit unset well. - * We may need to revisit the behaviour of this routine. - */ - - /* Get rid of trailing `;'. */ - if (p != c) { - --p; - if (*p == ';') { - *p = '\0'; - } - } - - return g_strdup(c); -} - - - -/** - * Reset all style properties. - */ -static void -sp_style_clear(SPStyle *style) -{ - g_return_if_fail(style != NULL); - - style->fill.clear(); - style->stroke.clear(); - sp_style_filter_clear(style); - - style->release_connection.disconnect(); - - style->fill_ps_modified_connection.disconnect(); - if (style->fill.value.href) { - delete style->fill.value.href; - style->fill.value.href = NULL; - } - style->stroke_ps_modified_connection.disconnect(); - if (style->stroke.value.href) { - delete style->stroke.value.href; - style->stroke.value.href = NULL; - } - style->filter_modified_connection.disconnect(); - if (style->filter.href) { - delete style->filter.href; - style->filter.href = NULL; - } - - style->stroke_dasharray.values.clear(); - style->stroke_dasharray.inherit = FALSE; - style->stroke_dashoffset.inherit = FALSE; - - /** \todo fixme: Do that text manipulation via parents */ - SPObject *object = style->object; - SPDocument *document = style->document; - gint const refcount = style->refcount; - SPTextStyle *text = style->text; - unsigned const text_private = style->text_private; - - - style->refcount = refcount; - style->object = object; - style->document = document; - - if (document) { - style->filter.href = new SPFilterReference(document); - style->filter.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); - - style->fill.value.href = new SPPaintServerReference(document); - style->fill.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_fill_paint_server_ref_changed), style)); - - style->stroke.value.href = new SPPaintServerReference(document); - style->stroke.value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_stroke_paint_server_ref_changed), style)); - } - - style->text = text; - style->text_private = text_private; - - style->text->font_specification.set = FALSE; - style->text->font.set = FALSE; - style->text->font_family.set = FALSE; - - style->font_size.set = FALSE; - style->font_size.inherit = FALSE; - style->font_size.type = SP_FONT_SIZE_LITERAL; - style->font_size.unit = 0; - style->font_size.literal = SP_CSS_FONT_SIZE_MEDIUM; - style->font_size.value = 12.0; - style->font_size.computed = 12.0; - style->font_style.set = FALSE; - style->font_style.inherit = FALSE; - style->font_style.value = style->font_style.computed = SP_CSS_FONT_STYLE_NORMAL; - style->font_variant.set = FALSE; - style->font_variant.inherit = FALSE; - style->font_variant.value = style->font_variant.computed = SP_CSS_FONT_VARIANT_NORMAL; - style->font_weight.set = FALSE; - style->font_weight.inherit = FALSE; - style->font_weight.value = SP_CSS_FONT_WEIGHT_NORMAL; - style->font_weight.computed = SP_CSS_FONT_WEIGHT_400; - style->font_stretch.set = FALSE; - style->font_stretch.inherit = FALSE; - style->font_stretch.value = style->font_stretch.computed = SP_CSS_FONT_STRETCH_NORMAL; - - /* text */ - style->text_indent.set = FALSE; - style->text_indent.inherit = FALSE; - style->text_indent.unit = SP_CSS_UNIT_NONE; - style->text_indent.computed = 0.0; - - style->text_align.set = FALSE; - style->text_align.inherit = FALSE; - style->text_align.value = style->text_align.computed = SP_CSS_TEXT_ALIGN_START; - - style->text_decoration_line.set = FALSE; - style->text_decoration_line.inherit = FALSE; - style->text_decoration_line.underline = FALSE; - style->text_decoration_line.overline = FALSE; - style->text_decoration_line.line_through = FALSE; - style->text_decoration_line.blink = FALSE; - - style->text_decoration_style.set = FALSE; - style->text_decoration_style.inherit = FALSE; - style->text_decoration_style.solid = FALSE; - style->text_decoration_style.isdouble = FALSE; - style->text_decoration_style.dotted = FALSE; - style->text_decoration_style.dashed = FALSE; - style->text_decoration_style.wavy = FALSE; - - style->text_decoration_color.clear(); - - style->line_height.set = FALSE; - style->line_height.inherit = FALSE; - style->line_height.unit = SP_CSS_UNIT_PERCENT; - style->line_height.normal = TRUE; - style->line_height.value = style->line_height.computed = 1.0; - - style->letter_spacing.set = FALSE; - style->letter_spacing.inherit = FALSE; - style->letter_spacing.unit = SP_CSS_UNIT_NONE; - style->letter_spacing.normal = TRUE; - style->letter_spacing.value = style->letter_spacing.computed = 0.0; - - style->word_spacing.set = FALSE; - style->word_spacing.inherit = FALSE; - style->word_spacing.unit = SP_CSS_UNIT_NONE; - style->word_spacing.normal = TRUE; - style->word_spacing.value = style->word_spacing.computed = 0.0; - - style->baseline_shift.set = FALSE; - style->baseline_shift.inherit = FALSE; - style->baseline_shift.type = SP_BASELINE_SHIFT_LITERAL; - style->baseline_shift.unit = SP_CSS_UNIT_NONE; - style->baseline_shift.literal = SP_CSS_BASELINE_SHIFT_BASELINE; - style->baseline_shift.value = 0.0; - style->baseline_shift.computed = 0.0; - - style->text_transform.set = FALSE; - style->text_transform.inherit = FALSE; - style->text_transform.value = style->text_transform.computed = SP_CSS_TEXT_TRANSFORM_NONE; - - style->direction.set = FALSE; - style->direction.inherit = FALSE; - style->direction.value = style->direction.computed = SP_CSS_DIRECTION_LTR; - - style->block_progression.set = FALSE; - style->block_progression.inherit = FALSE; - style->block_progression.value = style->block_progression.computed = SP_CSS_BLOCK_PROGRESSION_TB; - - style->writing_mode.set = FALSE; - style->writing_mode.inherit = FALSE; - style->writing_mode.value = style->writing_mode.computed = SP_CSS_WRITING_MODE_LR_TB; - - style->text_anchor.set = FALSE; - style->text_anchor.inherit = FALSE; - style->text_anchor.value = style->text_anchor.computed = SP_CSS_TEXT_ANCHOR_START; - - style->clip_set = FALSE; - style->color_set = FALSE; - style->cursor_set = FALSE; - style->overflow_set = FALSE; - style->clip_path_set = FALSE; - style->mask_set = FALSE; - - style->clip_rule.set = FALSE; - style->clip_rule.inherit = FALSE; - style->clip_rule.value = style->clip_rule.computed = SP_WIND_RULE_NONZERO; - - style->opacity.set = FALSE; - style->opacity.inherit = FALSE; - style->opacity.value = SP_SCALE24_MAX; - style->visibility.set = FALSE; - style->visibility.inherit = FALSE; - style->visibility.value = style->visibility.computed = SP_CSS_VISIBILITY_VISIBLE; - style->display.set = FALSE; - style->display.inherit = FALSE; - style->display.value = style->display.computed = SP_CSS_DISPLAY_INLINE; - style->overflow.set = FALSE; - style->overflow.inherit = FALSE; - style->overflow.value = style->overflow.computed = SP_CSS_OVERFLOW_VISIBLE; - style->isolation.set = FALSE; - style->isolation.inherit = FALSE; - style->isolation.value = style->isolation.computed = SP_CSS_ISOLATION_AUTO; - style->blend_mode.set = FALSE; - style->blend_mode.inherit = FALSE; - style->blend_mode.value = style->blend_mode.computed = SP_CSS_BLEND_NORMAL; - - style->color.clear(); - style->color.setColor(0.0, 0.0, 0.0); - style->color_interpolation.set = FALSE; - style->color_interpolation.inherit = FALSE; - style->color_interpolation.value = style->color_interpolation.computed = SP_CSS_COLOR_INTERPOLATION_SRGB; - style->color_interpolation_filters.set = FALSE; - style->color_interpolation_filters.inherit = FALSE; - style->color_interpolation_filters.value = style->color_interpolation_filters.computed = SP_CSS_COLOR_INTERPOLATION_LINEARRGB; - - - style->fill.clear(); - style->fill.setColor(0.0, 0.0, 0.0); - style->fill_opacity.set = FALSE; - style->fill_opacity.inherit = FALSE; - style->fill_opacity.value = SP_SCALE24_MAX; - style->fill_rule.set = FALSE; - style->fill_rule.inherit = FALSE; - style->fill_rule.value = style->fill_rule.computed = SP_WIND_RULE_NONZERO; - - style->stroke.clear(); - style->stroke_opacity.set = FALSE; - style->stroke_opacity.inherit = FALSE; - style->stroke_opacity.value = SP_SCALE24_MAX; - - style->stroke_width.set = FALSE; - style->stroke_width.inherit = FALSE; - style->stroke_width.unit = SP_CSS_UNIT_NONE; - style->stroke_width.value = style->stroke_width.computed = 1.0; - - style->stroke_linecap.set = FALSE; - style->stroke_linecap.inherit = FALSE; - style->stroke_linecap.value = style->stroke_linecap.computed = SP_STROKE_LINECAP_BUTT; - style->stroke_linejoin.set = FALSE; - style->stroke_linejoin.inherit = FALSE; - style->stroke_linejoin.value = style->stroke_linejoin.computed = SP_STROKE_LINEJOIN_MITER; - - style->stroke_miterlimit.set = FALSE; - style->stroke_miterlimit.inherit = FALSE; - style->stroke_miterlimit.value = 4.0; - - style->stroke_dasharray.values.clear(); - style->stroke_dasharray.set = FALSE; - style->stroke_dasharray.inherit = FALSE; - - style->stroke_dashoffset.value = style->stroke_dashoffset.computed = 0.0; - style->stroke_dashoffset.set = FALSE; - style->stroke_dashoffset.inherit = FALSE; - - for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) { - g_free(style->marker[i].value); - style->marker[i].set = FALSE; - style->marker[i].inherit = FALSE; - style->marker[i].data = 0; - style->marker[i].value = NULL; - } - - /* SVG 2 */ - style->paint_order.set = FALSE; - style->paint_order.inherit = FALSE; // For now - for (unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i) { - style->paint_order.layer[i] = SP_CSS_PAINT_ORDER_NORMAL; - style->paint_order.layer_set[i] = false; - } - style->paint_order.value = NULL; - - style->filter.set = FALSE; - style->filter.inherit = FALSE; - style->filter.href = NULL; - - style->enable_background.value = SP_CSS_BACKGROUND_ACCUMULATE; - style->enable_background.set = false; - style->enable_background.inherit = false; - - style->filter_blend_mode.set = style->filter_blend_mode.inherit = false; - style->filter_blend_mode.value = style->filter_blend_mode.computed = 0; - style->filter_gaussianBlur_deviation.set = style->filter_gaussianBlur_deviation.inherit = false; - style->filter_gaussianBlur_deviation.value = style->filter_gaussianBlur_deviation.computed = 0; - - style->color_rendering.set = style->color_rendering.inherit = false; - style->color_rendering.value = style->color_rendering.computed = SP_CSS_COLOR_RENDERING_AUTO; - style->image_rendering.set = style->image_rendering.inherit = false; - style->image_rendering.value = style->image_rendering.computed = SP_CSS_IMAGE_RENDERING_AUTO; - style->shape_rendering.set = style->shape_rendering.inherit = false; - style->shape_rendering.value = style->shape_rendering.computed = SP_CSS_SHAPE_RENDERING_AUTO; - style->text_rendering.set = style->text_rendering.inherit = false; - style->text_rendering.value = style->text_rendering.computed = SP_CSS_TEXT_RENDERING_AUTO; - - style->cloned = false; -} - - - -/** - * - */ -static void -sp_style_read_dash(SPStyle *style, gchar const *str) -{ - /* Ref: http://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty */ - style->stroke_dasharray.set = TRUE; - - if (strcmp(str, "inherit") == 0) { - style->stroke_dasharray.inherit = true; - return; - } - style->stroke_dasharray.inherit = false; - - style->stroke_dasharray.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); - style->stroke_dasharray.values.push_back( number ); - if (number > 0.00000001) - LineSolid = false; - if (e != str) { - str = e; - } - while (str && *str && !isalnum(*str)) str += 1; - } - - if (LineSolid) { - style->stroke_dasharray.values.clear(); - } -} - - -/*######################### -## SPTextStyle operations -#########################*/ - - -/** - * Return new SPTextStyle object with default settings. - */ -static SPTextStyle * -sp_text_style_new() -{ - SPTextStyle *ts = g_new0(SPTextStyle, 1); - ts->refcount = 1; - sp_text_style_clear(ts); - - ts->font_specification.value = g_strdup("sans-serif"); - ts->font.value = g_strdup("sans-serif"); - ts->font_family.value = g_strdup("sans-serif"); - - return ts; -} - - -/** - * Clear text style settings. - */ -static void -sp_text_style_clear(SPTextStyle *ts) -{ - ts->font_specification.set = FALSE; - ts->font.set = FALSE; - ts->font_family.set = FALSE; -} - - - -/** - * Reduce refcount of text style and possibly free it. - */ -static SPTextStyle * -sp_text_style_unref(SPTextStyle *st) -{ - st->refcount -= 1; - - if (st->refcount < 1) { - g_free(st->font_specification.value); - g_free(st->font.value); - g_free(st->font_family.value); - g_free(st); - } - - return NULL; -} - - - -/** - * Return duplicate of text style. - */ -static SPTextStyle * -sp_text_style_duplicate_unset(SPTextStyle *st) -{ - SPTextStyle *nt = g_new0(SPTextStyle, 1); - nt->refcount = 1; - - nt->font_specification.value = g_strdup(st->font_specification.value); - nt->font.value = g_strdup(st->font.value); - nt->font_family.value = g_strdup(st->font_family.value); - - return nt; -} - - - -/** - * Write SPTextStyle object into string. - */ -static guint -sp_text_style_write(gchar *p, guint const len, SPTextStyle const *const st, guint flags) -{ - gint d = 0; - - // We do not do diffing for text style - if (flags == SP_STYLE_FLAG_IFDIFF) - flags = SP_STYLE_FLAG_IFSET; - - d += sp_style_write_istring(p + d, len - d, "font-family", &st->font_family, NULL, flags); - d += sp_style_write_istring(p + d, len - d, "-inkscape-font-specification", &st->font_specification, NULL, flags); - return d; -} - - - -/* The following sp_tyle_read_* functions ignore invalid values, as per - * http://www.w3.org/TR/REC-CSS2/syndata.html#parsing-errors. - * - * [However, the SVG spec is somewhat unclear as to whether the style attribute should - * be handled as per CSS2 rules or whether it must simply be a set of PROPERTY:VALUE - * pairs, in which case SVG's error-handling rules - * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing should instead be applied.] - */ - - -/** - * Set SPIFloat object from string. - */ -static void -sp_style_read_ifloat(SPIFloat *val, gchar const *str) -{ - if (!strcmp(str, "inherit")) { - val->set = TRUE; - val->inherit = TRUE; - } else { - gfloat value; - if (sp_svg_number_read_f(str, &value)) { - val->set = TRUE; - val->inherit = FALSE; - val->value = value; - } - } -} - - - -/** - * Set SPIScale24 object from string. - */ -static void -sp_style_read_iscale24(SPIScale24 *val, gchar const *str) -{ - if (!strcmp(str, "inherit")) { - val->set = TRUE; - val->inherit = TRUE; - } else { - gfloat value; - if (sp_svg_number_read_f(str, &value)) { - val->set = TRUE; - val->inherit = FALSE; - value = CLAMP(value, 0.0, 1.0); - val->value = SP_SCALE24_FROM_FLOAT(value); - } - } -} - -/** - * Reads a style value and performs lookup based on the given style value enumerations. - */ -static void -sp_style_read_ienum(SPIEnum *val, gchar const *str, SPStyleEnum const *dict, - bool const can_explicitly_inherit) -{ - if ( can_explicitly_inherit && !strcmp(str, "inherit") ) { - val->set = TRUE; - val->inherit = TRUE; - } else { - for (unsigned i = 0; dict[i].key; i++) { - if (!strcmp(str, dict[i].key)) { - val->set = TRUE; - val->inherit = FALSE; - val->value = dict[i].value; - /* Save copying for values not needing it */ - val->computed = val->value; - break; - } - } - } - return; -} - - - -/** - * Set SPIString object from string. - */ -static void -sp_style_read_istring(SPIString *val, gchar const *str) -{ - g_free(val->value); - - if (!strcmp(str, "inherit")) { - val->set = TRUE; - val->inherit = TRUE; - val->value = NULL; - } else { - val->set = TRUE; - val->inherit = FALSE; - val->value = g_strdup(str); - } -} - - - -/** - * Set SPILength object from string. - */ -static void -sp_style_read_ilength(SPILength *val, gchar const *str) -{ - if (!strcmp(str, "inherit")) { - val->set = TRUE; - val->inherit = TRUE; - } else { - gdouble value; - gchar *e; - /** \todo fixme: Move this to standard place (Lauris) */ - value = g_ascii_strtod(str, &e); - if ( !IS_FINITE(value) ) { // fix for bug lp:935157 - return; - } - 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 - * it at the same time as percentage units are done. - */ - val->value = value; - if (!*e) { - /* Userspace */ - val->unit = SP_CSS_UNIT_NONE; - val->computed = value; - } else if (!strcmp(e, "px")) { - /* Userspace */ - val->unit = SP_CSS_UNIT_PX; - val->computed = value; - } else if (!strcmp(e, "pt")) { - /* Userspace / DEVICESCALE */ - val->unit = SP_CSS_UNIT_PT; - val->computed = Inkscape::Util::Quantity::convert(value, "pt", "px"); - } else if (!strcmp(e, "pc")) { - val->unit = SP_CSS_UNIT_PC; - val->computed = Inkscape::Util::Quantity::convert(value, "pc", "px"); - } else if (!strcmp(e, "mm")) { - val->unit = SP_CSS_UNIT_MM; - val->computed = Inkscape::Util::Quantity::convert(value, "mm", "px"); - } else if (!strcmp(e, "cm")) { - val->unit = SP_CSS_UNIT_CM; - val->computed = Inkscape::Util::Quantity::convert(value, "cm", "px"); - } else if (!strcmp(e, "in")) { - val->unit = SP_CSS_UNIT_IN; - val->computed = Inkscape::Util::Quantity::convert(value, "in", "px"); - } else if (!strcmp(e, "em")) { - /* EM square */ - val->unit = SP_CSS_UNIT_EM; - val->computed = value * SP_CSS_FONT_SIZE_DEFAULT; - } else if (!strcmp(e, "ex")) { - /* ex square */ - val->unit = SP_CSS_UNIT_EX; - val->computed = value * 0.5 * SP_CSS_FONT_SIZE_DEFAULT; - } else if (!strcmp(e, "%")) { - /* Percentage */ - val->unit = SP_CSS_UNIT_PERCENT; - val->value = value * 0.01; - } else { - /* Invalid */ - return; - } - val->set = TRUE; - val->inherit = FALSE; - } - } -} - -/** - * Set SPILengthOrNormal object from string. - */ -static void -sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str) -{ - if (!strcmp(str, "normal")) { - val->set = TRUE; - val->inherit = FALSE; - val->normal = TRUE; - val->unit = SP_CSS_UNIT_NONE; - val->value = val->computed = 0.0; - } else { - SPILength length; - sp_style_read_ilength(&length, str); - val->set = length.set; - val->inherit = length.inherit; - val->normal = FALSE; - val->unit = length.unit; - val->value = length.value; - val->computed = length.computed; - } -} - -/** - * Set SPIPaintOrder object from string. - */ -static void -sp_style_read_ipaintorder(SPIPaintOrder *val, gchar const *str) -{ - g_free(val->value); - - if (!strcmp(str, "inherit")) { - // NEED TO CHECK FINAL SPEC - val->set = TRUE; - val->inherit = TRUE; - val->value = NULL; - } else { - val->set = TRUE; - val->inherit = FALSE; - val->value = g_strdup(str); - - if (!strcmp(str, "normal")) { - val->layer[0] = SP_CSS_PAINT_ORDER_NORMAL; - val->layer_set[0] = true; - } else { - // This certainly can be done more efficiently - gchar** c = g_strsplit(str, " ", PAINT_ORDER_LAYERS + 1); - bool used[3] = {false, false, false}; - unsigned int i = 0; - for( ; i < PAINT_ORDER_LAYERS; ++i ) { - if( c[i] ) { - val->layer_set[i] = false; - if( !strcmp( c[i], "fill")) { - val->layer[i] = SP_CSS_PAINT_ORDER_FILL; - val->layer_set[i] = true; - used[0] = true; - } else if( !strcmp( c[i], "stroke")) { - val->layer[i] = SP_CSS_PAINT_ORDER_STROKE; - val->layer_set[i] = true; - used[1] = true; - } else if( !strcmp( c[i], "markers")) { - val->layer[i] = SP_CSS_PAINT_ORDER_MARKER; - val->layer_set[i] = true; - used[2] = true; - } else { - break; - } - } else { - break; - } - } - g_strfreev(c); - - // Fill out rest of the layers using the default order - if( !used[0] && i < PAINT_ORDER_LAYERS ) { - val->layer[i] = SP_CSS_PAINT_ORDER_FILL; - val->layer_set[i] = false; - ++i; - } - if( !used[1] && i < PAINT_ORDER_LAYERS ) { - val->layer[i] = SP_CSS_PAINT_ORDER_STROKE; - val->layer_set[i] = false; - ++i; - } - if( !used[2] && i < PAINT_ORDER_LAYERS ) { - val->layer[i] = SP_CSS_PAINT_ORDER_MARKER; - val->layer_set[i] = false; - } - } - } -} - - - -/** - * Set SPITextDecoration object from string. - */ -static void -sp_style_read_itextdecoration(SPITextDecorationLine *line, SPITextDecorationStyle *style, SPIPaint *color, gchar const *str){ - sp_style_read_itextdecorationLine(line, str); // scans all tokens for line types - sp_style_read_itextdecorationStyle(style, str); // scans all tokens for style types - // the color routine must be fed one token at a time - if multiple colors are found the LAST one is used - 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 - sp_style_read_itextdecorationColor(color, frag); - g_free(frag); - if(color->set)break; - if(*str == '\0')break; - hstr = str + 1; - } - str++; - } -} - -/** - * Set SPITextDecorationLine object from string. - * returns true if there was a match, false otherwise - */ -static void -sp_style_read_itextdecorationLine(SPITextDecorationLine *line, gchar const *str){ - if (!strcmp(str, "inherit")) { - line->set = true; - line->inherit = true; - } else if (!strcmp(str, "none")) { - line->set = true; - line->inherit = false; - line->underline = false; - line->overline = false; - line->line_through = false; - line->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) { - line->set = true; - line->inherit = false; - line->underline = found_underline; - line->overline = found_overline; - line->line_through = found_line_through; - line->blink = found_blink; - } - else { - line->set = false; - line->inherit = false; - } - } -} - -/** - * Set SPITextDecorationStyle object from string. - * returns true if there was a match, false otherwise -*/ -static void -sp_style_read_itextdecorationStyle(SPITextDecorationStyle *style, gchar const *str){ - if (!strcmp(str, "inherit")) { - style->set = true; - style->inherit = true; - } else if (!strcmp(str, "none")) { - style->set = true; - style->inherit = false; - style->solid = false; - style->isdouble = false; - style->dotted = false; - style->dashed = false; - style->wavy = 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){ - style->set = true; - style->inherit = false; - style->solid = found_solid; - style->isdouble = found_double; - style->dotted = found_dotted; - style->dashed = found_dashed; - style->wavy = found_wavy; - } - else { - style->set = false; - style->inherit = false; - } - } -} - -/** - * Set SPIPaint object from string. - */ -static void -sp_style_read_itextdecorationColor(SPIPaint *color, gchar const *str){ - sp_style_read_icolor(color, str, NULL,NULL); -} - - -/** - * Set SPIPaint object from string containing an integer value. - * \param document Ignored - */ -static void -sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document) -{ - (void)style; // TODO - (void)document; // TODO - paint->currentcolor = FALSE; /* currentColor not a valid <color>. */ - if (!strcmp(str, "inherit")) { - paint->set = TRUE; - paint->inherit = TRUE; - } else { - paint->inherit = FALSE; - guint32 const rgb0 = sp_svg_read_color(str, 0xff); - if (rgb0 != 0xff) { - paint->setColor(rgb0); - paint->set = TRUE; - } - } -} - - -/** - * Set SPIPaint object from string. - * - * \pre paint == \&style.fill || paint == \&style.stroke. - */ -void SPIPaint::read( gchar const *str, SPStyle &style, SPDocument *document ) -{ - while (g_ascii_isspace(*str)) { - ++str; - } - - clear(); - - if (streq(str, "inherit")) { - set = TRUE; - inherit = TRUE; - } else { - if ( strneq(str, "url", 3) ) { - gchar *uri = extract_uri( str, &str ); - while ( g_ascii_isspace(*str) ) { - ++str; - } - // TODO check on and comment the comparrison "paint != &style->color". - if ( uri && *uri && (this != &style.color) ) { - set = TRUE; - - // it may be that this style's SPIPaint has not yet created its URIReference; - // now that we have a document, we can create it here - if (!value.href && document) { - value.href = new SPPaintServerReference(document); - value.href->changedSignal().connect(sigc::bind(sigc::ptr_fun((this == &style.fill)? sp_style_fill_paint_server_ref_changed : sp_style_stroke_paint_server_ref_changed), &style)); - } - - // 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 ); - } - - if (streq(str, "currentColor") && (this != &style.color)) { - set = TRUE; - currentcolor = TRUE; - } else if (streq(str, "none") && (this != &style.color)) { - 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; - } - } - } - } -} - - - -/** - * Set SPIFontSize object from string. - */ -static void -sp_style_read_ifontsize(SPIFontSize *val, gchar const *str) -{ - if (!strcmp(str, "inherit")) { - val->set = TRUE; - val->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)) { - val->set = TRUE; - val->inherit = FALSE; - val->type = SP_FONT_SIZE_LITERAL; - val->literal = enum_font_size[i].value; - return; - } - } - /* Invalid */ - return; - } else { - SPILength length; - length.set = FALSE; - sp_style_read_ilength(&length, str); - if( length.set ) { - val->set = TRUE; - val->inherit = length.inherit; - val->unit = length.unit; - val->value = length.value; - val->computed = length.computed; - if( val->unit == SP_CSS_UNIT_PERCENT ) { - val->type = SP_FONT_SIZE_PERCENTAGE; - } else { - val->type = SP_FONT_SIZE_LENGTH; - } - } - return; - } -} - - -/** - * Set SPIBaselineShift object from string. - */ -static void -sp_style_read_ibaselineshift(SPIBaselineShift *val, gchar const *str) -{ - if (!strcmp(str, "inherit")) { - val->set = TRUE; - val->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)) { - val->set = TRUE; - val->inherit = FALSE; - val->type = SP_BASELINE_SHIFT_LITERAL; - val->literal = enum_baseline_shift[i].value; - return; - } - } - /* Invalid */ - return; - } else { - SPILength length; - sp_style_read_ilength(&length, str); - val->set = length.set; - val->inherit = length.inherit; - val->unit = length.unit; - val->value = length.value; - val->computed = length.computed; - if( val->unit == SP_CSS_UNIT_PERCENT ) { - val->type = SP_BASELINE_SHIFT_PERCENTAGE; - } else { - val->type = SP_BASELINE_SHIFT_LENGTH; - } - return; - } -} - - -/** - * Set SPIFilter object from string. - */ -static void -sp_style_read_ifilter(gchar const *str, SPStyle * style, SPDocument *document) -{ - SPIFilter *f = &(style->filter); - /* Try all possible values: inherit, none, uri */ - if (streq(str, "inherit")) { - f->set = TRUE; - f->inherit = TRUE; - if (f->href){ - if (f->href->getObject()){ - f->href->detach(); - } - } - } else if(streq(str, "none")) { - f->set = TRUE; - f->inherit = FALSE; - if (f->href){ - if (f->href->getObject()){ - f->href->detach(); - } - } - } else if (strneq(str, "url", 3)) { - char *uri = extract_uri(str); - if(uri == NULL || uri[0] == '\0') { - g_warning("Specified filter url is empty"); - f->set = TRUE; - f->inherit = FALSE; - return; - } - f->set = TRUE; - f->inherit = FALSE; - if (f->href){ - if (f->href->getObject()){ - f->href->detach(); - } - } - - // it may be that this style has not yet created its SPFilterReference; - // now that we have a document, we can create it here - if (!f->href && document) { - f->href = new SPFilterReference(document); - f->href->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_style_filter_ref_changed), style)); - } - - try { - f->href->attach(Inkscape::URI(uri)); - } catch (Inkscape::BadURIException &e) { - g_warning("%s", e.what()); - f->href->detach(); - } - g_free (uri); - - } else { - /* We shouldn't reach this if SVG input is well-formed */ - f->set = FALSE; - f->inherit = FALSE; - if (f->href){ - if (f->href->getObject()){ - f->href->detach(); - } - } - } -} - -/** - * Set SPIEnum object from repr attribute. - */ -static void -sp_style_read_penum(SPIEnum *val, Inkscape::XML::Node *repr, - gchar const *key, SPStyleEnum const *dict, - bool const can_explicitly_inherit) -{ - gchar const *str = repr->attribute(key); - if (str) { - sp_style_read_ienum(val, str, dict, can_explicitly_inherit); - } -} - - - -/** - * Set SPILength object from repr attribute. - */ -static void -sp_style_read_plength(SPILength *val, Inkscape::XML::Node *repr, gchar const *key) -{ - gchar const *str = repr->attribute(key); - if (str) { - sp_style_read_ilength(val, str); - } -} - -/** - * Set SPIFontSize object from repr attribute. - */ -static void -sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const *key) -{ - gchar const *str = repr->attribute(key); - if (str) { - sp_style_read_ifontsize(val, str); - } -} - - -/** - * Set SPIBaselineShift object from repr attribute. - */ -static void -sp_style_read_pbaselineshift(SPIBaselineShift *val, Inkscape::XML::Node *repr, gchar const *key) -{ - gchar const *str = repr->attribute(key); - if (str) { - sp_style_read_ibaselineshift(val, str); - } -} - - -/** - * Set SPIFloat object from repr attribute. - */ -static void -sp_style_read_pfloat(SPIFloat *val, Inkscape::XML::Node *repr, gchar const *key) -{ - gchar const *str = repr->attribute(key); - if (str) { - sp_style_read_ifloat(val, str); - } -} - - -/** - * Write SPIFloat object into string. - */ -static gint -sp_style_write_ifloat(gchar *p, gint const len, gchar const *const key, - SPIFloat const *const val, SPIFloat const *const base, guint const flags) -{ - Inkscape::CSSOStringStream os; - - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || (val->value != base->value)))) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } else { - os << key << ":" << val->value << ";"; - return g_strlcpy(p, os.str().c_str(), len); - } - } - return 0; -} - - -/** - * Write SPIScale24 object into string. - */ -static gint -sp_style_write_iscale24(gchar *p, gint const len, gchar const *const key, - SPIScale24 const *const val, SPIScale24 const *const base, - guint const flags) -{ - Inkscape::CSSOStringStream os; - - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || (val->value != base->value)))) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } else { - os << key << ":" << SP_SCALE24_TO_FLOAT(val->value) << ";"; - return g_strlcpy(p, os.str().c_str(), len); - } - } - return 0; -} - - -/** - * Write SPIEnum object into string. - */ -static gint -sp_style_write_ienum(gchar *p, gint const len, gchar const *const key, - SPStyleEnum const *const dict, - SPIEnum const *const val, SPIEnum const *const base, guint const flags) -{ - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || (val->computed != base->computed)))) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } - for (unsigned i = 0; dict[i].key; i++) { - if (dict[i].value == static_cast< gint > (val->value) ) { - return g_snprintf(p, len, "%s:%s;", key, dict[i].key); - } - } - } - return 0; -} - - - -/** - * Write SPIString object into string. - */ -static gint -sp_style_write_istring(gchar *p, gint const len, gchar const *const key, - SPIString const *const val, SPIString const *const base, guint const flags) -{ - gint res = 0; - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || strcmp(val->value, base->value)))) - { - if (val->inherit) { - res = g_snprintf(p, len, "%s:inherit;", key); - } else { - Glib::ustring val_quoted = css2_escape_quote(val->value); - if (~val_quoted.empty()) { - res = g_snprintf(p, len, "%s:%s;", key, val_quoted.c_str()); - } - } - } - return res; -} - - -/** - * - */ -static bool -sp_length_differ(SPILength const *const a, SPILength const *const b) -{ - if (a->unit != b->unit) { - if (a->unit == SP_CSS_UNIT_EM) return true; - if (a->unit == SP_CSS_UNIT_EX) return true; - if (a->unit == SP_CSS_UNIT_PERCENT) return true; - if (b->unit == SP_CSS_UNIT_EM) return true; - if (b->unit == SP_CSS_UNIT_EX) return true; - if (b->unit == SP_CSS_UNIT_PERCENT) return true; - } - - return (a->computed != b->computed); -} - - - -/** - * Write SPILength object into string. - */ -static gint -sp_style_write_ilength(gchar *p, gint const len, gchar const *const key, - SPILength const *const val, SPILength const *const base, guint const flags) -{ - Inkscape::CSSOStringStream os; - - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || sp_length_differ(val, base)))) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } else { - switch (val->unit) { - case SP_CSS_UNIT_NONE: - os << key << ":" << val->computed << ";"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_PX: - os << key << ":" << val->computed << "px;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_PT: - os << key << ":" << Inkscape::Util::Quantity::convert(val->computed, "px", "pt") << "pt;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_PC: - os << key << ":" << Inkscape::Util::Quantity::convert(val->computed, "px", "pc") << "pc;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_MM: - os << key << ":" << Inkscape::Util::Quantity::convert(val->computed, "px", "mm") << "mm;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_CM: - os << key << ":" << Inkscape::Util::Quantity::convert(val->computed, "px", "cm") << "cm;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_IN: - os << key << ":" << Inkscape::Util::Quantity::convert(val->computed, "px", "in") << "in;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_EM: - os << key << ":" << val->value << "em;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_EX: - os << key << ":" << val->value << "ex;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - case SP_CSS_UNIT_PERCENT: - os << key << ":" << (val->value * 100.0) << "%;"; - return g_strlcpy(p, os.str().c_str(), len); - break; - default: - /* Invalid */ - break; - } - } - } - return 0; -} - - -/** - * - */ -static bool -sp_lengthornormal_differ(SPILengthOrNormal const *const a, SPILengthOrNormal const *const b) -{ - if (a->normal != b->normal) return true; - if (a->normal) return false; - - if (a->unit != b->unit) { - if (a->unit == SP_CSS_UNIT_EM) return true; - if (a->unit == SP_CSS_UNIT_EX) return true; - if (a->unit == SP_CSS_UNIT_PERCENT) return true; - if (b->unit == SP_CSS_UNIT_EM) return true; - if (b->unit == SP_CSS_UNIT_EX) return true; - if (b->unit == SP_CSS_UNIT_PERCENT) return true; - } - - return (a->computed != b->computed); -} - -/** - * Write SPILengthOrNormal object into string. - */ -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) -{ - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || sp_lengthornormal_differ(val, base)))) - { - if (val->normal) { - return g_snprintf(p, len, "%s:normal;", key); - } else { - SPILength length; - length.set = val->set; - length.inherit = val->inherit; - length.unit = val->unit; - length.value = val->value; - length.computed = val->computed; - return sp_style_write_ilength(p, len, key, &length, NULL, SP_STYLE_FLAG_ALWAYS); - } - } - return 0; -} - -/** - * Write SPIDashArray object into string. - */ -static gint -sp_style_write_idasharray(gchar *p, gint const len, gchar const *const /*key*/, - SPIDashArray const *const val, SPIDashArray const *const base, guint const flags) -{ - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || (val->values != base->values)))) - { - if (val->inherit) { - return g_snprintf(p, len, "stroke-dasharray:inherit;"); - } else if ( !val->values.empty() ) { - Inkscape::CSSOStringStream os; - os << "stroke-dasharray:"; - for (unsigned i = 0; i < val->values.size(); i++) { - if (i) { - os << ", "; - } - os << val->values[i]; - } - os << ";"; - return g_strlcpy(p, os.str().c_str(), len); - } else { - return g_snprintf(p, len, "stroke-dasharray:none;"); - } - } - return 0; -} - - -/** - * - */ -static bool -sp_textdecorationLine_differ(SPITextDecorationLine const *const a, SPITextDecorationLine const *const b) -{ - return( (a->underline != b->underline ) - || (a->overline != b->overline ) - || (a->line_through != b->line_through) - || (a->blink != b->blink ) - ); -} - -/** - * - */ -static bool -sp_textdecorationStyle_differ(SPITextDecorationStyle const *const a, SPITextDecorationStyle const *const b) -{ - return( (a->solid != b->solid ) - || (a->isdouble != b->isdouble ) - || (a->dotted != b->dotted ) - || (a->dashed != b->dashed ) - || (a->wavy != b->wavy ) - ); -} - -/** - * - */ -static bool -sp_textdecorationColor_differ(SPIPaint const *const a, SPIPaint const *const b) -{ - bool status = (a->isPaintserver() == b->isPaintserver()) && - (a->colorSet == b->colorSet) && - (a->currentcolor == b->currentcolor); - return(status); -} - -/** - * Write SPITextDecoration object into string. - */ -static gint -sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, - SPITextDecorationLine const *const line, - SPITextDecorationStyle const *const style, - SPIPaint const *const color, - SPITextDecorationLine const *const baseLine, - SPITextDecorationStyle const *const baseStyle, - SPIPaint const *const baseColor, - guint const flags) -{ - Inkscape::CSSOStringStream os; - - if ( (flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && line->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && line->set - && ( !baseLine->set || sp_textdecorationLine_differ(line, baseLine))) - || ((flags & SP_STYLE_FLAG_IFSET) && style->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && style->set - && ( !baseStyle->set || sp_textdecorationStyle_differ(style, baseStyle))) - || ((flags & SP_STYLE_FLAG_IFSET) && color->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && color->set - && ( !baseColor->set || sp_textdecorationColor_differ(color, baseColor))) - ){ - os << key << ":"; - if (line->inherit || style->inherit || color->inherit) { - os << " inherit"; - } - else if (line->underline || line->overline || line->line_through || line->blink) { - if (line->underline) os << " underline"; - if (line->overline) os << " overline"; - if (line->line_through) os << " line-through"; - if (line->blink) os << " blink"; - - if ( style->solid) os << " solid"; - else if (style->isdouble) os << " double"; - else if (style->dotted) os << " dotted"; - else if (style->dashed) os << " dashed"; - else if (style->wavy) os << " wavy"; - // color, if it is set, otherwise omit it - if(color->set){ - char color_buf[8]; - sp_svg_write_color(color_buf, sizeof(color_buf), color->value.color.toRGBA32( 0 )); - os << " "; - os << color_buf; - } - } - else { - os << "none"; - } - os << ";"; - return g_strlcpy(p, os.str().c_str(), len); - } - return 0; -} - -/** - * - */ -static bool -sp_paint_differ(SPIPaint const *const a, SPIPaint const *const b) -{ - if ( (a->isColor() != b->isColor()) - || (a->isPaintserver() != b->isPaintserver()) - || (a->set != b->set) - || (a->currentcolor != b->currentcolor) - || (a->inherit!= b->inherit) ) { - return true; - } - - // TODO refactor to allow for mixed paints (rgb() *and* url(), etc) - - if ( a->isPaintserver() ) { - return (a->value.href == NULL || b->value.href == NULL || a->value.href->getObject() != b->value.href->getObject()); - } - - if ( a->isColor() ) { - return !( (a->value.color == b->value.color) - && ((a->value.color.icc == b->value.color.icc) - || (a->value.color.icc && b->value.color.icc - && (a->value.color.icc->colorProfile == b->value.color.icc->colorProfile) - && (a->value.color.icc->colors == b->value.color.icc->colors)))); - /* todo: Allow for epsilon differences in iccColor->colors, e.g. changes small enough not to show up - * in the string representation. */ - } - - return false; -} - - - -/** - * Write SPIPaint object into string. - */ -static gint -sp_style_write_ipaint(gchar *b, gint const len, gchar const *const key, - SPIPaint const *const paint, SPIPaint const *const base, guint const flags) -{ - int retval = 0; - - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && paint->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && paint->set - && (!base->set || sp_paint_differ(paint, base)))) - { - CSSOStringStream css; - - if (paint->inherit) { - css << "inherit"; - } else { - if ( paint->value.href && paint->value.href->getURI() ) { - const gchar* uri = paint->value.href->getURI()->toString(); - css << "url(" << uri << ")"; - g_free((void *)uri); - } - - if ( paint->noneSet ) { - if ( !css.str().empty() ) { - css << " "; - } - css << "none"; - } - - if ( paint->currentcolor ) { - if ( !css.str().empty() ) { - css << " "; - } - css << "currentColor"; - } - - if ( paint->colorSet && !paint->currentcolor ) { - if ( !css.str().empty() ) { - css << " "; - } - char color_buf[8]; - sp_svg_write_color(color_buf, sizeof(color_buf), paint->value.color.toRGBA32( 0 )); - css << color_buf; - } - - if (paint->value.color.icc && !paint->currentcolor) { - if ( !css.str().empty() ) { - css << " "; - } - css << "icc-color(" << paint->value.color.icc->colorProfile; - for (vector<double>::const_iterator i(paint->value.color.icc->colors.begin()), - iEnd(paint->value.color.icc->colors.end()); - i != iEnd; ++i) { - css << ", " << *i; - } - css << ')'; - } - } - - if ( !css.str().empty() ) { - retval = g_snprintf( b, len, "%s:%s;", key, css.str().c_str() ); - } - } - - return retval; -} - - -/** - * - */ -static bool -sp_paint_order_differ(SPIPaintOrder const *const a, SPIPaintOrder const *const b) -{ - if( (a->set != b->set) || - (a->inherit!= b->inherit) ) { - return true; - } - - // Check this works when paint-order value is 'normal' - for (unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) { - if( (a->layer[i] != b->layer[i]) || - (a->layer_set[i] != b->layer_set[i]) ) { - return true; - } - } - return false; -} - - - -/** - * Write SPIPaintOrder object into string. - */ -static gint -sp_style_write_ipaintorder(gchar *p, gint len, gchar const *key, SPIPaintOrder const *paint_order, SPIPaintOrder const *base, guint flags) -{ - int retval = 0; - - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && paint_order->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && paint_order->set - && (!base->set || sp_paint_order_differ(paint_order, base)))) - { - CSSOStringStream css; - - if (paint_order->inherit) { - css << "inherit"; - } else { - for( unsigned i = 0; i < PAINT_ORDER_LAYERS; ++i ) { - if( paint_order->layer_set[i] == true ) { - switch (paint_order->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; - } - } - } - - if ( !css.str().empty() ) { - retval = g_snprintf( p, len, "%s:%s;", key, css.str().c_str() ); - } - } - - return retval; -} - -/** - * - */ -static bool -sp_fontsize_differ(SPIFontSize const *const a, SPIFontSize const *const b) -{ - if (a->type != b->type) - return true; - if (a->type == SP_FONT_SIZE_LENGTH) { - if (a->computed != b->computed) - return true; - } else { - if (a->value != b->value) - return true; - } - return false; -} - - -/** - * Write SPIFontSize object into string. - */ -static gint -sp_style_write_ifontsize(gchar *p, gint const len, gchar const *key, - SPIFontSize const *const val, SPIFontSize const *const base, - guint const flags) -{ - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || sp_fontsize_differ(val, base)))) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } else if (val->type == SP_FONT_SIZE_LITERAL) { - for (unsigned i = 0; enum_font_size[i].key; i++) { - if (enum_font_size[i].value == static_cast< gint > (val->literal) ) { - return g_snprintf(p, len, "%s:%s;", key, enum_font_size[i].key); - } - } - } else if (val->type == SP_FONT_SIZE_LENGTH) { - Inkscape::CSSOStringStream os; - 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; - } - os << key << ":" << sp_style_css_size_px_to_units(val->computed, unit) << sp_style_get_css_unit_string(unit) << ";"; - return g_strlcpy(p, os.str().c_str(), len); - } else if (val->type == SP_FONT_SIZE_PERCENTAGE) { - Inkscape::CSSOStringStream os; - os << key << ":" << (val->value * 100.0) << "%;"; - return g_strlcpy(p, os.str().c_str(), len); - } - } - return 0; -} - - -/* - * baseline-shift is relative to parent. The only time it should - * not be written out is if it is zero (or not set). - */ -static bool -sp_baseline_shift_notzero(SPIBaselineShift const *const a ) -{ - if( a->type == SP_BASELINE_SHIFT_LITERAL ) { - if( a->literal == SP_CSS_BASELINE_SHIFT_BASELINE ) { - return false; - } - } else { - if( a->value == 0.0 ) { - return false; - } - } - return true; -} - -/** - * Write SPIBaselineShift object into string. - */ -static gint -sp_style_write_ibaselineshift(gchar *p, gint const len, gchar const *key, - SPIBaselineShift const *const val, SPIBaselineShift const *const base, - guint const flags) -{ - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set - && (!base->set || sp_baseline_shift_notzero(val) ))) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } else if (val->type == SP_BASELINE_SHIFT_LITERAL) { - for (unsigned i = 0; enum_baseline_shift[i].key; i++) { - if (enum_baseline_shift[i].value == static_cast< gint > (val->literal) ) { - return g_snprintf(p, len, "%s:%s;", key, enum_baseline_shift[i].key); - } - } - } else if (val->type == SP_BASELINE_SHIFT_LENGTH) { - if( val->unit == SP_CSS_UNIT_EM || val->unit == SP_CSS_UNIT_EX ) { - Inkscape::CSSOStringStream os; - os << key << ":" << val->value << (val->unit == SP_CSS_UNIT_EM ? "em;" : "ex;"); - return g_strlcpy(p, os.str().c_str(), len); - } else { - Inkscape::CSSOStringStream os; - os << key << ":" << val->computed << "px;"; // must specify px, see inkscape bug 1221626, mozilla bug 234789 - return g_strlcpy(p, os.str().c_str(), len); - } - } else if (val->type == SP_BASELINE_SHIFT_PERCENTAGE) { - Inkscape::CSSOStringStream os; - os << key << ":" << (val->value * 100.0) << "%;"; - return g_strlcpy(p, os.str().c_str(), len); - } - } - return 0; -} - - - -/** - * Write SPIFilter object into string. - */ -static gint -sp_style_write_ifilter(gchar *p, gint const len, gchar const *key, - SPIFilter const *const val, SPIFilter const *const base, - guint const flags) -{ - (void)base; // TODO - if ((flags & SP_STYLE_FLAG_ALWAYS) - || ((flags & SP_STYLE_FLAG_IFSET) && val->set) - || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set)) - { - if (val->inherit) { - return g_snprintf(p, len, "%s:inherit;", key); - } else if (val->href && val->href->getURI()) { - gchar *uri = val->href->getURI()->toString(); - gint ret = g_snprintf(p, len, "%s:url(%s);", key, uri); - g_free(uri); - return ret; - } - } - - - return 0; -} - -SPIPaint::SPIPaint() : - set(false), - inherit(0), - currentcolor(0), - colorSet(0), - noneSet(0), - value() -{ - value.color.set( 0 ); - value.href = 0; -} - -void SPIPaint::clear() -{ - set = false; - inherit = false; - currentcolor = false; - colorSet = false; - noneSet = false; - value.color.set( 0 ); - if (value.href){ - if (value.href->getObject()){ - value.href->detach(); - } - } -} - -/** - * Clear filter object, and disconnect style from paintserver (if present). - */ -static void -sp_style_filter_clear(SPStyle *style) -{ - if (style->filter.href){ - if (style->filter.href->getObject()){ - style->filter.href->detach(); - } - } + return g_strdup( from->write( SP_STYLE_FLAG_IFDIFF, to ).c_str() ); } @@ -4894,7 +1522,7 @@ sp_style_set_property_url (SPObject *item, gchar const *property, SPObject *link sp_repr_css_attr_unref(css); } - +// Called in sp-object.cpp /** * Clear all style property attributes in object. */ @@ -4948,16 +1576,16 @@ sp_style_unset_property_attrs(SPObject *o) if (style->stroke_linejoin.set) { repr->setAttribute("stroke-linejoin", NULL); } - if (style->marker[SP_MARKER_LOC].set) { + if (style->marker.set) { repr->setAttribute("marker", NULL); } - if (style->marker[SP_MARKER_LOC_START].set) { + if (style->marker_start.set) { repr->setAttribute("marker-start", NULL); } - if (style->marker[SP_MARKER_LOC_MID].set) { + if (style->marker_mid.set) { repr->setAttribute("marker-mid", NULL); } - if (style->marker[SP_MARKER_LOC_END].set) { + if (style->marker_end.set) { repr->setAttribute("marker-end", NULL); } if (style->stroke_opacity.set) { @@ -4972,10 +1600,10 @@ sp_style_unset_property_attrs(SPObject *o) if (style->paint_order.set) { repr->setAttribute("paint-order", NULL); } - if (style->text_private && style->text->font_specification.set) { + if (style->font_specification.set) { repr->setAttribute("-inkscape-font-specification", NULL); } - if (style->text_private && style->text->font_family.set) { + if (style->font_family.set) { repr->setAttribute("font-family", NULL); } if (style->text_anchor.set) { @@ -5025,7 +1653,8 @@ sp_css_attr_from_style(SPStyle const *const style, guint const flags) return css; } - +// Called in: selection-chemistry.cpp, widgets/stroke-marker-selector.cpp, widgets/stroke-style.cpp, +// ui/tools/freehand-base.cpp /** * \pre object != NULL * \pre flags in {IFSET, ALWAYS}. @@ -5042,13 +1671,14 @@ SPCSSAttr *sp_css_attr_from_object(SPObject *object, guint const flags) return result; } +// Called in: selection-chemistry.cpp, ui/dialog/inkscape-preferences.cpp /** * Unset any text-related properties */ SPCSSAttr * sp_css_attr_unset_text(SPCSSAttr *css) { - sp_repr_css_set_property(css, "font", NULL); // not implemented yet + sp_repr_css_set_property(css, "font", NULL); sp_repr_css_set_property(css, "-inkscape-font-specification", NULL); sp_repr_css_set_property(css, "font-size", NULL); sp_repr_css_set_property(css, "font-size-adjust", NULL); // not implemented yet @@ -5076,6 +1706,7 @@ sp_css_attr_unset_text(SPCSSAttr *css) return css; } +// Called in style.cpp static bool is_url(char const *p) { @@ -5088,6 +1719,7 @@ is_url(char const *p) return (g_ascii_strncasecmp(p, "url(", 4) == 0); } +// Called in: ui/dialog/inkscape-preferences.cpp, ui/tools/tweek-tool.cpp /** * Unset any properties that contain URI values. * @@ -5113,6 +1745,7 @@ sp_css_attr_unset_uris(SPCSSAttr *css) return css; } +// Called in style.cpp /** * Scale a single-value property. */ @@ -5137,6 +1770,7 @@ sp_css_attr_scale_property_single(SPCSSAttr *css, gchar const *property, } } +// Called in style.cpp for stroke-dasharray /** * Scale a list-of-values property. */ @@ -5169,6 +1803,7 @@ sp_css_attr_scale_property_list(SPCSSAttr *css, gchar const *property, double ex } } +// Called in: text-editing.cpp, /** * Scale any properties that may hold <length> by ex. */ @@ -5189,6 +1824,7 @@ sp_css_attr_scale(SPCSSAttr *css, double ex) } +// Called in style.cpp, xml/repr-css.cpp /** * Remove quotes and escapes from a string. Returned value must be g_free'd. * Note: in CSS (in style= and in stylesheets), unquoting and unescaping is done @@ -5215,6 +1851,7 @@ attribute_unquote(gchar const *val) return (val? g_strdup (val) : NULL); } +// Called in style.cpp, xml/repr-css.cpp /** * Quote and/or escape string for writing to CSS (style=). Returned value must be g_free'd. */ diff --git a/src/style.h b/src/style.h index 939ace0d3..9e592b78f 100644 --- a/src/style.h +++ b/src/style.h @@ -7,7 +7,9 @@ /* Authors: * Lauris Kaplinski <lauris@kaplinski.com> * Jon A. Cruz <jon@joncruz.org> + * Tavmjong Bah <tavmjong@free.fr> * + * Copyright (C) 2014 Tavmjong Bah * Copyright (C) 2010 Jon A. Cruz * Copyright (C) 2001-2002 Lauris Kaplinski * Copyright (C) 2001 Ximian, Inc. @@ -20,6 +22,15 @@ #include <stddef.h> #include <sigc++/connection.h> +#include <iostream> +#include <vector> +// #include <map> + +// Define SPIBasePtr, a Pointer to a data member of SPStyle of type SPIBase; +typedef SPIBase SPStyle::*SPIBasePtr; + +// Define SPPropMap, a map linking property name to property data +// typedef std::map<std::string, SPIBasePtr> SPPropMap; namespace Inkscape { namespace XML { @@ -27,24 +38,63 @@ class Node; } } +#include "libcroco/cr-declaration.h" +#include "libcroco/cr-prop-list.h" +//struct CRDeclaration; +//struct CRPropList; + + /// An SVG style object. -struct SPStyle { - int refcount; +class SPStyle { +public: + + SPStyle(SPDocument *document = NULL, SPObject *object = NULL);// document is ignored if valid object given + ~SPStyle(); + void clear(); + void read(SPObject *object, Inkscape::XML::Node *repr); + void readFromObject(SPObject *object); + void readFromPrefs(Glib::ustring const &path); + void readIfUnset( gint id, gchar const *val ); + Glib::ustring write( guint const flags, SPStyle const *const base = NULL ) const; + void cascade( SPStyle const *const parent ); + void merge( SPStyle const *const parent ); + bool operator==(const SPStyle& rhs); + + int ref() { ++_refcount; return _refcount; } + int unref() { --_refcount; return _refcount; } + +//FIXME: Make private +public: + void _mergeString( gchar const *const p ); // Rename to readFromString? +private: + void _mergeDeclList( CRDeclaration const *const decl_list ); + void _mergeDecl( CRDeclaration const *const decl ); + void _mergeProps( CRPropList *const props ); + void _mergeObjectStylesheet( SPObject const *const object ); + +private: + int _refcount; + static int _count; // Poor man's leak detector + +// FIXME: Make private +public: /** Object we are attached to */ SPObject *object; /** Document we are associated with */ SPDocument *document; - /** Our text style component */ - SPTextStyle *text; - unsigned text_private : 1; +private: + /// Pointers to all the properties (for looping through them) + std::vector<SPIBase *> _properties; + // static SPPropMap _propmap; + +public: + + /* ----------------------- THE PROPERTIES ------------------------- */ - /* CSS2 */ /* Font */ - /** Size of the font */ - SPIFontSize font_size; - /** Style of the font */ + /** Font style */ SPIEnum font_style; /** Which substyle of the font */ SPIEnum font_variant; @@ -52,26 +102,34 @@ struct SPStyle { SPIEnum font_weight; /** Stretch of the font */ SPIEnum font_stretch; + /** Size of the font */ + SPIFontSize font_size; + /** Line height (css2 10.8.1) */ + SPILengthOrNormal line_height; + /** Font family */ + SPIString font_family; + /** Font shorthand */ + SPIFont font; + /** Full font name, as font_factory::ConstructFontSpecification would give, for internal use. */ + SPIString font_specification; /** First line indent of paragraphs (css2 16.1) */ SPILength text_indent; /** text alignment (css2 16.2) (not to be confused with text-anchor) */ SPIEnum text_align; - /** text decoration (css2 16.3.1) is now handled as a subset of css3 2.4 */ - // SPITextDecoration text_decoration; - + + /** text decoration (css2 16.3.1) */ + SPITextDecoration text_decoration; /** CSS 3 2.1, 2.2, 2.3 */ /** Not done yet, test_decoration3 = css3 2.4*/ SPITextDecorationLine text_decoration_line; - SPIPaint text_decoration_color; - SPITextDecorationStyle text_decoration_style; - + SPITextDecorationStyle text_decoration_style; // SPIEnum? Only one can be set at time. + SPIColor text_decoration_color; // used to implement text_decoration, not saved to or read from SVG file SPITextDecorationData text_decoration_data; // 16.3.2 is text-shadow. That's complicated. - /** Line spacing (css2 10.8.1) */ - SPILengthOrNormal line_height; + /** letter spacing (css2 16.4) */ SPILengthOrNormal letter_spacing; /** word spacing (also css2 16.4) */ @@ -93,14 +151,6 @@ struct SPStyle { /** Anchor of the text (svg1.1 10.9.1) */ SPIEnum text_anchor; - /* Misc attributes */ - unsigned clip_set : 1; - unsigned color_set : 1; - unsigned cursor_set : 1; - unsigned overflow_set : 1; - unsigned clip_path_set : 1; - unsigned mask_set : 1; - /** clip-rule: 0 nonzero, 1 evenodd */ SPIEnum clip_rule; @@ -121,8 +171,10 @@ struct SPStyle { // Could be shared with Filter blending mode SPIEnum blend_mode; + SPIPaintOrder paint_order; + /** color */ - SPIPaint color; + SPIColor color; /** color-interpolation */ SPIEnum color_interpolation; /** color-interpolation-filters */ @@ -153,18 +205,22 @@ struct SPStyle { SPIScale24 stroke_opacity; /** Marker list */ - SPIString marker[SP_MARKER_LOC_QTY]; - - SPIPaintOrder paint_order; + SPIString marker; + SPIString marker_start; + SPIString marker_mid; + SPIString marker_end; + SPIString* marker_ptrs[SP_MARKER_LOC_QTY]; /** Filter effect */ SPIFilter filter; - + /** Filter blend mode */ SPIEnum filter_blend_mode; - - /** normally not used, but duplicates the Gaussian blur deviation (if any) from the attached + /** normally not used, but duplicates the Gaussian blur deviation (if any) from the attached filter when the style is used for querying */ SPILength filter_gaussianBlur_deviation; + /** enable-background, used for defining where filter effects get their background image */ + SPIEnum enable_background; + /** hints on how to render: e.g. speed vs. accuracy. * As of April, 2013, only image_rendering used. */ @@ -173,9 +229,7 @@ struct SPStyle { SPIEnum shape_rendering; SPIEnum text_rendering; - /** enable-background, used for defining where filter effects get - * their background image */ - SPIEnum enable_background; + /* ----------------------- END PROPERTIES ------------------------- */ /// style belongs to a cloned object bool cloned; @@ -186,46 +240,46 @@ struct SPStyle { sigc::connection fill_ps_modified_connection; sigc::connection stroke_ps_modified_connection; - SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : NULL; } - SPObject const *getFilter() const { return (filter.href) ? filter.href->getObject() : NULL; } - gchar const *getFilterURI() const { return (filter.href) ? filter.href->getURI()->toString() : NULL; } + SPObject *getFilter() { return (filter.href) ? filter.href->getObject() : NULL; } + SPObject const *getFilter() const { return (filter.href) ? filter.href->getObject() : NULL; } + gchar const *getFilterURI() const { return (filter.href) ? filter.href->getURI()->toString() : NULL; } - SPPaintServer *getFillPaintServer() { return (fill.value.href) ? fill.value.href->getObject() : NULL; } - SPPaintServer const *getFillPaintServer() const { return (fill.value.href) ? fill.value.href->getObject() : NULL; } - gchar const *getFillURI() const { return (fill.value.href) ? fill.value.href->getURI()->toString() : NULL; } + SPPaintServer *getFillPaintServer() { return (fill.value.href) ? fill.value.href->getObject() : NULL; } + SPPaintServer const *getFillPaintServer() const { return (fill.value.href) ? fill.value.href->getObject() : NULL; } + gchar const *getFillURI() const { return (fill.value.href) ? fill.value.href->getURI()->toString() : NULL; } - SPPaintServer *getStrokePaintServer() { return (stroke.value.href) ? stroke.value.href->getObject() : NULL; } + SPPaintServer *getStrokePaintServer() { return (stroke.value.href) ? stroke.value.href->getObject() : NULL; } SPPaintServer const *getStrokePaintServer() const { return (stroke.value.href) ? stroke.value.href->getObject() : NULL; } - gchar const *getStrokeURI() const { return (stroke.value.href) ? stroke.value.href->getURI()->toString() : NULL; } + gchar const *getStrokeURI() const { return (stroke.value.href) ? stroke.value.href->getURI()->toString() : NULL; } }; -SPStyle *sp_style_new(SPDocument *document); +SPStyle *sp_style_new(SPDocument *document); // SPStyle::SPStyle( SPDocument *document = NULL ); -SPStyle *sp_style_new_from_object(SPObject *object); +SPStyle *sp_style_new_from_object(SPObject *object); // SPStyle::SPStyle( SPObject *object ); -SPStyle *sp_style_ref(SPStyle *style); +SPStyle *sp_style_ref(SPStyle *style); // SPStyle::ref(); -SPStyle *sp_style_unref(SPStyle *style); +SPStyle *sp_style_unref(SPStyle *style); // SPStyle::unref(); -void sp_style_read_from_object(SPStyle *style, SPObject *object); +void sp_style_read_from_object(SPStyle *style, SPObject *object); //SPStyle::read( SPObject * object); -void sp_style_read_from_prefs(SPStyle *style, Glib::ustring const &path); +void sp_style_read_from_prefs(SPStyle *style, Glib::ustring const &path); // SPStyle::read( ... ); -void sp_style_merge_from_style_string(SPStyle *style, gchar const *p); +void sp_style_merge_from_style_string(SPStyle *style, gchar const *p); // SPStyle::merge( ... );? -void sp_style_merge_from_parent(SPStyle *style, SPStyle const *parent); +void sp_style_merge_from_parent(SPStyle *style, SPStyle const *parent); // SPStyle::cascade( ... ); -void sp_style_merge_from_dying_parent(SPStyle *style, SPStyle const *parent); +void sp_style_merge_from_dying_parent(SPStyle *style, SPStyle const *parent); // SPStyle::merge( ... ) -gchar *sp_style_write_string(SPStyle const *style, guint flags = SP_STYLE_FLAG_IFSET); +gchar *sp_style_write_string(SPStyle const *style, guint flags = SP_STYLE_FLAG_IFSET);//SPStyle::write -gchar *sp_style_write_difference(SPStyle const *from, SPStyle const *to); +gchar *sp_style_write_difference(SPStyle const *from, SPStyle const *to); // SPStyle::write -void sp_style_set_to_uri_string (SPStyle *style, bool isfill, const gchar *uri); +void sp_style_set_to_uri_string (SPStyle *style, bool isfill, const gchar *uri); // ? -gchar const *sp_style_get_css_unit_string(int unit); -double sp_style_css_size_px_to_units(double size, int unit); -double sp_style_css_size_units_to_px(double size, int unit); +gchar const *sp_style_get_css_unit_string(int unit); // No change? +double sp_style_css_size_px_to_units(double size, int unit); // No change? +double sp_style_css_size_units_to_px(double size, int unit); // No change? SPCSSAttr *sp_css_attr_from_style (SPStyle const *const style, guint flags); @@ -254,3 +308,4 @@ Glib::ustring css2_escape_quote(gchar const *val); End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : + diff --git a/src/text-editing.h b/src/text-editing.h index 04ef6461d..290a39194 100644 --- a/src/text-editing.h +++ b/src/text-editing.h @@ -18,11 +18,11 @@ #include "libnrtype/Layout-TNG.h" #include "text-tag-attributes.h" -class SPCSSAttr; -class SPDesktop; -class SPItem; -class SPObject; -struct SPStyle; +class SPCSSAttr; +class SPDesktop; +class SPItem; +class SPObject; +class SPStyle; typedef std::pair<Inkscape::Text::Layout::iterator, Inkscape::Text::Layout::iterator> iterator_pair; diff --git a/src/ui/dialog/font-substitution.cpp b/src/ui/dialog/font-substitution.cpp index bf9133086..6e9ecc3c8 100644 --- a/src/ui/dialog/font-substitution.cpp +++ b/src/ui/dialog/font-substitution.cpp @@ -200,16 +200,16 @@ GSList * FontSubstitution::getFontReplacedItems(SPDocument* doc, Glib::ustring * } } - if (style && style->text) { + if (style) { gchar const *style_font = NULL; - if (style->text->font_family.set) - style_font = style->text->font_family.value; - else if (style->text->font_specification.set) - style_font = style->text->font_specification.value; - else if (style->text->font_family.value) - style_font = style->text->font_family.value; - else if (style->text->font_specification.value) - style_font = style->text->font_specification.value; + if (style->font_family.set) + style_font = style->font_family.value; + else if (style->font_specification.set) + style_font = style->font_specification.value; + else if (style->font_family.value) + style_font = style->font_family.value; + else if (style->font_specification.value) + style_font = style->font_specification.value; if (style_font) { if (has_visible_text(item)) { diff --git a/src/ui/dialog/livepatheffect-add.cpp b/src/ui/dialog/livepatheffect-add.cpp index b5f51d81d..c558eddaf 100644 --- a/src/ui/dialog/livepatheffect-add.cpp +++ b/src/ui/dialog/livepatheffect-add.cpp @@ -38,6 +38,7 @@ LivePathEffectAdd::LivePathEffectAdd() : scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scrolled_window.set_shadow_type(Gtk::SHADOW_IN); scrolled_window.set_size_request(250, 200); + scrolled_window.set_can_focus(); /** * Effect Store and Tree @@ -83,10 +84,12 @@ LivePathEffectAdd::LivePathEffectAdd() : add_action_widget(close_button, Gtk::RESPONSE_CLOSE); add_action_widget(add_button, Gtk::RESPONSE_APPLY); + /** * Signal handlers */ effectlist_treeview.signal_button_press_event().connect_notify( sigc::mem_fun(*this, &LivePathEffectAdd::onButtonEvent) ); + effectlist_treeview.signal_key_press_event().connect_notify(sigc::mem_fun(*this, &LivePathEffectAdd::onKeyEvent)); close_button.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectAdd::onClose)); add_button.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectAdd::onAdd)); signal_delete_event().connect( sigc::bind_return(sigc::hide(sigc::mem_fun(*this, &LivePathEffectAdd::onClose)), true ) ); @@ -107,6 +110,16 @@ void LivePathEffectAdd::onClose() hide(); } +void LivePathEffectAdd::onKeyEvent(GdkEventKey* evt) +{ + if (evt->keyval == GDK_KEY_Return) { + onAdd(); + } + if (evt->keyval == GDK_KEY_Escape) { + onClose(); + } +} + void LivePathEffectAdd::onButtonEvent(GdkEventButton* evt) { // Double click on tree is same as clicking the add button @@ -135,6 +148,7 @@ void LivePathEffectAdd::show(SPDesktop *desktop) dial.set_modal(true); desktop->setWindowTransient (dial.gobj()); dial.property_destroy_with_parent() = true; + dial.effectlist_treeview.grab_focus(); dial.run(); } diff --git a/src/ui/dialog/livepatheffect-add.h b/src/ui/dialog/livepatheffect-add.h index 7fa766272..99ead878c 100644 --- a/src/ui/dialog/livepatheffect-add.h +++ b/src/ui/dialog/livepatheffect-add.h @@ -74,6 +74,10 @@ protected: */ void onButtonEvent(GdkEventButton* evt); + /** + * Key event + */ + void onKeyEvent(GdkEventKey* evt); private: Gtk::TreeView effectlist_treeview; diff --git a/src/ui/widget/style-subject.h b/src/ui/widget/style-subject.h index c2941d995..47da91732 100644 --- a/src/ui/widget/style-subject.h +++ b/src/ui/widget/style-subject.h @@ -20,7 +20,7 @@ class SPDesktop; class SPObject; class SPCSSAttr; -struct SPStyle; +class SPStyle; namespace Inkscape { class Selection; diff --git a/src/ui/widget/style-swatch.h b/src/ui/widget/style-swatch.h index 23ecbdfda..557ca82e2 100644 --- a/src/ui/widget/style-swatch.h +++ b/src/ui/widget/style-swatch.h @@ -29,7 +29,7 @@ #include "desktop.h" #include "preferences.h" -struct SPStyle; +class SPStyle; class SPCSSAttr; namespace Gtk { diff --git a/src/widgets/paint-selector.h b/src/widgets/paint-selector.h index d3b3f4116..d6ad3f50c 100644 --- a/src/widgets/paint-selector.h +++ b/src/widgets/paint-selector.h @@ -23,7 +23,7 @@ class SPGradient; class SPDesktop; class SPPattern; -struct SPStyle; +class SPStyle; #define SP_TYPE_PAINT_SELECTOR (sp_paint_selector_get_type ()) #define SP_PAINT_SELECTOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), SP_TYPE_PAINT_SELECTOR, SPPaintSelector)) diff --git a/src/widgets/stroke-style.cpp b/src/widgets/stroke-style.cpp index a4cca9472..0e0a4fd72 100644 --- a/src/widgets/stroke-style.cpp +++ b/src/widgets/stroke-style.cpp @@ -581,7 +581,8 @@ StrokeStyle::forkMarker(SPObject *marker, int loc, SPItem *item) Glib::ustring urlId = Glib::ustring::format("url(#", marker->getRepr()->attribute("id"), ")"); unsigned int refs = 0; for (int i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) { - if (item->style->marker[i].set && !strcmp(urlId.c_str(), item->style->marker[i].value)) { + if (item->style->marker_ptrs[i]->set && + !strcmp(urlId.c_str(), item->style->marker_ptrs[i]->value)) { refs++; } } @@ -1176,11 +1177,11 @@ StrokeStyle::updateAllMarkers(GSList const *objects) combo->setDesktop(desktop); - if (object->style->marker[keyloc[i].loc].value != NULL && !all_texts) { + if (object->style->marker_ptrs[keyloc[i].loc]->value != NULL && !all_texts) { // If the object has this type of markers, // Extract the name of the marker that the object uses - SPObject *marker = getMarkerObj(object->style->marker[keyloc[i].loc].value, object->document); + SPObject *marker = getMarkerObj(object->style->marker_ptrs[keyloc[i].loc]->value, object->document); // Scroll the combobox to that marker combo->set_current(marker); diff --git a/src/widgets/text-toolbar.cpp b/src/widgets/text-toolbar.cpp index 7b59fa633..3a4f315da 100644 --- a/src/widgets/text-toolbar.cpp +++ b/src/widgets/text-toolbar.cpp @@ -72,20 +72,20 @@ using Inkscape::UI::PrefPusher; static void sp_print_font( SPStyle *query ) { - bool family_set = query->text->font_family.set; + bool family_set = query->font_family.set; bool style_set = query->font_style.set; - bool fontspec_set = query->text->font_specification.set; + bool fontspec_set = query->font_specification.set; std::cout << " Family set? " << family_set << " Style set? " << style_set << " FontSpec set? " << fontspec_set << std::endl; std::cout << " Family: " - << (query->text->font_family.value ? query->text->font_family.value : "No value") + << (query->font_family.value ? query->font_family.value : "No value") << " Style: " << query->font_style.computed << " Weight: " << query->font_weight.computed << " FontSpec: " - << (query->text->font_specification.value ? query->text->font_specification.value : "No value") + << (query->font_specification.value ? query->font_specification.value : "No value") << std::endl; std::cout << " LineHeight: " << query->line_height.computed << " WordSpacing: " << query->word_spacing.computed @@ -930,7 +930,6 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ } // If we have valid query data for text (font-family, font-specification) set toolbar accordingly. - if (query->text) { // Size (average of text selected) Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -1058,11 +1057,11 @@ static void sp_text_toolbox_selection_changed(Inkscape::Selection */*selection*/ ege_select_one_action_set_active( textOrientationAction, activeButton2 ); - } // if( query->text ) + } #ifdef DEBUG_TEXT std::cout << " GUI: fontfamily.value: " - << (query->text->font_family.value ? query->text->font_family.value : "No value") + << (query->font_family.value ? query->font_family.value : "No value") << std::endl; std::cout << " GUI: font_size.computed: " << query->font_size.computed << std::endl; std::cout << " GUI: font_weight.computed: " << query->font_weight.computed << std::endl; @@ -1174,15 +1173,15 @@ static void sp_text_toolbox_select_cb( GtkEntry* entry, GtkEntryIconPosition /*p SPItem *item = SP_ITEM(i->data); SPStyle *style = item->style; - if (style && style->text) { + if (style) { Glib::ustring family_style; - if (style->text->font_family.set) { - family_style = style->text->font_family.value; + if (style->font_family.set) { + family_style = style->font_family.value; //std::cout << " family style from font_family: " << family_style << std::endl; } - else if (style->text->font_specification.set) { - family_style = style->text->font_specification.value; + else if (style->font_specification.set) { + family_style = style->font_specification.value; //std::cout << " family style from font_spec: " << family_style << std::endl; } |
