summaryrefslogtreecommitdiffstats
path: root/src/xml/repr-util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xml/repr-util.cpp')
-rw-r--r--src/xml/repr-util.cpp470
1 files changed, 470 insertions, 0 deletions
diff --git a/src/xml/repr-util.cpp b/src/xml/repr-util.cpp
new file mode 100644
index 0000000..fba3a49
--- /dev/null
+++ b/src/xml/repr-util.cpp
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Miscellaneous helpers for reprs.
+ */
+
+/*
+ * Authors:
+ * Lauris Kaplinski <lauris@ximian.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ *
+ * Copyright (C) 1999-2000 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ * g++ port Copyright (C) 2003 Nathan Hurst
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <cstring>
+
+#include <glib.h>
+#include <glibmm.h>
+
+#include "svg/svg-length.h"
+
+#include "xml/repr.h"
+#include "xml/repr-sorting.h"
+
+
+struct SPXMLNs {
+ SPXMLNs *next;
+ unsigned int uri, prefix;
+};
+
+/*#####################
+# DEFINITIONS
+#####################*/
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifndef TRUE
+# define TRUE (!FALSE)
+#endif
+
+#ifndef MAX
+# define MAX(a,b) (((a) < (b)) ? (b) : (a))
+#endif
+
+/*#####################
+# FORWARD DECLARATIONS
+#####################*/
+
+static void sp_xml_ns_register_defaults();
+static char *sp_xml_ns_auto_prefix(char const *uri);
+
+/*#####################
+# MAIN
+#####################*/
+
+/**
+ * SPXMLNs
+ */
+
+static SPXMLNs *namespaces=nullptr;
+
+/*
+ * There are the prefixes to use for the XML namespaces defined
+ * in repr.h
+ */
+static void sp_xml_ns_register_defaults()
+{
+ static SPXMLNs defaults[11];
+
+ defaults[0].uri = g_quark_from_static_string(SP_SODIPODI_NS_URI);
+ defaults[0].prefix = g_quark_from_static_string("sodipodi");
+ defaults[0].next = &defaults[1];
+
+ defaults[1].uri = g_quark_from_static_string(SP_XLINK_NS_URI);
+ defaults[1].prefix = g_quark_from_static_string("xlink");
+ defaults[1].next = &defaults[2];
+
+ defaults[2].uri = g_quark_from_static_string(SP_SVG_NS_URI);
+ defaults[2].prefix = g_quark_from_static_string("svg");
+ defaults[2].next = &defaults[3];
+
+ defaults[3].uri = g_quark_from_static_string(SP_INKSCAPE_NS_URI);
+ defaults[3].prefix = g_quark_from_static_string("inkscape");
+ defaults[3].next = &defaults[4];
+
+ defaults[4].uri = g_quark_from_static_string(SP_RDF_NS_URI);
+ defaults[4].prefix = g_quark_from_static_string("rdf");
+ defaults[4].next = &defaults[5];
+
+ defaults[5].uri = g_quark_from_static_string(SP_CC_NS_URI);
+ defaults[5].prefix = g_quark_from_static_string("cc");
+ defaults[5].next = &defaults[6];
+
+ defaults[6].uri = g_quark_from_static_string(SP_DC_NS_URI);
+ defaults[6].prefix = g_quark_from_static_string("dc");
+ defaults[6].next = &defaults[8];
+
+ //defaults[7].uri = g_quark_from_static_string("https://inkscape.org/namespaces/deprecated/osb");
+ //defaults[7].prefix = g_quark_from_static_string("osb");
+ //defaults[7].next = &defaults[8];
+
+ // Inkscape versions prior to 0.44 would write this namespace
+ // URI instead of the correct sodipodi namespace; by adding this
+ // entry to the table last (where it gets used for URI -> prefix
+ // lookups, but not prefix -> URI lookups), we effectively transfer
+ // elements in this namespace to the correct sodipodi namespace:
+
+ defaults[8].uri = g_quark_from_static_string(SP_BROKEN_SODIPODI_NS_URI);
+ defaults[8].prefix = g_quark_from_static_string("sodipodi");
+ defaults[8].next = &defaults[9];
+
+ // "Duck prion"
+ // This URL became widespread due to a bug in versions <= 0.43
+
+ defaults[9].uri = g_quark_from_static_string("http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd");
+ defaults[9].prefix = g_quark_from_static_string("sodipodi");
+ defaults[9].next = &defaults[10];
+
+ // This namespace URI is being phased out by Creative Commons
+
+ defaults[10].uri = g_quark_from_static_string(SP_OLD_CC_NS_URI);
+ defaults[10].prefix = g_quark_from_static_string("cc");
+ defaults[10].next = nullptr;
+
+ namespaces = &defaults[0];
+}
+
+char *sp_xml_ns_auto_prefix(char const *uri)
+{
+ char const *start, *end;
+ char *new_prefix;
+ start = uri;
+ while ((end = strpbrk(start, ":/"))) {
+ start = end + 1;
+ }
+ end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
+ if (end == start) {
+ start = "ns";
+ end = start + 2;
+ }
+ new_prefix = g_strndup(start, end - start);
+ if (sp_xml_ns_prefix_uri(new_prefix)) {
+ char *temp;
+ int counter=0;
+ do {
+ temp = g_strdup_printf("%s%d", new_prefix, counter++);
+ } while (sp_xml_ns_prefix_uri(temp));
+ g_free(new_prefix);
+ new_prefix = temp;
+ }
+ return new_prefix;
+}
+
+gchar const *sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
+{
+ char const *prefix;
+
+ if (!uri) return nullptr;
+
+ if (!namespaces) {
+ sp_xml_ns_register_defaults();
+ }
+
+ GQuark const key = g_quark_from_string(uri);
+ prefix = nullptr;
+ for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
+ if ( iter->uri == key ) {
+ prefix = g_quark_to_string(iter->prefix);
+ break;
+ }
+ }
+
+ if (!prefix) {
+ char *new_prefix;
+ SPXMLNs *ns;
+ if (suggested) {
+ GQuark const prefix_key=g_quark_from_string(suggested);
+
+ SPXMLNs *found=namespaces;
+ while (found) {
+ if (found->prefix != prefix_key) {
+ found = found->next;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (found) { // prefix already used?
+ new_prefix = sp_xml_ns_auto_prefix(uri);
+ } else { // safe to use suggested
+ new_prefix = g_strdup(suggested);
+ }
+ } else {
+ new_prefix = sp_xml_ns_auto_prefix(uri);
+ }
+
+ ns = g_new(SPXMLNs, 1);
+ g_assert( ns != nullptr );
+ ns->uri = g_quark_from_string(uri);
+ ns->prefix = g_quark_from_string(new_prefix);
+
+ g_free(new_prefix);
+
+ ns->next = namespaces;
+ namespaces = ns;
+
+ prefix = g_quark_to_string(ns->prefix);
+ }
+
+ return prefix;
+}
+
+gchar const *sp_xml_ns_prefix_uri(gchar const *prefix)
+{
+ SPXMLNs *iter;
+ char const *uri;
+
+ if (!prefix) return nullptr;
+
+ if (!namespaces) {
+ sp_xml_ns_register_defaults();
+ }
+
+ GQuark const key = g_quark_from_string(prefix);
+ uri = nullptr;
+ for ( iter = namespaces ; iter ; iter = iter->next ) {
+ if ( iter->prefix == key ) {
+ uri = g_quark_to_string(iter->uri);
+ break;
+ }
+ }
+ return uri;
+}
+
+/**
+ * Works for different-parent objects, so long as they have a common ancestor. Return value:
+ * 0 positions are equivalent
+ * 1 first object's position is greater than the second
+ * -1 first object's position is less than the second
+ * @todo Rewrite this function's description to be understandable
+ */
+int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
+{
+ int p1, p2;
+ if (first->parent() == second->parent()) {
+ /* Basic case - first and second have same parent */
+ p1 = first->position();
+ p2 = second->position();
+ } else {
+ /* Special case - the two objects have different parents. They
+ could be in different groups or on different layers for
+ instance. */
+
+ // Find the lowest common ancestor(LCA)
+ Inkscape::XML::Node const *ancestor = LCA(first, second);
+ g_assert(ancestor != nullptr);
+
+ if (ancestor == first) {
+ return 1;
+ } else if (ancestor == second) {
+ return -1;
+ } else {
+ Inkscape::XML::Node const *to_first = AncetreFils(first, ancestor);
+ Inkscape::XML::Node const *to_second = AncetreFils(second, ancestor);
+ g_assert(to_second->parent() == to_first->parent());
+ p1 = to_first->position();
+ p2 = to_second->position();
+ }
+ }
+
+ if (p1 > p2) return 1;
+ if (p1 < p2) return -1;
+ return 0;
+
+ /* effic: Assuming that the parent--child relationship is consistent
+ (i.e. that the parent really does contain first and second among
+ its list of children), it should be equivalent to walk along the
+ children and see which we encounter first (returning 0 iff first
+ == second).
+
+ Given that this function is used solely for sorting, we can use a
+ similar approach to do the sort: gather the things to be sorted,
+ into an STL vector (to allow random access and faster
+ traversals). Do a single pass of the parent's children; for each
+ child, do a pass on whatever items in the vector we haven't yet
+ encountered. If the child is found, then swap it to the
+ beginning of the yet-unencountered elements of the vector.
+ Continue until no more than one remains unencountered. --
+ pjrm */
+}
+
+bool sp_repr_compare_position_bool(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second){
+ return sp_repr_compare_position(first, second)<0;
+}
+
+
+/**
+ * Find an element node using an unique attribute.
+ *
+ * This function returns the first child of the specified node that has the attribute
+ * @c key equal to @c value. Note that this function does not recurse.
+ *
+ * @param repr The node to start from
+ * @param key The name of the attribute to use for comparisons
+ * @param value The value of the attribute to look for
+ * @relatesalso Inkscape::XML::Node
+ */
+Inkscape::XML::Node *sp_repr_lookup_child(Inkscape::XML::Node *repr,
+ gchar const *key,
+ gchar const *value)
+{
+ g_return_val_if_fail(repr != nullptr, NULL);
+ for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
+ gchar const *child_value = child->attribute(key);
+ if ( (child_value == value) ||
+ (value && child_value && !strcmp(child_value, value)) )
+ {
+ return child;
+ }
+ }
+ return nullptr;
+}
+
+/**
+ * Recursive version of sp_repr_lookup_child().
+ */
+Inkscape::XML::Node const *sp_repr_lookup_descendant(Inkscape::XML::Node const *repr,
+ gchar const *key,
+ gchar const *value)
+{
+ Inkscape::XML::Node const *found = nullptr;
+ g_return_val_if_fail(repr != nullptr, NULL);
+ gchar const *repr_value = repr->attribute(key);
+ if ( (repr_value == value) ||
+ (repr_value && value && strcmp(repr_value, value) == 0) ) {
+ found = repr;
+ } else {
+ for (Inkscape::XML::Node const *child = repr->firstChild() ; child && !found; child = child->next() ) {
+ found = sp_repr_lookup_descendant( child, key, value );
+ }
+ }
+ return found;
+}
+
+
+Inkscape::XML::Node *sp_repr_lookup_descendant(Inkscape::XML::Node *repr,
+ gchar const *key,
+ gchar const *value)
+{
+ Inkscape::XML::Node const *found = sp_repr_lookup_descendant( const_cast<Inkscape::XML::Node const *>(repr), key, value );
+ return const_cast<Inkscape::XML::Node *>(found);
+}
+
+Inkscape::XML::Node const *sp_repr_lookup_name( Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth )
+{
+ Inkscape::XML::Node const *found = nullptr;
+ g_return_val_if_fail(repr != nullptr, NULL);
+ g_return_val_if_fail(name != nullptr, NULL);
+
+ GQuark const quark = g_quark_from_string(name);
+
+ if ( (GQuark)repr->code() == quark ) {
+ found = repr;
+ } else if ( maxdepth != 0 ) {
+ // maxdepth == -1 means unlimited
+ if ( maxdepth == -1 ) {
+ maxdepth = 0;
+ }
+
+ for (Inkscape::XML::Node const *child = repr->firstChild() ; child && !found; child = child->next() ) {
+ found = sp_repr_lookup_name( child, name, maxdepth - 1 );
+ }
+ }
+ return found;
+}
+
+Inkscape::XML::Node *sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
+{
+ Inkscape::XML::Node const *found = sp_repr_lookup_name( const_cast<Inkscape::XML::Node const *>(repr), name, maxdepth );
+ return const_cast<Inkscape::XML::Node *>(found);
+}
+
+std::vector<Inkscape::XML::Node const *> sp_repr_lookup_name_many( Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth )
+{
+ std::vector<Inkscape::XML::Node const *> nodes;
+ std::vector<Inkscape::XML::Node const *> found;
+ g_return_val_if_fail(repr != nullptr, nodes);
+ g_return_val_if_fail(name != nullptr, nodes);
+
+ GQuark const quark = g_quark_from_string(name);
+
+ if ( (GQuark)repr->code() == quark ) {
+ nodes.push_back(repr);
+ }
+
+ if ( maxdepth != 0 ) {
+ // maxdepth == -1 means unlimited
+ if ( maxdepth == -1 ) {
+ maxdepth = 0;
+ }
+
+ for (Inkscape::XML::Node const *child = repr->firstChild() ; child; child = child->next() ) {
+ found = sp_repr_lookup_name_many( child, name, maxdepth - 1);
+ nodes.insert(nodes.end(), found.begin(), found.end());
+ }
+ }
+
+ return nodes;
+}
+
+std::vector<Inkscape::XML::Node *>
+sp_repr_lookup_property_many( Inkscape::XML::Node *repr, Glib::ustring const& property,
+ Glib::ustring const &value, int maxdepth )
+{
+ std::vector<Inkscape::XML::Node *> nodes;
+ std::vector<Inkscape::XML::Node *> found;
+ g_return_val_if_fail(repr != nullptr, nodes);
+
+ SPCSSAttr* css = sp_repr_css_attr (repr, "style");
+ if (value == sp_repr_css_property (css, property, "")) {
+ nodes.push_back(repr);
+ }
+
+ if ( maxdepth != 0 ) {
+ // maxdepth == -1 means unlimited
+ if ( maxdepth == -1 ) {
+ maxdepth = 0;
+ }
+
+ for (Inkscape::XML::Node *child = repr->firstChild() ; child; child = child->next() ) {
+ found = sp_repr_lookup_property_many( child, property, value, maxdepth - 1);
+ nodes.insert(nodes.end(), found.begin(), found.end());
+ }
+ }
+
+ return nodes;
+}
+
+/**
+ * Determine if the node is a 'title', 'desc' or 'metadata' element.
+ */
+bool sp_repr_is_meta_element(const Inkscape::XML::Node *node)
+{
+ if (node == nullptr) return false;
+ if (node->type() != Inkscape::XML::NodeType::ELEMENT_NODE) return false;
+ gchar const *name = node->name();
+ if (name == nullptr) return false;
+ if (!std::strcmp(name, "svg:title")) return true;
+ if (!std::strcmp(name, "svg:desc")) return true;
+ if (!std::strcmp(name, "svg:metadata")) return true;
+ return false;
+}
+
+/*
+ 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 :