diff options
Diffstat (limited to 'src/xml/repr-util.cpp')
| -rw-r--r-- | src/xml/repr-util.cpp | 553 |
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 : |
