summaryrefslogtreecommitdiffstats
path: root/src/attribute-rel-util.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:50:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:50:49 +0000
commitc853ffb5b2f75f5a889ed2e3ef89b818a736e87a (patch)
tree7d13a0883bb7936b84d6ecdd7bc332b41ed04bee /src/attribute-rel-util.cpp
parentInitial commit. (diff)
downloadinkscape-c853ffb5b2f75f5a889ed2e3ef89b818a736e87a.tar.xz
inkscape-c853ffb5b2f75f5a889ed2e3ef89b818a736e87a.zip
Adding upstream version 1.3+ds.upstream/1.3+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/attribute-rel-util.cpp')
-rw-r--r--src/attribute-rel-util.cpp375
1 files changed, 375 insertions, 0 deletions
diff --git a/src/attribute-rel-util.cpp b/src/attribute-rel-util.cpp
new file mode 100644
index 0000000..4527aae
--- /dev/null
+++ b/src/attribute-rel-util.cpp
@@ -0,0 +1,375 @@
+// 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 "attribute-rel-util.h"
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <2geom/path-sink.h>
+#include <2geom/svg-path-parser.h>
+
+#include "attribute-rel-css.h"
+#include "attribute-rel-svg.h"
+#include "preferences.h"
+#include "xml/attribute-record.h"
+
+using Inkscape::XML::Node;
+
+/**
+ * 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_ATTRCLEAN_ATTR_WARN;
+ if (prefs->getBool("/options/svgoutput/incorrect_attributes_remove") &&
+ !prefs->getBool("/options/svgoutput/disable_optimizations"))
+ flags += SP_ATTRCLEAN_ATTR_REMOVE;
+ if (prefs->getBool("/options/svgoutput/incorrect_style_properties_warn"))
+ flags += SP_ATTRCLEAN_STYLE_WARN;
+ if (prefs->getBool("/options/svgoutput/incorrect_style_properties_remove") &&
+ !prefs->getBool("/options/svgoutput/disable_optimizations"))
+ flags += SP_ATTRCLEAN_STYLE_REMOVE;
+ if (prefs->getBool("/options/svgoutput/style_defaults_warn"))
+ flags += SP_ATTRCLEAN_DEFAULT_WARN;
+ if (prefs->getBool("/options/svgoutput/style_defaults_remove") &&
+ !prefs->getBool("/options/svgoutput/disable_optimizations"))
+ flags += SP_ATTRCLEAN_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::NodeType::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_ATTRCLEAN_DEFAULT_WARN | SP_ATTRCLEAN_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::NodeType::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
+ std::set<Glib::ustring> attributesToDelete;
+ for (const auto &iter : repr->attributeList()) {
+ 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_ATTRCLEAN_ATTR_WARN);
+ if (!is_useful && (flags & SP_ATTRCLEAN_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::NodeType::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::NodeType::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 (const auto &iter : css->attributeList()) {
+ Glib::ustring 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_ATTRCLEAN_STYLE_WARN) {
+ g_warning("<%s id=\"%s\">: CSS Style property: \"%s\" is inappropriate.", element.c_str(), id.c_str(),
+ property.c_str());
+ }
+ if (flags & SP_ATTRCLEAN_STYLE_REMOVE) {
+ toDelete.insert(property);
+ }
+ continue;
+ }
+
+ // Find parent value for same property (property)
+ gchar const *value_p = nullptr;
+ if (css_parent != nullptr) {
+ for (const auto &iter_p : css_parent->attributeList()) {
+ gchar const *property_p = g_quark_to_string(iter_p.key);
+
+ if (!g_strcmp0(property.c_str(), 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_ATTRCLEAN_DEFAULT_WARN) {
+ g_warning("<%s id=\"%s\">: CSS Style property: \"%s\" has same value as parent (%s).", element.c_str(),
+ id.c_str(), property.c_str(), value);
+ }
+ if (flags & SP_ATTRCLEAN_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) &&
+ ((value_p == nullptr) || !SPAttributeRelCSS::findIfInherit(property))) {
+ if (flags & SP_ATTRCLEAN_DEFAULT_WARN) {
+ g_warning("<%s id=\"%s\">: CSS Style property: \"%s\" with default value (%s) not needed.",
+ element.c_str(), id.c_str(), property.c_str(), value);
+ }
+ if (flags & SP_ATTRCLEAN_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 (const auto &iter : css->attributeList()) {
+ Glib::ustring 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_ATTRCLEAN_DEFAULT_WARN) {
+ g_warning("Preferences CSS Style property: \"%s\" with default value (%s) not needed.",
+ property.c_str(), value);
+ }
+ if (flags & SP_ATTRCLEAN_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 const &element, Glib::ustring const &id, Glib::ustring const &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;
+}
+
+bool sp_is_valid_svg_path_d(Glib::ustring const &d)
+{
+ /** A PathSink going straight to /dev/null */
+ class PathBlackHole final : public Geom::PathSink
+ {
+ using Geom::PathSink::feed;
+ void moveTo(Geom::Point const &) final {}
+ void lineTo(Geom::Point const &) final {}
+ void curveTo(Geom::Point const &, Geom::Point const &, Geom::Point const &) final {}
+ void quadTo(Geom::Point const &, Geom::Point const &) final {}
+ void arcTo(Geom::Coord, Geom::Coord, Geom::Coord, bool, bool, Geom::Point const &) final {}
+ void closePath() final {}
+ void flush() final {}
+ void feed(Geom::Curve const &, bool) final {}
+ void feed(Geom::Path const &) final {}
+ void feed(Geom::PathVector const &) final {}
+ void feed(Geom::Rect const &) final {}
+ void feed(Geom::Circle const &) final {}
+ void feed(Geom::Ellipse const &) final {}
+ } dev_null;
+
+ auto validator = Geom::SVGPathParser(dev_null);
+ try {
+ validator.parse(static_cast<std::string>(d));
+ } catch (Geom::SVGPathParseError &) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ 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 :