diff options
Diffstat (limited to 'src/attribute-rel-util.cpp')
-rw-r--r-- | src/attribute-rel-util.cpp | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/src/attribute-rel-util.cpp b/src/attribute-rel-util.cpp new file mode 100644 index 0000000..fd4747e --- /dev/null +++ b/src/attribute-rel-util.cpp @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2018 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +/* + * attribute-rel-util.h + * + * Created on: Sep 8, 2011 + * Author: tavmjong + */ + +/** + * Utility functions for cleaning SVG tree of unneeded attributes and style properties. + */ + +#include <fstream> +#include <sstream> +#include <iostream> + +#include "preferences.h" + +#include "xml/attribute-record.h" + +#include "attribute-rel-css.h" +#include "attribute-rel-svg.h" + +#include "attribute-rel-util.h" + +using Inkscape::XML::Node; +using Inkscape::XML::AttributeRecord; +using Inkscape::Util::List; + +/** + * Get preferences + */ +unsigned int sp_attribute_clean_get_prefs() { + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + unsigned int flags = 0; + if( prefs->getBool("/options/svgoutput/incorrect_attributes_warn") ) flags += SP_ATTR_CLEAN_ATTR_WARN; + if( prefs->getBool("/options/svgoutput/incorrect_attributes_remove") && + !prefs->getBool("/options/svgoutput/disable_optimizations" ) ) flags += SP_ATTR_CLEAN_ATTR_REMOVE; + if( prefs->getBool("/options/svgoutput/incorrect_style_properties_warn") ) flags += SP_ATTR_CLEAN_STYLE_WARN; + if( prefs->getBool("/options/svgoutput/incorrect_style_properties_remove" ) && + !prefs->getBool("/options/svgoutput/disable_optimizations" ) ) flags += SP_ATTR_CLEAN_STYLE_REMOVE; + if( prefs->getBool("/options/svgoutput/style_defaults_warn") ) flags += SP_ATTR_CLEAN_DEFAULT_WARN; + if( prefs->getBool("/options/svgoutput/style_defaults_remove") && + !prefs->getBool("/options/svgoutput/disable_optimizations" ) ) flags += SP_ATTR_CLEAN_DEFAULT_REMOVE; + + return flags; +} + +/** + * Remove or warn about inappropriate attributes and useless stype properties. + * repr: the root node in a document or any other node. + */ +void sp_attribute_clean_tree(Node *repr) { + + g_return_if_fail (repr != nullptr); + + unsigned int flags = sp_attribute_clean_get_prefs(); + + if( flags ) { + sp_attribute_clean_recursive( repr, flags ); + } +} + +/** + * Clean recursively over all elements. + */ +void sp_attribute_clean_recursive(Node *repr, unsigned int flags) { + + g_return_if_fail (repr != nullptr); + + if( repr->type() == Inkscape::XML::ELEMENT_NODE ) { + Glib::ustring element = repr->name(); + + // Only clean elements in svg namespace + if( element.substr(0,4) == "svg:" ) { + sp_attribute_clean_element(repr, flags ); + } + } + + for(Node *child=repr->firstChild() ; child ; child = child->next()) { + + // Don't remove default css values if element is in <defs> or is a <symbol> + Glib::ustring element = child->name(); + unsigned int flags_temp = flags; + if( element.compare( "svg:defs" ) == 0 || element.compare( "svg:symbol" ) == 0 ) { + flags_temp &= ~(SP_ATTR_CLEAN_DEFAULT_WARN|SP_ATTR_CLEAN_DEFAULT_REMOVE); + } + sp_attribute_clean_recursive( child, flags_temp ); + } +} + +/** + * Clean attributes on an element + */ +void sp_attribute_clean_element(Node *repr, unsigned int flags) { + + g_return_if_fail (repr != nullptr); + g_return_if_fail (repr->type() == Inkscape::XML::ELEMENT_NODE); + + Glib::ustring element = repr->name(); + Glib::ustring id = (repr->attribute( "id" )==nullptr ? "" : repr->attribute( "id" )); + + // Clean style: this attribute is unique in that normally we want to change it and not simply + // delete it. + sp_attribute_clean_style(repr, flags ); + + // Clean attributes + List<AttributeRecord const> attributes = repr->attributeList(); + + std::set<Glib::ustring> attributesToDelete; + for ( List<AttributeRecord const> iter = attributes ; iter ; ++iter ) { + + Glib::ustring attribute = g_quark_to_string(iter->key); + //Glib::ustring value = (const char*)iter->value; + + bool is_useful = sp_attribute_check_attribute( element, id, attribute, flags & SP_ATTR_CLEAN_ATTR_WARN ); + if( !is_useful && (flags & SP_ATTR_CLEAN_ATTR_REMOVE) ) { + attributesToDelete.insert( attribute ); + } + } + + // Do actual deleting (done after so as not to perturb List iterator). + for(const auto & iter_d : attributesToDelete) { + repr->removeAttribute(iter_d); + } +} + + +/** + * Clean CSS style on an element. + */ +void sp_attribute_clean_style(Node *repr, unsigned int flags) { + + g_return_if_fail (repr != nullptr); + g_return_if_fail (repr->type() == Inkscape::XML::ELEMENT_NODE); + + // Find element's style + SPCSSAttr *css = sp_repr_css_attr( repr, "style" ); + sp_attribute_clean_style(repr, css, flags); + + // 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); + repr->setAttributeOrRemoveIfEmpty("style", value); + + sp_repr_css_attr_unref( css ); +} + + +/** + * Clean CSS style on an element. + */ +Glib::ustring sp_attribute_clean_style(Node *repr, gchar const *string, unsigned int flags) { + + g_return_val_if_fail (repr != nullptr, 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_clean_style(repr, css, flags); + Glib::ustring string_cleaned; + sp_repr_css_write_string (css, string_cleaned); + + sp_repr_css_attr_unref( css ); + + return string_cleaned; +} + + +/** + * Clean CSS style on an element. + * + * 1. Is a style property appropriate on the given element? + * e.g, font-size is useless on <svg:rect> + * 2. Is the value of the style property useful? + * Is it the same as the parent and it inherits? + * Is it the default value (and the property on the parent is not set or does not inherit)? + */ +void sp_attribute_clean_style(Node* repr, SPCSSAttr *css, unsigned int flags) { + + g_return_if_fail (repr != nullptr); + g_return_if_fail (css != nullptr); + + Glib::ustring element = repr->name(); + Glib::ustring id = (repr->attribute( "id" )==nullptr ? "" : repr->attribute( "id" )); + + // Find parent's style, including properties that are inherited. + // Note, a node may not have a parent if it has not yet been added to tree. + SPCSSAttr *css_parent = nullptr; + if( repr->parent() ) css_parent = sp_repr_css_attr_inherited( repr->parent(), "style" ); + + // Loop over all properties in "style" node, keeping track of which to delete. + std::set<Glib::ustring> toDelete; + for ( List<AttributeRecord const> iter = css->attributeList() ; iter ; ++iter ) { + + gchar const * property = g_quark_to_string(iter->key); + gchar const * value = iter->value; + + // Check if a property is applicable to an element (i.e. is font-family useful for a <rect>?). + if( !SPAttributeRelCSS::findIfValid( property, element ) ) { + if( flags & SP_ATTR_CLEAN_STYLE_WARN ) { + g_warning( "<%s id=\"%s\">: CSS Style property: \"%s\" is inappropriate.", + element.c_str(), id.c_str(), property ); + } + if( flags & SP_ATTR_CLEAN_STYLE_REMOVE ) { + toDelete.insert(property); + } + continue; + } + + // Find parent value for same property (property) + gchar const * value_p = nullptr; + if( css_parent != nullptr ) { + for ( List<AttributeRecord const> iter_p = css_parent->attributeList() ; iter_p ; ++iter_p ) { + + gchar const * property_p = g_quark_to_string(iter_p->key); + + if( !g_strcmp0( property, property_p ) ) { + value_p = iter_p->value; + break; + } + } + } + + // If parent has same property value and property is inherited, mark for deletion. + if ( !g_strcmp0( value, value_p ) && SPAttributeRelCSS::findIfInherit( property ) ) { + + if ( flags & SP_ATTR_CLEAN_DEFAULT_WARN ) { + g_warning( "<%s id=\"%s\">: CSS Style property: \"%s\" has same value as parent (%s).", + element.c_str(), id.c_str(), property, value ); + } + if ( flags & SP_ATTR_CLEAN_DEFAULT_REMOVE ) { + toDelete.insert( property ); + } + continue; + } + + // If property value is same as default and the parent value not set or property is not inherited, + // mark for deletion. + if ( SPAttributeRelCSS::findIfDefault( property, value ) && + ( (css_parent != nullptr && value_p == nullptr) || !SPAttributeRelCSS::findIfInherit( property ) ) ) { + + if ( flags & SP_ATTR_CLEAN_DEFAULT_WARN ) { + g_warning( "<%s id=\"%s\">: CSS Style property: \"%s\" with default value (%s) not needed.", + element.c_str(), id.c_str(), property, value ); + } + if ( flags & SP_ATTR_CLEAN_DEFAULT_REMOVE ) { + toDelete.insert( property ); + } + continue; + } + + } // End loop over style properties + + // Delete unneeded style properties. Do this at the end so as to not perturb List iterator. + for(const auto & iter_d : toDelete) { + sp_repr_css_set_property( css, iter_d.c_str(), nullptr ); + } + +} + +/** + * Remove CSS style properties with default values. + */ +void sp_attribute_purge_default_style(SPCSSAttr *css, unsigned int flags) { + + g_return_if_fail (css != nullptr); + + // Loop over all properties in "style" node, keeping track of which to delete. + std::set<Glib::ustring> toDelete; + for ( List<AttributeRecord const> iter = css->attributeList() ; iter ; ++iter ) { + + gchar const * property = g_quark_to_string(iter->key); + gchar const * value = iter->value; + + // If property value is same as default mark for deletion. + if ( SPAttributeRelCSS::findIfDefault( property, value ) ) { + + if ( flags & SP_ATTR_CLEAN_DEFAULT_WARN ) { + g_warning( "Preferences CSS Style property: \"%s\" with default value (%s) not needed.", + property, value ); + } + if ( flags & SP_ATTR_CLEAN_DEFAULT_REMOVE ) { + toDelete.insert( property ); + } + continue; + } + + } // End loop over style properties + + // Delete unneeded style properties. Do this at the end so as to not perturb List iterator. + for(const auto & iter_d : toDelete) { + sp_repr_css_set_property( css, iter_d.c_str(), nullptr ); + } + +} + +/** + * Check one attribute on an element + */ +bool sp_attribute_check_attribute(Glib::ustring element, Glib::ustring id, Glib::ustring attribute, bool warn) { + + bool is_useful = true; + + if( SPAttributeRelCSS::findIfProperty( attribute ) ) { + + // First check if it is a presentation attribute. Presentation attributes can be applied to + // any element. At the moment, we are only going to check if it is a possibly useful + // attribute. Note, we don't explicitly check against the list of elements where presentation + // attributes are allowed (See SVG1.1 spec, Appendix M.2). + if( !SPAttributeRelCSS::findIfValid( attribute, element ) ) { + + // Non-useful presentation attribute on SVG <element> + if( warn ) { + g_warning( "<%s id=\"%s\">: Non-useful presentation attribute: \"%s\" found.", + element.c_str(), + id.c_str(), + attribute.c_str() ); + } + is_useful = false; + } + + } else { + + // Second check if it is a valid attribute + if ( !SPAttributeRelSVG::findIfValid( attribute, element ) ) { + + // Invalid attribute on SVG <element> + if( warn ) { + g_warning( "<%s id=\"%s\">: Invalid attribute: \"%s\" found.", + element.c_str(), + id.c_str(), + attribute.c_str() ); + } + is_useful = false; + } + } + + return is_useful; +} + +/* + 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 : |