Code

* Lots of documentation for the Inkscape::XML 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 <cstring>
24 #endif
26 #if HAVE_STDLIB_H
27 # include <cstdlib>
28 #endif
31 #include <glib.h>
32 #include <2geom/point.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[10];
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     // Inkscape versions prior to 0.44 would write this namespace
183     // URI instead of the correct sodipodi namespace; by adding this
184     // entry to the table last (where it gets used for URI -> prefix
185     // lookups, but not prefix -> URI lookups), we effectively transfer
186     // elements in this namespace to the correct sodipodi namespace:
188     defaults[7].uri = g_quark_from_static_string(SP_BROKEN_SODIPODI_NS_URI);
189     defaults[7].prefix = g_quark_from_static_string("sodipodi");
190     defaults[7].next = &defaults[8];
192     // "Duck prion"
193     // This URL became widespread due to a bug in versions <= 0.43
195     defaults[8].uri = g_quark_from_static_string("http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd");
196     defaults[8].prefix = g_quark_from_static_string("sodipodi");
197     defaults[8].next = &defaults[9];
199     // This namespace URI is being phased out by Creative Commons
201     defaults[9].uri = g_quark_from_static_string(SP_OLD_CC_NS_URI);
202     defaults[9].prefix = g_quark_from_static_string("cc");
203     defaults[9].next = NULL;
205     namespaces = &defaults[0];
208 char *
209 sp_xml_ns_auto_prefix(char const *uri)
211     char const *start, *end;
212     char *new_prefix;
213     start = uri;
214     while ((end = strpbrk(start, ":/"))) {
215         start = end + 1;
216     }
217     end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
218     if (end == start) {
219         start = "ns";
220         end = start + 2;
221     }
222     new_prefix = g_strndup(start, end - start);
223     if (sp_xml_ns_prefix_uri(new_prefix)) {
224         char *temp;
225         int counter=0;
226         do {
227             temp = g_strdup_printf("%s%d", new_prefix, counter++);
228         } while (sp_xml_ns_prefix_uri(temp));
229         g_free(new_prefix);
230         new_prefix = temp;
231     }
232     return new_prefix;
235 gchar const *
236 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
238     char const *prefix;
240     if (!uri) return NULL;
242     if (!namespaces) {
243         sp_xml_ns_register_defaults();
244     }
246     GQuark const key = g_quark_from_string(uri);
247     prefix = NULL;
248     for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
249         if ( iter->uri == key ) {
250             prefix = g_quark_to_string(iter->prefix);
251             break;
252         }
253     }
255     if (!prefix) {
256         char *new_prefix;
257         SPXMLNs *ns;
258         if (suggested) {
259             GQuark const prefix_key=g_quark_from_string(suggested);
261             SPXMLNs *found=namespaces;
262             while ( found && found->prefix != prefix_key ) {
263                 found = found->next;
264             }
266             if (found) { // prefix already used?
267                 new_prefix = sp_xml_ns_auto_prefix(uri);
268             } else { // safe to use suggested
269                 new_prefix = g_strdup(suggested);
270             }
271         } else {
272             new_prefix = sp_xml_ns_auto_prefix(uri);
273         }
275         ns = g_new(SPXMLNs, 1);
276         g_assert( ns != NULL );
277         ns->uri = g_quark_from_string(uri);
278         ns->prefix = g_quark_from_string(new_prefix);
280         g_free(new_prefix);
282         ns->next = namespaces;
283         namespaces = ns;
285         prefix = g_quark_to_string(ns->prefix);
286     }
288     return prefix;
291 gchar const *
292 sp_xml_ns_prefix_uri(gchar const *prefix)
294     SPXMLNs *iter;
295     char const *uri;
297     if (!prefix) return NULL;
299     if (!namespaces) {
300         sp_xml_ns_register_defaults();
301     }
303     GQuark const key = g_quark_from_string(prefix);
304     uri = NULL;
305     for ( iter = namespaces ; iter ; iter = iter->next ) {
306         if ( iter->prefix == key ) {
307             uri = g_quark_to_string(iter->uri);
308             break;
309         }
310     }
311     return uri;
314 double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
316     char *result;
318     g_return_val_if_fail(repr != NULL, def);
319     g_return_val_if_fail(key != NULL, def);
321     result = (char *) repr->attribute(key);
323     if (result == NULL) return def;
325     return g_ascii_strtod(result, NULL);
328 long long int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, long long int def)
330     char *result;
332     g_return_val_if_fail(repr != NULL, def);
333     g_return_val_if_fail(key != NULL, def);
335     result = (char *) repr->attribute(key);
337     if (result == NULL) return def;
339     return atoll(result);
342 /** 
343  *  Works for different-parent objects, so long as they have a common ancestor. Return value:
344  *    0    positions are equivalent
345  *    1    first object's position is greater than the second
346  *   -1    first object's position is less than the second
347  * @todo Rewrite this function's description to be understandable
348  */
349 int
350 sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
352     int p1, p2;
353     if (sp_repr_parent(first) == sp_repr_parent(second)) {
354         /* Basic case - first and second have same parent */
355         p1 = first->position();
356         p2 = second->position();
357     } else {
358         /* Special case - the two objects have different parents.  They
359            could be in different groups or on different layers for
360            instance. */
362         // Find the lowest common ancestor(LCA)
363         Inkscape::XML::Node *ancestor = LCA(first, second);
364         g_assert(ancestor != NULL);
366         if (ancestor == first) {
367             return 1;
368         } else if (ancestor == second) {
369             return -1;
370         } else {
371             Inkscape::XML::Node const *to_first = AncetreFils(first, ancestor);
372             Inkscape::XML::Node const *to_second = AncetreFils(second, ancestor);
373             g_assert(sp_repr_parent(to_second) == sp_repr_parent(to_first));
374             p1 = to_first->position();
375             p2 = to_second->position();
376         }
377     }
379     if (p1 > p2) return 1;
380     if (p1 < p2) return -1;
381     return 0;
383     /* effic: Assuming that the parent--child relationship is consistent
384        (i.e. that the parent really does contain first and second among
385        its list of children), it should be equivalent to walk along the
386        children and see which we encounter first (returning 0 iff first
387        == second).
388        
389        Given that this function is used solely for sorting, we can use a
390        similar approach to do the sort: gather the things to be sorted,
391        into an STL vector (to allow random access and faster
392        traversals).  Do a single pass of the parent's children; for each
393        child, do a pass on whatever items in the vector we haven't yet
394        encountered.  If the child is found, then swap it to the
395        beginning of the yet-unencountered elements of the vector.
396        Continue until no more than one remains unencountered.  --
397        pjrm */
400 /**
401  * @brief Find an element node using an unique attribute
402  *
403  * This function returns the first child of the specified node that has the attribute
404  * @c key equal to @c value. Note that this function does not recurse.
405  *
406  * @param repr The node to start from
407  * @param key The name of the attribute to use for comparisons
408  * @param value The value of the attribute to look for
409  * @relatesalso Inkscape::XML::Node
410  */
411 Inkscape::XML::Node *
412 sp_repr_lookup_child(Inkscape::XML::Node *repr,
413                      gchar const *key,
414                      gchar const *value)
416     g_return_val_if_fail(repr != NULL, NULL);
417     for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
418         gchar const *child_value = child->attribute(key);
419         if ( child_value == value ||
420              value && child_value && !strcmp(child_value, value) )
421         {
422             return child;
423         }
424     }
425     return NULL;
428 /**
429  * @brief Find an element node with the given name
430  *
431  * This function searches the descendants of the specified node depth-first for
432  * the first XML node with the specified name.
433  *
434  * @param repr The node to start from
435  * @param name The name of the element node to find
436  * @param maxdepth Maximum search depth, or -1 for an unlimited depth
437  * @return  A pointer to the matching Inkscape::XML::Node
438  * @relatesalso Inkscape::XML::Node
439  */
440 Inkscape::XML::Node *
441 sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
443     g_return_val_if_fail(repr != NULL, NULL);
444     g_return_val_if_fail(name != NULL, NULL);
446     GQuark const quark = g_quark_from_string(name);
448     if ( (GQuark)repr->code() == quark ) return repr;
449     if ( maxdepth == 0 ) return NULL;
451     // maxdepth == -1 means unlimited
452     if ( maxdepth == -1 ) maxdepth = 0;
454     Inkscape::XML::Node *found = NULL;
455     for (Inkscape::XML::Node *child = repr->firstChild() ; child && !found; child = child->next() ) {
456         found = sp_repr_lookup_name( child, name, maxdepth-1 );
457     }
459     return found;
462 /**
463  * Determine if the node is a 'title', 'desc' or 'metadata' element.
464  */
465 bool
466 sp_repr_is_meta_element(const Inkscape::XML::Node *node)
468     if (node == NULL) return false;
469     if (node->type() != Inkscape::XML::ELEMENT_NODE) return false;
470     gchar const *name = node->name();
471     if (name == NULL) return false;
472     if (!std::strcmp(name, "svg:title")) return true;
473     if (!std::strcmp(name, "svg:desc")) return true;
474     if (!std::strcmp(name, "svg:metadata")) return true;
475     return false;
478 /**
479  * Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
480  * the attr is not set.
481  *
482  * \return TRUE if the attr was set, FALSE otherwise.
483  */
484 unsigned int
485 sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
487     gchar const *v;
489     g_return_val_if_fail(repr != NULL, FALSE);
490     g_return_val_if_fail(key != NULL, FALSE);
491     g_return_val_if_fail(val != NULL, FALSE);
493     v = repr->attribute(key);
495     if (v != NULL) {
496         if (!g_strcasecmp(v, "true") ||
497             !g_strcasecmp(v, "yes" ) ||
498             !g_strcasecmp(v, "y"   ) ||
499             (atoi(v) != 0)) {
500             *val = TRUE;
501         } else {
502             *val = FALSE;
503         }
504         return TRUE;
505     } else {
506         *val = FALSE;
507         return FALSE;
508     }
511 unsigned int
512 sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
514     gchar const *v;
516     g_return_val_if_fail(repr != NULL, FALSE);
517     g_return_val_if_fail(key != NULL, FALSE);
518     g_return_val_if_fail(val != NULL, FALSE);
520     v = repr->attribute(key);
522     if (v != NULL) {
523         *val = atoi(v);
524         return TRUE;
525     }
527     return FALSE;
530 unsigned int
531 sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
533     gchar const *v;
535     g_return_val_if_fail(repr != NULL, FALSE);
536     g_return_val_if_fail(key != NULL, FALSE);
537     g_return_val_if_fail(val != NULL, FALSE);
539     v = repr->attribute(key);
541     if (v != NULL) {
542         *val = g_ascii_strtod(v, NULL);
543         return TRUE;
544     }
546     return FALSE;
549 unsigned int
550 sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
552     g_return_val_if_fail(repr != NULL, FALSE);
553     g_return_val_if_fail(key != NULL, FALSE);
555     repr->setAttribute(key, (val) ? "true" : "false");
556     return true;
559 unsigned int
560 sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
562     gchar c[32];
564     g_return_val_if_fail(repr != NULL, FALSE);
565     g_return_val_if_fail(key != NULL, FALSE);
567     g_snprintf(c, 32, "%d", val);
569     repr->setAttribute(key, c);
570     return true;
573 /**
574  * Set a property attribute to \a val [slightly rounded], in the format
575  * required for CSS properties: in particular, it never uses exponent
576  * notation.
577  */
578 unsigned int
579 sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
581     g_return_val_if_fail(repr != NULL, FALSE);
582     g_return_val_if_fail(key != NULL, FALSE);
584     Inkscape::CSSOStringStream os;
585     os << val;
587     repr->setAttribute(key, os.str().c_str());
588     return true;
591 /**
592  * For attributes where an exponent is allowed.
593  *
594  * Not suitable for property attributes (fill-opacity, font-size etc.).
595  */
596 unsigned int
597 sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
599     g_return_val_if_fail(repr != NULL, FALSE);
600     g_return_val_if_fail(key != NULL, FALSE);
602     Inkscape::SVGOStringStream os;
603     os << val;
605     repr->setAttribute(key, os.str().c_str());
606     return true;
609 unsigned sp_repr_set_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point const & val)
611     g_return_val_if_fail(repr != NULL, FALSE);
612     g_return_val_if_fail(key != NULL, FALSE);
614     Inkscape::SVGOStringStream os;
615     os << val[Geom::X] << "," << val[Geom::Y];
617     repr->setAttribute(key, os.str().c_str());
618     return true;
621 unsigned int
622 sp_repr_get_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point *val)
624     g_return_val_if_fail(repr != NULL, FALSE);
625     g_return_val_if_fail(key != NULL, FALSE);
626     g_return_val_if_fail(val != NULL, FALSE);
628     gchar const *v = repr->attribute(key);
630     gchar ** strarray = g_strsplit(v, ",", 2);
632     if (strarray && strarray[0] && strarray[1]) {
633         double newx, newy;
634         newx = g_ascii_strtod(strarray[0], NULL);
635         newy = g_ascii_strtod(strarray[1], NULL);
636         g_strfreev (strarray);
637         *val = Geom::Point(newx, newy);
638         return TRUE;
639     }
641     g_strfreev (strarray);
642     return FALSE;
645 /*
646   Local Variables:
647   mode:c++
648   c-file-style:"stroustrup"
649   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
650   indent-tabs-mode:nil
651   fill-column:99
652   End:
653 */
654 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :