summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/Makefile_insert1
-rw-r--r--src/attribute-sort-util.cpp206
-rw-r--r--src/attribute-sort-util.h62
-rw-r--r--src/attributes.cpp91
-rw-r--r--src/attributes.h101
-rw-r--r--src/preferences-skeleton.h3
-rw-r--r--src/xml/repr-io.cpp5
8 files changed, 383 insertions, 88 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index cf5130317..66f16b7fb 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -10,6 +10,7 @@ set(sp_SRC
attribute-rel-css.cpp
attribute-rel-svg.cpp
attribute-rel-util.cpp
+ attribute-sort-util.cpp
sp-anchor.cpp
sp-clippath.cpp
sp-conn-end-pair.cpp
@@ -92,6 +93,7 @@ set(sp_SRC
attribute-rel-css.h
attribute-rel-svg.h
attribute-rel-util.h
+ attribute-sort-util.h
sp-anchor.h
sp-clippath.h
sp-conn-end-pair.h
diff --git a/src/Makefile_insert b/src/Makefile_insert
index b9723ac42..55fde4dd2 100644
--- a/src/Makefile_insert
+++ b/src/Makefile_insert
@@ -8,6 +8,7 @@ ink_common_sources += \
attribute-rel-svg.cpp attribute-rel-svg.h \
attribute-rel-css.cpp attribute-rel-css.h \
attribute-rel-util.cpp attribute-rel-util.h \
+ attribute-sort-util.cpp attribute-sort-util.h \
axis-manip.cpp axis-manip.h \
bad-uri-exception.h \
box3d.cpp box3d.h \
diff --git a/src/attribute-sort-util.cpp b/src/attribute-sort-util.cpp
new file mode 100644
index 000000000..ef08a142f
--- /dev/null
+++ b/src/attribute-sort-util.cpp
@@ -0,0 +1,206 @@
+/*
+ * attribute-sort-util.cpp
+ *
+ * Created on: Jun 10, 2016
+ * Author: Tavmjong Bah
+ */
+
+/**
+ * Utility functions for sorting attributes by name.
+ */
+
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <iostream>
+#include <vector>
+#include <utility> // std::pair
+#include <algorithm> // std::sort
+
+#include "attribute-sort-util.h"
+
+#include "xml/repr.h"
+#include "xml/attribute-record.h"
+#include "xml/sp-css-attr.h"
+
+#include "attributes.h"
+
+using Inkscape::XML::Node;
+using Inkscape::XML::AttributeRecord;
+using Inkscape::Util::List;
+
+/**
+ * Sort attributes by name.
+ */
+void sp_attribute_sort_tree(Node *repr) {
+
+ g_return_if_fail (repr != NULL);
+
+ sp_attribute_sort_recursive( repr );
+}
+
+/**
+ * Sort recursively over all elements.
+ */
+void sp_attribute_sort_recursive(Node *repr) {
+
+ g_return_if_fail (repr != NULL);
+
+ if( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
+ Glib::ustring element = repr->name();
+
+ // Only sort elements in svg namespace
+ if( element.substr(0,4) == "svg:" ) {
+ sp_attribute_sort_element( repr );
+ }
+ }
+
+ for(Node *child=repr->firstChild() ; child ; child = child->next()) {
+ sp_attribute_sort_recursive( child );
+ }
+}
+
+/**
+ * Compare function
+ */
+bool cmp(std::pair< Glib::ustring, Glib::ustring > const &a,
+ std::pair< Glib::ustring, Glib::ustring > const &b) {
+ unsigned val_b = sp_attribute_lookup(b.first.c_str());
+ if (val_b == 0) return true; // Unknown attributes at end.
+ return sp_attribute_lookup(a.first.c_str()) < val_b;
+}
+
+/**
+ * Sort attributes on an element
+ */
+void sp_attribute_sort_element(Node *repr) {
+
+ g_return_if_fail (repr != NULL);
+ g_return_if_fail (repr->type() == Inkscape::XML::ELEMENT_NODE);
+
+ // Glib::ustring element = repr->name();
+ // Glib::ustring id = (repr->attribute( "id" )==NULL ? "" : repr->attribute( "id" ));
+
+ sp_attribute_sort_style(repr);
+
+ // Sort attributes:
+
+ // It doesn't seem possible to sort a List directly so we dump the list into
+ // a std::list and sort that. Not very efficient. Sad.
+ List<AttributeRecord const> attributes = repr->attributeList();
+
+ std::vector<std::pair< Glib::ustring, Glib::ustring > > my_list;
+ for ( List<AttributeRecord const> iter = attributes ; iter ; ++iter ) {
+
+ Glib::ustring attribute = g_quark_to_string(iter->key);
+ Glib::ustring value = (const char*)iter->value;
+
+ // C++11 my_list.emlace_back(attribute, value);
+ my_list.push_back(std::make_pair(attribute,value));
+ }
+ std::sort(my_list.begin(), my_list.end(), cmp);
+ // Delete all attributes.
+ //for (auto it: my_list) {
+ for (std::vector<std::pair< Glib::ustring, Glib::ustring > >::iterator it = my_list.begin();
+ it != my_list.end(); ++it) {
+ repr->setAttribute( it->first.c_str(), NULL, false );
+ }
+ // Insert all attributes in proper order
+ for (std::vector<std::pair< Glib::ustring, Glib::ustring > >::iterator it = my_list.begin();
+ it != my_list.end(); ++it) {
+ repr->setAttribute( it->first.c_str(), it->second.c_str(), false );
+ }
+}
+
+
+/**
+ * Sort CSS style on an element.
+ */
+void sp_attribute_sort_style(Node *repr) {
+
+ g_return_if_fail (repr != NULL);
+ g_return_if_fail (repr->type() == Inkscape::XML::ELEMENT_NODE);
+
+ // Find element's style
+ SPCSSAttr *css = sp_repr_css_attr( repr, "style" );
+ sp_attribute_sort_style(repr, css);
+
+ // Convert css node's properties data to string and set repr node's attribute "style" to that string.
+ // sp_repr_css_set( repr, css, "style"); // Don't use as it will cause loop.
+ Glib::ustring value;
+ sp_repr_css_write_string(css, value);
+ if( value.empty() ) {
+ repr->setAttribute("style", NULL );
+ } else {
+ repr->setAttribute("style", value.c_str());
+ }
+
+ sp_repr_css_attr_unref( css );
+}
+
+
+/**
+ * Sort CSS style on an element.
+ */
+Glib::ustring sp_attribute_sort_style(Node *repr, gchar const *string) {
+
+ g_return_val_if_fail (repr != NULL, NULL);
+ g_return_val_if_fail (repr->type() == Inkscape::XML::ELEMENT_NODE, NULL);
+
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_attr_add_from_string( css, string );
+ sp_attribute_sort_style(repr, css);
+ Glib::ustring string_cleaned;
+ sp_repr_css_write_string (css, string_cleaned);
+
+ sp_repr_css_attr_unref( css );
+
+ return string_cleaned;
+}
+
+
+/**
+ * Sort CSS style on an element.
+ */
+void sp_attribute_sort_style(Node* repr, SPCSSAttr *css) {
+
+ g_return_if_fail (repr != NULL);
+ g_return_if_fail (css != NULL);
+
+ Glib::ustring element = repr->name();
+ Glib::ustring id = (repr->attribute( "id" )==NULL ? "" : repr->attribute( "id" ));
+
+ // Loop over all properties in "style" node.
+ std::vector<std::pair< Glib::ustring, Glib::ustring > > my_list;
+ for ( List<AttributeRecord const> iter = css->attributeList() ; iter ; ++iter ) {
+
+ Glib::ustring property = g_quark_to_string(iter->key);
+ Glib::ustring value = (const char*)iter->value;
+
+ // C++11 my_list.emlace_back(property, value);
+ my_list.push_back(std::make_pair(property,value));
+ }
+ std::sort(my_list.begin(), my_list.end(), cmp);
+ // Delete all attributes.
+ //for (auto it: my_list) {
+ for (std::vector<std::pair< Glib::ustring, Glib::ustring > >::iterator it = my_list.begin();
+ it != my_list.end(); ++it) {
+ sp_repr_css_set_property( css, it->first.c_str(), NULL );
+ }
+ // Insert all attributes in proper order
+ for (std::vector<std::pair< Glib::ustring, Glib::ustring > >::iterator it = my_list.begin();
+ it != my_list.end(); ++it) {
+ sp_repr_css_set_property( css, it->first.c_str(), it->second.c_str() );
+ }
+}
+
+/*
+ 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 :
diff --git a/src/attribute-sort-util.h b/src/attribute-sort-util.h
new file mode 100644
index 000000000..0b2f81ed9
--- /dev/null
+++ b/src/attribute-sort-util.h
@@ -0,0 +1,62 @@
+#ifndef __SP_ATTRIBUTE_SORT_UTIL_H__
+#define __SP_ATTRIBUTE_SORT_UTIL_H__
+
+/*
+ * attribute-sort-util.h
+ *
+ * Created on: Jun 10, 2016
+ * Author: Tavmjong Bah
+ */
+
+#include <glibmm/ustring.h>
+#include "xml/sp-css-attr.h"
+
+using Inkscape::XML::Node;
+
+/**
+ * Utility functions for sorting attributes.
+ */
+
+/**
+ * Sort attributes by name.
+ */
+void sp_attribute_sort_tree(Node *repr);
+
+/**
+ * Recursively sort.
+ * repr: the root node in a document or any other node.
+ */
+void sp_attribute_sort_recursive(Node *repr);
+
+/**
+ * Sort one element (attributes).
+ */
+void sp_attribute_sort_element(Node *repr);
+
+/**
+ * Sort style properties for one element.
+ */
+void sp_attribute_sort_style(Node *repr);
+
+/**
+ * Sort style_property for a style string
+ */
+Glib::ustring sp_attribute_sort_style(Node *repr, gchar const *string);
+
+/**
+ * Sort style properties for one CSS.
+ */
+void sp_attribute_sort_style(Node* repr, SPCSSAttr *css);
+
+#endif /* __SP_ATTRIBUTE_SORT_UTIL_H__ */
+
+/*
+ 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/attributes.cpp b/src/attributes.cpp
index 24c16d5fc..e281dad65 100644
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
@@ -420,11 +420,49 @@ static SPStyleProp const props[] = {
{SP_ATTR_TEXT_EXCLUDE,"inkscape:excludeShape"},
{SP_ATTR_LAYOUT_OPTIONS,"inkscape:layoutOptions"},
- /* CSS2 */
- {SP_PROP_INKSCAPE_FONT_SPEC, "-inkscape-font-specification"},
+ /* CSS & SVG Properites */
+
+ /* Paint */
+ {SP_PROP_COLOR, "color"},
+ {SP_PROP_OPACITY, "opacity"},
+ {SP_PROP_FILL, "fill"},
+ {SP_PROP_FILL_OPACITY, "fill-opacity"},
+ {SP_PROP_FILL_RULE, "fill-rule"},
+ {SP_PROP_STROKE, "stroke"},
+ {SP_PROP_STROKE_OPACITY, "stroke-opacity"},
+ {SP_PROP_STROKE_WIDTH, "stroke-width"},
+ {SP_PROP_STROKE_LINECAP, "stroke-linecap"},
+ {SP_PROP_STROKE_LINEJOIN, "stroke-linejoin"},
+ {SP_PROP_STROKE_MITERLIMIT, "stroke-miterlimit"},
+ {SP_PROP_STROKE_DASHARRAY, "stroke-dasharray"},
+ {SP_PROP_STROKE_DASHOFFSET, "stroke-dashoffset"},
+ {SP_PROP_MARKER, "marker"},
+ {SP_PROP_MARKER_END, "marker-end"},
+ {SP_PROP_MARKER_MID, "marker-mid"},
+ {SP_PROP_MARKER_START, "marker-start"},
+ {SP_PROP_PAINT_ORDER, "paint-order" },
+ {SP_PROP_SOLID_COLOR, "solid-color"},
+ {SP_PROP_SOLID_OPACITY, "solid-opacity"},
+
+ /* CSS Blending/Compositing */
+ {SP_PROP_MIX_BLEND_MODE, "mix-blend-mode"},
+ {SP_PROP_ISOLATION, "isolation"},
+
+ /* Misc. Display */
+ {SP_PROP_DISPLAY, "display"},
+ {SP_PROP_OVERFLOW, "overflow"},
+ {SP_PROP_VISIBILITY, "visibility"},
+
+ /* Clip/Mask */
+ {SP_PROP_CLIP, "clip"},
+ {SP_PROP_CLIP_PATH, "clip-path"},
+ {SP_PROP_CLIP_RULE, "clip-rule"},
+ {SP_PROP_MASK, "mask"},
+
/* Font */
{SP_PROP_FONT, "font"},
{SP_PROP_FONT_FAMILY, "font-family"},
+ {SP_PROP_INKSCAPE_FONT_SPEC, "-inkscape-font-specification"},
{SP_PROP_FONT_SIZE, "font-size"},
{SP_PROP_FONT_SIZE_ADJUST, "font-size-adjust"},
{SP_PROP_FONT_STRETCH, "font-stretch"},
@@ -449,7 +487,7 @@ static SPStyleProp const props[] = {
{SP_PROP_WORD_SPACING, "word-spacing"},
{SP_PROP_TEXT_TRANSFORM, "text-transform"},
- /* Text (css3) */
+ /* Text (CSS3) */
{SP_PROP_DIRECTION, "direction"},
{SP_PROP_WRITING_MODE, "writing-mode"},
{SP_PROP_TEXT_ORIENTATION, "text-orientation"},
@@ -470,70 +508,43 @@ static SPStyleProp const props[] = {
{SP_PROP_SHAPE_MARGIN, "shape-margin"},
/* Text Decoration */
- {SP_PROP_TEXT_DECORATION, "text-decoration"},
+ {SP_PROP_TEXT_DECORATION, "text-decoration"}, // CSS 2/CSS3-Shorthand
{SP_PROP_TEXT_DECORATION_LINE, "text-decoration-line"},
{SP_PROP_TEXT_DECORATION_STYLE, "text-decoration-style"},
{SP_PROP_TEXT_DECORATION_COLOR, "text-decoration-color"},
{SP_PROP_TEXT_DECORATION_FILL, "text-decoration-fill"},
{SP_PROP_TEXT_DECORATION_STROKE,"text-decoration-stroke"},
- /* Misc */
- {SP_PROP_CLIP, "clip"},
- {SP_PROP_COLOR, "color"},
- {SP_PROP_CURSOR, "cursor"},
- {SP_PROP_DISPLAY, "display"},
- {SP_PROP_OVERFLOW, "overflow"},
- {SP_PROP_VISIBILITY, "visibility"},
- {SP_PROP_MIX_BLEND_MODE, "mix-blend-mode"}, // CSS Blending and Compositing
- {SP_PROP_ISOLATION, "isolation"},
- /* SVG */
- /* Clip/Mask */
- {SP_PROP_CLIP_PATH, "clip-path"},
- {SP_PROP_CLIP_RULE, "clip-rule"},
- {SP_PROP_MASK, "mask"},
- {SP_PROP_OPACITY, "opacity"},
/* Filter */
{SP_PROP_ENABLE_BACKGROUND, "enable-background"},
{SP_PROP_FILTER, "filter"},
{SP_PROP_FLOOD_COLOR, "flood-color"},
{SP_PROP_FLOOD_OPACITY, "flood-opacity"},
{SP_PROP_LIGHTING_COLOR, "lighting-color"},
+
/* Gradient */
{SP_PROP_STOP_COLOR, "stop-color"},
{SP_PROP_STOP_OPACITY, "stop-opacity"},
{SP_PROP_STOP_PATH, "path"},
- /* Interactivity */
- {SP_PROP_POINTER_EVENTS, "pointer-events"},
- /* Paint */
+
+ /* Rendering */
{SP_PROP_COLOR_INTERPOLATION, "color-interpolation"},
{SP_PROP_COLOR_INTERPOLATION_FILTERS, "color-interpolation-filters"},
{SP_PROP_COLOR_PROFILE, "color-profile"},
{SP_PROP_COLOR_RENDERING, "color-rendering"},
- {SP_PROP_FILL, "fill"},
- {SP_PROP_FILL_OPACITY, "fill-opacity"},
- {SP_PROP_FILL_RULE, "fill-rule"},
{SP_PROP_IMAGE_RENDERING, "image-rendering"},
- {SP_PROP_MARKER, "marker"},
- {SP_PROP_MARKER_END, "marker-end"},
- {SP_PROP_MARKER_MID, "marker-mid"},
- {SP_PROP_MARKER_START, "marker-start"},
- {SP_PROP_PAINT_ORDER, "paint-order" },
{SP_PROP_SHAPE_RENDERING, "shape-rendering"},
- {SP_PROP_SOLID_COLOR, "solid-color"},
- {SP_PROP_SOLID_OPACITY, "solid-opacity"},
- {SP_PROP_STROKE, "stroke"},
- {SP_PROP_STROKE_DASHARRAY, "stroke-dasharray"},
- {SP_PROP_STROKE_DASHOFFSET, "stroke-dashoffset"},
- {SP_PROP_STROKE_LINECAP, "stroke-linecap"},
- {SP_PROP_STROKE_LINEJOIN, "stroke-linejoin"},
- {SP_PROP_STROKE_MITERLIMIT, "stroke-miterlimit"},
- {SP_PROP_STROKE_OPACITY, "stroke-opacity"},
- {SP_PROP_STROKE_WIDTH, "stroke-width"},
{SP_PROP_TEXT_RENDERING, "text-rendering"},
+
+ /* Interactivity */
+ {SP_PROP_POINTER_EVENTS, "pointer-events"},
+ {SP_PROP_CURSOR, "cursor"},
+
/* Conditional */
{SP_PROP_SYSTEM_LANGUAGE, "systemLanguage"},
{SP_PROP_REQUIRED_FEATURES, "requiredFeatures"},
{SP_PROP_REQUIRED_EXTENSIONS, "requiredExtensions"},
+
/* LivePathEffect */
{SP_PROP_PATH_EFFECT, "effect"},
};
diff --git a/src/attributes.h b/src/attributes.h
index e82db5f2a..ff426e8cf 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -24,6 +24,13 @@ unsigned char const *sp_attribute_name(unsigned int id);
*/
#define SP_ATTRIBUTE_IS_CSS(k) (((k) >= SP_PROP_INKSCAPE_FONT_SPEC) && ((k) <= SP_PROP_TEXT_RENDERING))
+/*
+ * Do not change order of attributes and properties. Attribute and
+ * order in an SVG file is (optionally) determined by the order here.
+ * This makes comparing different versions of a drawing easier using
+ * line-by-line comparison. Also, inorder for proper parsing, some
+ * properites must be before others (e.g. 'font' before 'font-family').
+ */
enum SPAttributeEnum {
SP_ATTR_INVALID, ///< Must have value 0.
/* SPObject */
@@ -420,14 +427,49 @@ enum SPAttributeEnum {
SP_ATTR_TEXT_EXCLUDE,
SP_ATTR_LAYOUT_OPTIONS,
- /* CSS & SVG Properties */
+ /* CSS & SVG Properties KEEP ORDER */
- /* Custom full font name because Font stuff below is inadequate REMOVE ME */
- SP_PROP_INKSCAPE_FONT_SPEC,
+ /* Paint */
+ SP_PROP_COLOR,
+ SP_PROP_OPACITY,
+ SP_PROP_FILL,
+ SP_PROP_FILL_OPACITY,
+ SP_PROP_FILL_RULE,
+ SP_PROP_STROKE,
+ SP_PROP_STROKE_OPACITY,
+ SP_PROP_STROKE_WIDTH,
+ SP_PROP_STROKE_LINECAP,
+ SP_PROP_STROKE_LINEJOIN,
+ SP_PROP_STROKE_MITERLIMIT,
+ SP_PROP_STROKE_DASHARRAY,
+ SP_PROP_STROKE_DASHOFFSET,
+ SP_PROP_MARKER,
+ SP_PROP_MARKER_END,
+ SP_PROP_MARKER_MID,
+ SP_PROP_MARKER_START,
+ SP_PROP_PAINT_ORDER, /* SVG2 */
+ SP_PROP_SOLID_COLOR,
+ SP_PROP_SOLID_OPACITY,
+
+ /* CSS Blending/Compositing */
+ SP_PROP_MIX_BLEND_MODE,
+ SP_PROP_ISOLATION,
+
+ /* Misc. Display */
+ SP_PROP_DISPLAY,
+ SP_PROP_OVERFLOW,
+ SP_PROP_VISIBILITY,
- /* Font */
+ /* Clip/Mask */
+ SP_PROP_CLIP,
+ SP_PROP_CLIP_PATH,
+ SP_PROP_CLIP_RULE,
+ SP_PROP_MASK,
+
+ /* Font: Order is important! */
SP_PROP_FONT,
SP_PROP_FONT_FAMILY,
+ SP_PROP_INKSCAPE_FONT_SPEC, // Remove me
SP_PROP_FONT_SIZE,
SP_PROP_FONT_SIZE_ADJUST,
SP_PROP_FONT_STRETCH,
@@ -447,12 +489,12 @@ enum SPAttributeEnum {
/* Text Layout */
SP_PROP_TEXT_INDENT,
SP_PROP_TEXT_ALIGN,
-
SP_PROP_LINE_HEIGHT,
SP_PROP_LETTER_SPACING,
SP_PROP_WORD_SPACING,
SP_PROP_TEXT_TRANSFORM,
+ /* Text (CSS3) */
SP_PROP_DIRECTION,
SP_PROP_WRITING_MODE,
SP_PROP_TEXT_ORIENTATION,
@@ -472,32 +514,14 @@ enum SPAttributeEnum {
SP_PROP_SHAPE_PADDING,
SP_PROP_SHAPE_MARGIN,
- /* Text Decoration CSS 2/CSS 3 Shorthand */
- SP_PROP_TEXT_DECORATION,
- /* Text Decoration CSS 3/SVG 2 */
+ /* Text Decoration */
+ SP_PROP_TEXT_DECORATION, // CSS 2/CSS3-Shorthand
SP_PROP_TEXT_DECORATION_LINE,
SP_PROP_TEXT_DECORATION_STYLE,
SP_PROP_TEXT_DECORATION_COLOR,
SP_PROP_TEXT_DECORATION_FILL,
SP_PROP_TEXT_DECORATION_STROKE,
- /* Misc */
- SP_PROP_CLIP,
- SP_PROP_COLOR,
- SP_PROP_CURSOR,
- SP_PROP_DISPLAY,
- SP_PROP_OVERFLOW,
- SP_PROP_VISIBILITY,
- SP_PROP_MIX_BLEND_MODE,
- SP_PROP_ISOLATION,
-
- /* SVG */
- /* Clip/Mask */
- SP_PROP_CLIP_PATH,
- SP_PROP_CLIP_RULE,
- SP_PROP_MASK,
- SP_PROP_OPACITY,
-
/* Filter */
SP_PROP_ENABLE_BACKGROUND,
SP_PROP_FILTER,
@@ -510,36 +534,19 @@ enum SPAttributeEnum {
SP_PROP_STOP_OPACITY,
SP_PROP_STOP_PATH,
- /* Interactivity */
- SP_PROP_POINTER_EVENTS,
-
- /* Paint */
+ /* Rendering */
SP_PROP_COLOR_INTERPOLATION,
SP_PROP_COLOR_INTERPOLATION_FILTERS,
SP_PROP_COLOR_PROFILE,
SP_PROP_COLOR_RENDERING,
- SP_PROP_FILL,
- SP_PROP_FILL_OPACITY,
- SP_PROP_FILL_RULE,
SP_PROP_IMAGE_RENDERING,
- SP_PROP_MARKER,
- SP_PROP_MARKER_END,
- SP_PROP_MARKER_MID,
- SP_PROP_MARKER_START,
- SP_PROP_PAINT_ORDER, /* SVG2 */
SP_PROP_SHAPE_RENDERING,
- SP_PROP_SOLID_COLOR,
- SP_PROP_SOLID_OPACITY,
- SP_PROP_STROKE,
- SP_PROP_STROKE_DASHARRAY,
- SP_PROP_STROKE_DASHOFFSET,
- SP_PROP_STROKE_LINECAP,
- SP_PROP_STROKE_LINEJOIN,
- SP_PROP_STROKE_MITERLIMIT,
- SP_PROP_STROKE_OPACITY,
- SP_PROP_STROKE_WIDTH,
SP_PROP_TEXT_RENDERING,
+ /* Interactivity */
+ SP_PROP_POINTER_EVENTS,
+ SP_PROP_CURSOR,
+
/* Conditional */
SP_PROP_SYSTEM_LANGUAGE,
SP_PROP_REQUIRED_FEATURES,
diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h
index 5ccc4d729..38196a58e 100644
--- a/src/preferences-skeleton.h
+++ b/src/preferences-skeleton.h
@@ -338,7 +338,8 @@ static char const preferences_skeleton[] =
" style_defaults_remove=\"0\" "
" check_on_reading=\"0\" "
" check_on_editing=\"0\" "
-" check_on_writing=\"0\"/>\n"
+" check_on_writing=\"0\" "
+" sort_attributes=\"0\"/>\n"
" <group id=\"externalresources\">\n"
" <group id=\"xml\" "
" allow_net_access=\"0\"/>\n"
diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp
index 4a6f59b43..6977bc1e2 100644
--- a/src/xml/repr-io.cpp
+++ b/src/xml/repr-io.cpp
@@ -34,6 +34,7 @@
#include "extension/extension.h"
#include "attribute-rel-util.h"
+#include "attribute-sort-util.h"
#include "preferences.h"
@@ -882,6 +883,10 @@ static void sp_repr_write_stream_root_element(Node *repr, Writer &out,
bool clean = prefs->getBool("/options/svgoutput/check_on_writing");
if (clean) sp_attribute_clean_tree( repr );
+ // Sort attributes in a canonical order (helps with "diffing" SVG files).
+ bool sort = prefs->getBool("/options/svgoutput/sort_attributes");
+ if (sort) sp_attribute_sort_tree( repr );
+
Glib::QueryQuark xml_prefix=g_quark_from_static_string("xml");
NSMap ns_map;