Code

add fixup for incorrect sodipodi namespace
[inkscape.git] / src / xml / repr-util.cpp
1 #define __SP_REPR_UTIL_C__
3 /** \file
4  * Miscellaneous helpers for reprs.
5  */
7 /*
8  * Authors:
9  *   Lauris Kaplinski <lauris@ximian.com>
10  *
11  * Copyright (C) 1999-2000 Lauris Kaplinski
12  * Copyright (C) 2000-2001 Ximian, Inc.
13  * g++ port Copyright (C) 2003 Nathan Hurst
14  *
15  * Licensed under GNU GPL
16  */
18 #include "config.h"
20 #include <math.h>
22 #if HAVE_STRING_H
23 # include <string.h>
24 #endif
26 #if HAVE_STDLIB_H
27 # include <stdlib.h>
28 #endif
31 #include <glib.h>
33 #include "svg/stringstream.h"
34 #include "svg/css-ostringstream.h"
36 #include "xml/repr.h"
37 #include "xml/repr-sorting.h"
39 struct SPXMLNs {
40     SPXMLNs *next;
41     unsigned int uri, prefix;
42 };
44 /*#####################
45 # DEFINITIONS
46 #####################*/
48 #ifndef FALSE
49 # define FALSE 0
50 #endif
52 #ifndef TRUE
53 # define TRUE (!FALSE)
54 #endif
56 #ifndef MAX
57 # define MAX(a,b) (((a) < (b)) ? (b) : (a))
58 #endif
60 /*#####################
61 # FORWARD DECLARATIONS
62 #####################*/
64 static void sp_xml_ns_register_defaults();
65 static char *sp_xml_ns_auto_prefix(char const *uri);
67 /*#####################
68 # UTILITY
69 #####################*/
71 /**
72  * Locale-independent double to string conversion
73  */
74 unsigned int
75 sp_xml_dtoa(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
76 {
77     double dival, fval, epsilon;
78     int idigits, ival, i;
79     i = 0;
80     if (val < 0.0) {
81         buf[i++] = '-';
82         val = -val;
83     }
84     /* Determine number of integral digits */
85     if (val >= 1.0) {
86         idigits = (int) floor(log10(val));
87     } else {
88         idigits = 0;
89     }
90     /* Determine the actual number of fractional digits */
91     fprec = MAX(fprec, tprec - idigits);
92     /* Find epsilon */
93     epsilon = 0.5 * pow(10.0, - (double) fprec);
94     /* Round value */
95     val += epsilon;
96     /* Extract integral and fractional parts */
97     dival = floor(val);
98     ival = (int) dival;
99     fval = val - dival;
100     /* Write integra */
101     if (ival > 0) {
102         char c[32];
103         int j;
104         j = 0;
105         while (ival > 0) {
106             c[32 - (++j)] = '0' + (ival % 10);
107             ival /= 10;
108         }
109         memcpy(buf + i, &c[32 - j], j);
110         i += j;
111         tprec -= j;
112     } else {
113         buf[i++] = '0';
114         tprec -= 1;
115     }
116     if ((fprec > 0) && (padf || (fval > epsilon))) {
117         buf[i++] = '.';
118         while ((fprec > 0) && (padf || (fval > epsilon))) {
119             fval *= 10.0;
120             dival = floor(fval);
121             fval -= dival;
122             buf[i++] = '0' + (int) dival;
123             fprec -= 1;
124         }
126     }
127     buf[i] = 0;
128     return i;
135 /*#####################
136 # MAIN
137 #####################*/
139 /**
140  * SPXMLNs
141  */
143 static SPXMLNs *namespaces=NULL;
145 /*
146  * There are the prefixes to use for the XML namespaces defined
147  * in repr.h
148  */
149 static void
150 sp_xml_ns_register_defaults()
152     static SPXMLNs defaults[8];
154     defaults[0].uri = g_quark_from_static_string(SP_SODIPODI_NS_URI);
155     defaults[0].prefix = g_quark_from_static_string("sodipodi");
156     defaults[0].next = &defaults[1];
158     defaults[1].uri = g_quark_from_static_string(SP_XLINK_NS_URI);
159     defaults[1].prefix = g_quark_from_static_string("xlink");
160     defaults[1].next = &defaults[2];
162     defaults[2].uri = g_quark_from_static_string(SP_SVG_NS_URI);
163     defaults[2].prefix = g_quark_from_static_string("svg");
164     defaults[2].next = &defaults[3];
166     defaults[3].uri = g_quark_from_static_string(SP_INKSCAPE_NS_URI);
167     defaults[3].prefix = g_quark_from_static_string("inkscape");
168     defaults[3].next = &defaults[4];
170     defaults[4].uri = g_quark_from_static_string(SP_RDF_NS_URI);
171     defaults[4].prefix = g_quark_from_static_string("rdf");
172     defaults[4].next = &defaults[5];
174     defaults[5].uri = g_quark_from_static_string(SP_CC_NS_URI);
175     defaults[5].prefix = g_quark_from_static_string("cc");
176     defaults[5].next = &defaults[6];
178     defaults[6].uri = g_quark_from_static_string(SP_DC_NS_URI);
179     defaults[6].prefix = g_quark_from_static_string("dc");
180     defaults[6].next = &defaults[7];
182     defaults[7].uri = g_quark_from_static_string(SP_BROKEN_SODIPODI_NS_URI);
183     defaults[7].prefix = g_quark_from_static_string("sodipodi");
184     defaults[7].next = NULL;
186     namespaces = &defaults[0];
189 char *
190 sp_xml_ns_auto_prefix(char const *uri)
192     char const *start, *end;
193     char *new_prefix;
194     start = uri;
195     while ((end = strpbrk(start, ":/"))) {
196         start = end + 1;
197     }
198     end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
199     if (end == start) {
200         start = "ns";
201         end = start + 2;
202     }
203     new_prefix = g_strndup(start, end - start);
204     if (sp_xml_ns_prefix_uri(new_prefix)) {
205         char *temp;
206         int counter=0;
207         do {
208             temp = g_strdup_printf("%s%d", new_prefix, counter++);
209         } while (sp_xml_ns_prefix_uri(temp));
210         g_free(new_prefix);
211         new_prefix = temp;
212     }
213     return new_prefix;
216 gchar const *
217 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
219     char const *prefix;
221     if (!uri) return NULL;
223     if (!namespaces) {
224         sp_xml_ns_register_defaults();
225     }
227     GQuark const key = g_quark_from_string(uri);
228     prefix = NULL;
229     for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
230         if ( iter->uri == key ) {
231             prefix = g_quark_to_string(iter->prefix);
232             break;
233         }
234     }
236     if (!prefix) {
237         char *new_prefix;
238         SPXMLNs *ns;
239         if (suggested) {
240             GQuark const prefix_key=g_quark_from_string(suggested);
242             SPXMLNs *found=namespaces;
243             while ( found && found->prefix != prefix_key ) {
244                 found = found->next;
245             }
247             if (found) { // prefix already used?
248                 new_prefix = sp_xml_ns_auto_prefix(uri);
249             } else { // safe to use suggested
250                 new_prefix = g_strdup(suggested);
251             }
252         } else {
253             new_prefix = sp_xml_ns_auto_prefix(uri);
254         }
256         ns = g_new(SPXMLNs, 1);
257         g_assert( ns != NULL );
258         ns->uri = g_quark_from_string(uri);
259         ns->prefix = g_quark_from_string(new_prefix);
261         g_free(new_prefix);
263         ns->next = namespaces;
264         namespaces = ns;
266         prefix = g_quark_to_string(ns->prefix);
267     }
269     return prefix;
272 gchar const *
273 sp_xml_ns_prefix_uri(gchar const *prefix)
275     SPXMLNs *iter;
276     char const *uri;
278     if (!prefix) return NULL;
280     if (!namespaces) {
281         sp_xml_ns_register_defaults();
282     }
284     GQuark const key = g_quark_from_string(prefix);
285     uri = NULL;
286     for ( iter = namespaces ; iter ; iter = iter->next ) {
287         if ( iter->prefix == key ) {
288             uri = g_quark_to_string(iter->uri);
289             break;
290         }
291     }
292     return uri;
295 double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
297     char *result;
299     g_return_val_if_fail(repr != NULL, def);
300     g_return_val_if_fail(key != NULL, def);
302     result = (char *) repr->attribute(key);
304     if (result == NULL) return def;
306     return g_ascii_strtod(result, NULL);
309 int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int def)
311     char *result;
313     g_return_val_if_fail(repr != NULL, def);
314     g_return_val_if_fail(key != NULL, def);
316     result = (char *) repr->attribute(key);
318     if (result == NULL) return def;
320     return atoi(result);
323 /** 
324  *  Works for different-parent objects, so long as they have a common ancestor. Return value:
325  *    0    positions are equivalent
326  *    1    first object's position is greater than the second
327  *   -1    first object's position is less than the second
328  */
329 int
330 sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
332     int p1, p2;
333     if (sp_repr_parent(first) == sp_repr_parent(second)) {
334         /* Basic case - first and second have same parent */
335         p1 = first->position();
336         p2 = second->position();
337     } else {
338         /* Special case - the two objects have different parents.  They
339            could be in different groups or on different layers for
340            instance. */
342         // Find the lowest common ancestor(LCA)
343         Inkscape::XML::Node *ancestor = LCA(first, second);
344         g_assert(ancestor != NULL);
346         if (ancestor == first) {
347             return 1;
348         } else if (ancestor == second) {
349             return -1;
350         } else {
351             Inkscape::XML::Node const *to_first = AncetreFils(first, ancestor);
352             Inkscape::XML::Node const *to_second = AncetreFils(second, ancestor);
353             g_assert(sp_repr_parent(to_second) == sp_repr_parent(to_first));
354             p1 = to_first->position();
355             p2 = to_second->position();
356         }
357     }
359     if (p1 > p2) return 1;
360     if (p1 < p2) return -1;
361     return 0;
363     /* effic: Assuming that the parent--child relationship is consistent
364        (i.e. that the parent really does contain first and second among
365        its list of children), it should be equivalent to walk along the
366        children and see which we encounter first (returning 0 iff first
367        == second).
368        
369        Given that this function is used solely for sorting, we can use a
370        similar approach to do the sort: gather the things to be sorted,
371        into an STL vector (to allow random access and faster
372        traversals).  Do a single pass of the parent's children; for each
373        child, do a pass on whatever items in the vector we haven't yet
374        encountered.  If the child is found, then swap it to the
375        beginning of the yet-unencountered elements of the vector.
376        Continue until no more than one remains unencountered.  --
377        pjrm */
380 /**
381  * lookup child by \a key, \a value.
382  */
383 Inkscape::XML::Node *
384 sp_repr_lookup_child(Inkscape::XML::Node *repr,
385                      gchar const *key,
386                      gchar const *value)
388     g_return_val_if_fail(repr != NULL, NULL);
389     for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
390         gchar const *child_value = child->attribute(key);
391         if ( child_value == value ||
392              value && child_value && !strcmp(child_value, value) )
393         {
394             return child;
395         }
396     }
397     return NULL;
400 /**
401  *  \brief   Recursively find the Inkscape::XML::Node matching the given XML name.
402  *  \return  A pointer to the matching Inkscape::XML::Node
403  *  \param   repr    The Inkscape::XML::Node to start from
404  *  \param   name    The desired XML name
405  *  
406  */
407 Inkscape::XML::Node *
408 sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
410     g_return_val_if_fail(repr != NULL, NULL);
411     g_return_val_if_fail(name != NULL, NULL);
413     GQuark const quark = g_quark_from_string(name);
415     if ( (GQuark)repr->code() == quark ) return repr;
416     if ( maxdepth == 0 ) return NULL;
418     // maxdepth == -1 means unlimited
419     if ( maxdepth == -1 ) maxdepth = 0;
421     Inkscape::XML::Node *found = NULL;
422     for (Inkscape::XML::Node *child = repr->firstChild() ; child && !found; child = child->next() ) {
423         found = sp_repr_lookup_name( child, name, maxdepth-1 );
424     }
426     return found;
429 /**
430  * Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
431  * the attr is not set.
432  *
433  * \return TRUE if the attr was set, FALSE otherwise.
434  */
435 unsigned int
436 sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
438     gchar const *v;
440     g_return_val_if_fail(repr != NULL, FALSE);
441     g_return_val_if_fail(key != NULL, FALSE);
442     g_return_val_if_fail(val != NULL, FALSE);
444     v = repr->attribute(key);
446     if (v != NULL) {
447         if (!g_strcasecmp(v, "true") ||
448             !g_strcasecmp(v, "yes" ) ||
449             !g_strcasecmp(v, "y"   ) ||
450             (atoi(v) != 0)) {
451             *val = TRUE;
452         } else {
453             *val = FALSE;
454         }
455         return TRUE;
456     } else {
457         *val = FALSE;
458         return FALSE;
459     }
462 unsigned int
463 sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
465     gchar const *v;
467     g_return_val_if_fail(repr != NULL, FALSE);
468     g_return_val_if_fail(key != NULL, FALSE);
469     g_return_val_if_fail(val != NULL, FALSE);
471     v = repr->attribute(key);
473     if (v != NULL) {
474         *val = atoi(v);
475         return TRUE;
476     }
478     return FALSE;
481 unsigned int
482 sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
484     gchar const *v;
486     g_return_val_if_fail(repr != NULL, FALSE);
487     g_return_val_if_fail(key != NULL, FALSE);
488     g_return_val_if_fail(val != NULL, FALSE);
490     v = repr->attribute(key);
492     if (v != NULL) {
493         *val = g_ascii_strtod(v, NULL);
494         return TRUE;
495     }
497     return FALSE;
500 unsigned int
501 sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
503     g_return_val_if_fail(repr != NULL, FALSE);
504     g_return_val_if_fail(key != NULL, FALSE);
506     repr->setAttribute(key, (val) ? "true" : "false");
507     return true;
510 unsigned int
511 sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
513     gchar c[32];
515     g_return_val_if_fail(repr != NULL, FALSE);
516     g_return_val_if_fail(key != NULL, FALSE);
518     g_snprintf(c, 32, "%d", val);
520     repr->setAttribute(key, c);
521     return true;
524 /**
525  * Set a property attribute to \a val [slightly rounded], in the format
526  * required for CSS properties: in particular, it never uses exponent
527  * notation.
528  */
529 unsigned int
530 sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
532     g_return_val_if_fail(repr != NULL, FALSE);
533     g_return_val_if_fail(key != NULL, FALSE);
535     Inkscape::CSSOStringStream os;
536     os << val;
538     repr->setAttribute(key, os.str().c_str());
539     return true;
542 /**
543  * For attributes where an exponent is allowed.
544  *
545  * Not suitable for property attributes (fill-opacity, font-size etc.).
546  */
547 unsigned int
548 sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
550     g_return_val_if_fail(repr != NULL, FALSE);
551     g_return_val_if_fail(key != NULL, FALSE);
553     Inkscape::SVGOStringStream os;
554     os << val;
556     repr->setAttribute(key, os.str().c_str());
557     return true;
561 /*
562   Local Variables:
563   mode:c++
564   c-file-style:"stroustrup"
565   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
566   indent-tabs-mode:nil
567   fill-column:99
568   End:
569 */
570 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :