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.cpp553
1 files changed, 553 insertions, 0 deletions
diff --git a/src/xml/repr-util.cpp b/src/xml/repr-util.cpp
new file mode 100644
index 000000000..75ae692af
--- /dev/null
+++ b/src/xml/repr-util.cpp
@@ -0,0 +1,553 @@
+#define __SP_REPR_UTIL_C__
+
+/** \file
+ * Miscellaneous helpers for reprs.
+ */
+
+/*
+ * Authors:
+ * Lauris Kaplinski <lauris@ximian.com>
+ *
+ * Copyright (C) 1999-2000 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ * g++ port Copyright (C) 2003 Nathan Hurst
+ *
+ * Licensed under GNU GPL
+ */
+
+#include "config.h"
+
+#include <math.h>
+
+#if HAVE_STRING_H
+# include <string.h>
+#endif
+
+#if HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+
+#include <glib.h>
+
+#include "svg/stringstream.h"
+#include "svg/css-ostringstream.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);
+
+/*#####################
+# UTILITY
+#####################*/
+
+/**
+ * Locale-independent double to string conversion
+ */
+unsigned int
+sp_xml_dtoa(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
+{
+ double dival, fval, epsilon;
+ int idigits, ival, i;
+ i = 0;
+ if (val < 0.0) {
+ buf[i++] = '-';
+ val = -val;
+ }
+ /* Determine number of integral digits */
+ if (val >= 1.0) {
+ idigits = (int) floor(log10(val));
+ } else {
+ idigits = 0;
+ }
+ /* Determine the actual number of fractional digits */
+ fprec = MAX(fprec, tprec - idigits);
+ /* Find epsilon */
+ epsilon = 0.5 * pow(10.0, - (double) fprec);
+ /* Round value */
+ val += epsilon;
+ /* Extract integral and fractional parts */
+ dival = floor(val);
+ ival = (int) dival;
+ fval = val - dival;
+ /* Write integra */
+ if (ival > 0) {
+ char c[32];
+ int j;
+ j = 0;
+ while (ival > 0) {
+ c[32 - (++j)] = '0' + (ival % 10);
+ ival /= 10;
+ }
+ memcpy(buf + i, &c[32 - j], j);
+ i += j;
+ tprec -= j;
+ } else {
+ buf[i++] = '0';
+ tprec -= 1;
+ }
+ if ((fprec > 0) && (padf || (fval > epsilon))) {
+ buf[i++] = '.';
+ while ((fprec > 0) && (padf || (fval > epsilon))) {
+ fval *= 10.0;
+ dival = floor(fval);
+ fval -= dival;
+ buf[i++] = '0' + (int) dival;
+ fprec -= 1;
+ }
+
+ }
+ buf[i] = 0;
+ return i;
+}
+
+
+
+
+
+/*#####################
+# MAIN
+#####################*/
+
+/**
+ * SPXMLNs
+ */
+
+static SPXMLNs *namespaces=NULL;
+
+/*
+ * There are the prefixes to use for the XML namespaces defined
+ * in repr.h
+ */
+static void
+sp_xml_ns_register_defaults()
+{
+ static SPXMLNs defaults[7];
+
+ 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 = NULL;
+
+ 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)
+{
+ SPXMLNs *iter;
+ char const *prefix;
+
+ if (!uri) return NULL;
+
+ if (!namespaces) {
+ sp_xml_ns_register_defaults();
+ }
+
+ GQuark const key = g_quark_from_string(uri);
+ prefix = NULL;
+ for ( iter = namespaces ; iter ; iter = iter->next ) {
+ if ( iter->uri == key ) {
+ prefix = g_quark_to_string(iter->prefix);
+ break;
+ }
+ }
+ if (!prefix) {
+ char const *new_prefix;
+ SPXMLNs *ns;
+ if (suggested) {
+ new_prefix = suggested;
+ } else {
+ new_prefix = sp_xml_ns_auto_prefix(uri);
+ }
+ ns = g_new(SPXMLNs, 1);
+ if (ns) {
+ ns->uri = g_quark_from_string(uri);
+ ns->prefix = g_quark_from_string(new_prefix);
+ ns->next = namespaces;
+ namespaces = ns;
+ prefix = g_quark_to_string(ns->prefix);
+ }
+ if (!suggested) {
+ g_free((char *)new_prefix);
+ }
+ }
+ return prefix;
+}
+
+gchar const *
+sp_xml_ns_prefix_uri(gchar const *prefix)
+{
+ SPXMLNs *iter;
+ char const *uri;
+
+ if (!prefix) return NULL;
+
+ if (!namespaces) {
+ sp_xml_ns_register_defaults();
+ }
+
+ GQuark const key = g_quark_from_string(prefix);
+ uri = NULL;
+ for ( iter = namespaces ; iter ; iter = iter->next ) {
+ if ( iter->prefix == key ) {
+ uri = g_quark_to_string(iter->uri);
+ break;
+ }
+ }
+ return uri;
+}
+
+double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
+{
+ char *result;
+
+ g_return_val_if_fail(repr != NULL, def);
+ g_return_val_if_fail(key != NULL, def);
+
+ result = (char *) repr->attribute(key);
+
+ if (result == NULL) return def;
+
+ return g_ascii_strtod(result, NULL);
+}
+
+int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int def)
+{
+ char *result;
+
+ g_return_val_if_fail(repr != NULL, def);
+ g_return_val_if_fail(key != NULL, def);
+
+ result = (char *) repr->attribute(key);
+
+ if (result == NULL) return def;
+
+ return atoi(result);
+}
+
+/**
+ * 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
+ */
+int
+sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
+{
+ int p1, p2;
+ if (sp_repr_parent(first) == sp_repr_parent(second)) {
+ /* 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 *ancestor = LCA(first, second);
+ g_assert(ancestor != NULL);
+
+ 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(sp_repr_parent(to_second) == sp_repr_parent(to_first));
+ 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 */
+}
+
+/**
+ * lookup child by \a key, \a value.
+ */
+Inkscape::XML::Node *
+sp_repr_lookup_child(Inkscape::XML::Node *repr,
+ gchar const *key,
+ gchar const *value)
+{
+ g_return_val_if_fail(repr != NULL, 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 NULL;
+}
+
+/**
+ * \brief Recursively find the Inkscape::XML::Node matching the given XML name.
+ * \return A pointer to the matching Inkscape::XML::Node
+ * \param repr The Inkscape::XML::Node to start from
+ * \param name The desired XML name
+ *
+ */
+Inkscape::XML::Node *
+sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
+{
+ g_return_val_if_fail(repr != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ GQuark const quark = g_quark_from_string(name);
+
+ if ( (GQuark)repr->code() == quark ) return repr;
+ if ( maxdepth == 0 ) return NULL;
+
+ // maxdepth == -1 means unlimited
+ if ( maxdepth == -1 ) maxdepth = 0;
+
+ Inkscape::XML::Node *found = NULL;
+ for (Inkscape::XML::Node *child = repr->firstChild() ; child && !found; child = child->next() ) {
+ found = sp_repr_lookup_name( child, name, maxdepth-1 );
+ }
+
+ return found;
+}
+
+/**
+ * Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
+ * the attr is not set.
+ *
+ * \return TRUE if the attr was set, FALSE otherwise.
+ */
+unsigned int
+sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
+{
+ gchar const *v;
+
+ g_return_val_if_fail(repr != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+ g_return_val_if_fail(val != NULL, FALSE);
+
+ v = repr->attribute(key);
+
+ if (v != NULL) {
+ if (!g_strcasecmp(v, "true") ||
+ !g_strcasecmp(v, "yes" ) ||
+ !g_strcasecmp(v, "y" ) ||
+ (atoi(v) != 0)) {
+ *val = TRUE;
+ } else {
+ *val = FALSE;
+ }
+ return TRUE;
+ } else {
+ *val = FALSE;
+ return FALSE;
+ }
+}
+
+unsigned int
+sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
+{
+ gchar const *v;
+
+ g_return_val_if_fail(repr != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+ g_return_val_if_fail(val != NULL, FALSE);
+
+ v = repr->attribute(key);
+
+ if (v != NULL) {
+ *val = atoi(v);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+unsigned int
+sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
+{
+ gchar const *v;
+
+ g_return_val_if_fail(repr != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+ g_return_val_if_fail(val != NULL, FALSE);
+
+ v = repr->attribute(key);
+
+ if (v != NULL) {
+ *val = g_ascii_strtod(v, NULL);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+unsigned int
+sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
+{
+ g_return_val_if_fail(repr != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+
+ repr->setAttribute(key, (val) ? "true" : "false");
+ return true;
+}
+
+unsigned int
+sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
+{
+ gchar c[32];
+
+ g_return_val_if_fail(repr != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+
+ g_snprintf(c, 32, "%d", val);
+
+ repr->setAttribute(key, c);
+ return true;
+}
+
+/**
+ * Set a property attribute to \a val [slightly rounded], in the format
+ * required for CSS properties: in particular, it never uses exponent
+ * notation.
+ */
+unsigned int
+sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
+{
+ g_return_val_if_fail(repr != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+
+ Inkscape::CSSOStringStream os;
+ os << val;
+
+ repr->setAttribute(key, os.str().c_str());
+ return true;
+}
+
+/**
+ * For attributes where an exponent is allowed.
+ *
+ * Not suitable for property attributes (fill-opacity, font-size etc.).
+ */
+unsigned int
+sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
+{
+ g_return_val_if_fail(repr != NULL, FALSE);
+ g_return_val_if_fail(key != NULL, FALSE);
+
+ Inkscape::SVGOStringStream os;
+ os << val;
+
+ repr->setAttribute(key, os.str().c_str());
+ 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:encoding=utf-8:textwidth=99 :