Code

Fixed const/non-const mismatch loop.
[inkscape.git] / src / xml / repr-util.cpp
1 /** \file
2  * Miscellaneous helpers for reprs.
3  */
5 /*
6  * Authors:
7  *   Lauris Kaplinski <lauris@ximian.com>
8  *   Jon A. Cruz <jon@joncruz.org>
9  *
10  * Copyright (C) 1999-2000 Lauris Kaplinski
11  * Copyright (C) 2000-2001 Ximian, Inc.
12  * g++ port Copyright (C) 2003 Nathan Hurst
13  *
14  * Licensed under GNU GPL
15  */
17 #include "config.h"
19 #include <math.h>
21 #if HAVE_STRING_H
22 # include <cstring>
23 #endif
25 #if HAVE_STDLIB_H
26 # include <cstdlib>
27 #endif
30 #include <glib.h>
31 #include <2geom/point.h>
32 #include "svg/stringstream.h"
33 #include "svg/css-ostringstream.h"
35 #include "xml/repr.h"
36 #include "xml/repr-sorting.h"
39 #define OSB_NS_URI "http://www.openswatchbook.org/uri/2009/osb"
42 struct SPXMLNs {
43     SPXMLNs *next;
44     unsigned int uri, prefix;
45 };
47 /*#####################
48 # DEFINITIONS
49 #####################*/
51 #ifndef FALSE
52 # define FALSE 0
53 #endif
55 #ifndef TRUE
56 # define TRUE (!FALSE)
57 #endif
59 #ifndef MAX
60 # define MAX(a,b) (((a) < (b)) ? (b) : (a))
61 #endif
63 /*#####################
64 # FORWARD DECLARATIONS
65 #####################*/
67 static void sp_xml_ns_register_defaults();
68 static char *sp_xml_ns_auto_prefix(char const *uri);
70 /*#####################
71 # UTILITY
72 #####################*/
74 /**
75  * Locale-independent double to string conversion
76  */
77 unsigned int
78 sp_xml_dtoa(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
79 {
80     double dival, fval, epsilon;
81     int idigits, ival, i;
82     i = 0;
83     if (val < 0.0) {
84         buf[i++] = '-';
85         val = -val;
86     }
87     /* Determine number of integral digits */
88     if (val >= 1.0) {
89         idigits = (int) floor(log10(val));
90     } else {
91         idigits = 0;
92     }
93     /* Determine the actual number of fractional digits */
94     fprec = MAX(fprec, tprec - idigits);
95     /* Find epsilon */
96     epsilon = 0.5 * pow(10.0, - (double) fprec);
97     /* Round value */
98     val += epsilon;
99     /* Extract integral and fractional parts */
100     dival = floor(val);
101     ival = (int) dival;
102     fval = val - dival;
103     /* Write integra */
104     if (ival > 0) {
105         char c[32];
106         int j;
107         j = 0;
108         while (ival > 0) {
109             c[32 - (++j)] = '0' + (ival % 10);
110             ival /= 10;
111         }
112         memcpy(buf + i, &c[32 - j], j);
113         i += j;
114         tprec -= j;
115     } else {
116         buf[i++] = '0';
117         tprec -= 1;
118     }
119     if ((fprec > 0) && (padf || (fval > epsilon))) {
120         buf[i++] = '.';
121         while ((fprec > 0) && (padf || (fval > epsilon))) {
122             fval *= 10.0;
123             dival = floor(fval);
124             fval -= dival;
125             buf[i++] = '0' + (int) dival;
126             fprec -= 1;
127         }
129     }
130     buf[i] = 0;
131     return i;
138 /*#####################
139 # MAIN
140 #####################*/
142 /**
143  * SPXMLNs
144  */
146 static SPXMLNs *namespaces=NULL;
148 /*
149  * There are the prefixes to use for the XML namespaces defined
150  * in repr.h
151  */
152 static void
153 sp_xml_ns_register_defaults()
155     static SPXMLNs defaults[11];
157     defaults[0].uri = g_quark_from_static_string(SP_SODIPODI_NS_URI);
158     defaults[0].prefix = g_quark_from_static_string("sodipodi");
159     defaults[0].next = &defaults[1];
161     defaults[1].uri = g_quark_from_static_string(SP_XLINK_NS_URI);
162     defaults[1].prefix = g_quark_from_static_string("xlink");
163     defaults[1].next = &defaults[2];
165     defaults[2].uri = g_quark_from_static_string(SP_SVG_NS_URI);
166     defaults[2].prefix = g_quark_from_static_string("svg");
167     defaults[2].next = &defaults[3];
169     defaults[3].uri = g_quark_from_static_string(SP_INKSCAPE_NS_URI);
170     defaults[3].prefix = g_quark_from_static_string("inkscape");
171     defaults[3].next = &defaults[4];
173     defaults[4].uri = g_quark_from_static_string(SP_RDF_NS_URI);
174     defaults[4].prefix = g_quark_from_static_string("rdf");
175     defaults[4].next = &defaults[5];
177     defaults[5].uri = g_quark_from_static_string(SP_CC_NS_URI);
178     defaults[5].prefix = g_quark_from_static_string("cc");
179     defaults[5].next = &defaults[6];
181     defaults[6].uri = g_quark_from_static_string(SP_DC_NS_URI);
182     defaults[6].prefix = g_quark_from_static_string("dc");
183     defaults[6].next = &defaults[7];
185     defaults[7].uri = g_quark_from_static_string(OSB_NS_URI);
186     defaults[7].prefix = g_quark_from_static_string("osb");
187     defaults[7].next = &defaults[8];
189     // Inkscape versions prior to 0.44 would write this namespace
190     // URI instead of the correct sodipodi namespace; by adding this
191     // entry to the table last (where it gets used for URI -> prefix
192     // lookups, but not prefix -> URI lookups), we effectively transfer
193     // elements in this namespace to the correct sodipodi namespace:
195     defaults[8].uri = g_quark_from_static_string(SP_BROKEN_SODIPODI_NS_URI);
196     defaults[8].prefix = g_quark_from_static_string("sodipodi");
197     defaults[8].next = &defaults[9];
199     // "Duck prion"
200     // This URL became widespread due to a bug in versions <= 0.43
202     defaults[9].uri = g_quark_from_static_string("http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd");
203     defaults[9].prefix = g_quark_from_static_string("sodipodi");
204     defaults[9].next = &defaults[10];
206     // This namespace URI is being phased out by Creative Commons
208     defaults[10].uri = g_quark_from_static_string(SP_OLD_CC_NS_URI);
209     defaults[10].prefix = g_quark_from_static_string("cc");
210     defaults[10].next = NULL;
212     namespaces = &defaults[0];
215 char *
216 sp_xml_ns_auto_prefix(char const *uri)
218     char const *start, *end;
219     char *new_prefix;
220     start = uri;
221     while ((end = strpbrk(start, ":/"))) {
222         start = end + 1;
223     }
224     end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
225     if (end == start) {
226         start = "ns";
227         end = start + 2;
228     }
229     new_prefix = g_strndup(start, end - start);
230     if (sp_xml_ns_prefix_uri(new_prefix)) {
231         char *temp;
232         int counter=0;
233         do {
234             temp = g_strdup_printf("%s%d", new_prefix, counter++);
235         } while (sp_xml_ns_prefix_uri(temp));
236         g_free(new_prefix);
237         new_prefix = temp;
238     }
239     return new_prefix;
242 gchar const *
243 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
245     char const *prefix;
247     if (!uri) return NULL;
249     if (!namespaces) {
250         sp_xml_ns_register_defaults();
251     }
253     GQuark const key = g_quark_from_string(uri);
254     prefix = NULL;
255     for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
256         if ( iter->uri == key ) {
257             prefix = g_quark_to_string(iter->prefix);
258             break;
259         }
260     }
262     if (!prefix) {
263         char *new_prefix;
264         SPXMLNs *ns;
265         if (suggested) {
266             GQuark const prefix_key=g_quark_from_string(suggested);
268             SPXMLNs *found=namespaces;
269             while ( found && found->prefix != prefix_key ) {
270                 found = found->next;
271             }
273             if (found) { // prefix already used?
274                 new_prefix = sp_xml_ns_auto_prefix(uri);
275             } else { // safe to use suggested
276                 new_prefix = g_strdup(suggested);
277             }
278         } else {
279             new_prefix = sp_xml_ns_auto_prefix(uri);
280         }
282         ns = g_new(SPXMLNs, 1);
283         g_assert( ns != NULL );
284         ns->uri = g_quark_from_string(uri);
285         ns->prefix = g_quark_from_string(new_prefix);
287         g_free(new_prefix);
289         ns->next = namespaces;
290         namespaces = ns;
292         prefix = g_quark_to_string(ns->prefix);
293     }
295     return prefix;
298 gchar const *
299 sp_xml_ns_prefix_uri(gchar const *prefix)
301     SPXMLNs *iter;
302     char const *uri;
304     if (!prefix) return NULL;
306     if (!namespaces) {
307         sp_xml_ns_register_defaults();
308     }
310     GQuark const key = g_quark_from_string(prefix);
311     uri = NULL;
312     for ( iter = namespaces ; iter ; iter = iter->next ) {
313         if ( iter->prefix == key ) {
314             uri = g_quark_to_string(iter->uri);
315             break;
316         }
317     }
318     return uri;
321 double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
323     char *result;
325     g_return_val_if_fail(repr != NULL, def);
326     g_return_val_if_fail(key != NULL, def);
328     result = (char *) repr->attribute(key);
330     if (result == NULL) return def;
332     return g_ascii_strtod(result, NULL);
335 long long int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, long long int def)
337     char *result;
339     g_return_val_if_fail(repr != NULL, def);
340     g_return_val_if_fail(key != NULL, def);
342     result = (char *) repr->attribute(key);
344     if (result == NULL) return def;
346     return atoll(result);
349 /** 
350  *  Works for different-parent objects, so long as they have a common ancestor. Return value:
351  *    0    positions are equivalent
352  *    1    first object's position is greater than the second
353  *   -1    first object's position is less than the second
354  * @todo Rewrite this function's description to be understandable
355  */
356 int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
358     int p1, p2;
359     if (sp_repr_parent(first) == sp_repr_parent(second)) {
360         /* Basic case - first and second have same parent */
361         p1 = first->position();
362         p2 = second->position();
363     } else {
364         /* Special case - the two objects have different parents.  They
365            could be in different groups or on different layers for
366            instance. */
368         // Find the lowest common ancestor(LCA)
369         Inkscape::XML::Node const *ancestor = LCA(first, second);
370         g_assert(ancestor != NULL);
372         if (ancestor == first) {
373             return 1;
374         } else if (ancestor == second) {
375             return -1;
376         } else {
377             Inkscape::XML::Node const *to_first = AncetreFils(first, ancestor);
378             Inkscape::XML::Node const *to_second = AncetreFils(second, ancestor);
379             g_assert(sp_repr_parent(to_second) == sp_repr_parent(to_first));
380             p1 = to_first->position();
381             p2 = to_second->position();
382         }
383     }
385     if (p1 > p2) return 1;
386     if (p1 < p2) return -1;
387     return 0;
389     /* effic: Assuming that the parent--child relationship is consistent
390        (i.e. that the parent really does contain first and second among
391        its list of children), it should be equivalent to walk along the
392        children and see which we encounter first (returning 0 iff first
393        == second).
394        
395        Given that this function is used solely for sorting, we can use a
396        similar approach to do the sort: gather the things to be sorted,
397        into an STL vector (to allow random access and faster
398        traversals).  Do a single pass of the parent's children; for each
399        child, do a pass on whatever items in the vector we haven't yet
400        encountered.  If the child is found, then swap it to the
401        beginning of the yet-unencountered elements of the vector.
402        Continue until no more than one remains unencountered.  --
403        pjrm */
406 /**
407  * @brief Find an element node using an unique attribute
408  *
409  * This function returns the first child of the specified node that has the attribute
410  * @c key equal to @c value. Note that this function does not recurse.
411  *
412  * @param repr The node to start from
413  * @param key The name of the attribute to use for comparisons
414  * @param value The value of the attribute to look for
415  * @relatesalso Inkscape::XML::Node
416  */
417 Inkscape::XML::Node *
418 sp_repr_lookup_child(Inkscape::XML::Node *repr,
419                      gchar const *key,
420                      gchar const *value)
422     g_return_val_if_fail(repr != NULL, NULL);
423     for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
424         gchar const *child_value = child->attribute(key);
425         if ( (child_value == value) ||
426              (value && child_value && !strcmp(child_value, value)) )
427         {
428             return child;
429         }
430     }
431     return NULL;
434 Inkscape::XML::Node const *sp_repr_lookup_name( Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth )
436     Inkscape::XML::Node const *found = 0;
437     g_return_val_if_fail(repr != NULL, NULL);
438     g_return_val_if_fail(name != NULL, NULL);
440     GQuark const quark = g_quark_from_string(name);
442     if ( (GQuark)repr->code() == quark ) {
443         found = repr;
444     } else if ( maxdepth != 0 ) {
445         // maxdepth == -1 means unlimited
446         if ( maxdepth == -1 ) {
447             maxdepth = 0;
448         }
450         for (Inkscape::XML::Node const *child = repr->firstChild() ; child && !found; child = child->next() ) {
451             found = sp_repr_lookup_name( child, name, maxdepth - 1 );
452         }
453     }
454     return found;
457 Inkscape::XML::Node *sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
459     Inkscape::XML::Node const *found = sp_repr_lookup_name( const_cast<Inkscape::XML::Node const *>(repr), name, maxdepth );
460     return const_cast<Inkscape::XML::Node *>(found);
463 /**
464  * Determine if the node is a 'title', 'desc' or 'metadata' element.
465  */
466 bool
467 sp_repr_is_meta_element(const Inkscape::XML::Node *node)
469     if (node == NULL) return false;
470     if (node->type() != Inkscape::XML::ELEMENT_NODE) return false;
471     gchar const *name = node->name();
472     if (name == NULL) return false;
473     if (!std::strcmp(name, "svg:title")) return true;
474     if (!std::strcmp(name, "svg:desc")) return true;
475     if (!std::strcmp(name, "svg:metadata")) return true;
476     return false;
479 /**
480  * Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
481  * the attr is not set.
482  *
483  * \return TRUE if the attr was set, FALSE otherwise.
484  */
485 unsigned int
486 sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
488     gchar const *v;
490     g_return_val_if_fail(repr != NULL, FALSE);
491     g_return_val_if_fail(key != NULL, FALSE);
492     g_return_val_if_fail(val != NULL, FALSE);
494     v = repr->attribute(key);
496     if (v != NULL) {
497         if (!g_strcasecmp(v, "true") ||
498             !g_strcasecmp(v, "yes" ) ||
499             !g_strcasecmp(v, "y"   ) ||
500             (atoi(v) != 0)) {
501             *val = TRUE;
502         } else {
503             *val = FALSE;
504         }
505         return TRUE;
506     } else {
507         *val = FALSE;
508         return FALSE;
509     }
512 unsigned int
513 sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
515     gchar const *v;
517     g_return_val_if_fail(repr != NULL, FALSE);
518     g_return_val_if_fail(key != NULL, FALSE);
519     g_return_val_if_fail(val != NULL, FALSE);
521     v = repr->attribute(key);
523     if (v != NULL) {
524         *val = atoi(v);
525         return TRUE;
526     }
528     return FALSE;
531 unsigned int
532 sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
534     gchar const *v;
536     g_return_val_if_fail(repr != NULL, FALSE);
537     g_return_val_if_fail(key != NULL, FALSE);
538     g_return_val_if_fail(val != NULL, FALSE);
540     v = repr->attribute(key);
542     if (v != NULL) {
543         *val = g_ascii_strtod(v, NULL);
544         return TRUE;
545     }
547     return FALSE;
550 unsigned int
551 sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
553     g_return_val_if_fail(repr != NULL, FALSE);
554     g_return_val_if_fail(key != NULL, FALSE);
556     repr->setAttribute(key, (val) ? "true" : "false");
557     return true;
560 unsigned int
561 sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
563     gchar c[32];
565     g_return_val_if_fail(repr != NULL, FALSE);
566     g_return_val_if_fail(key != NULL, FALSE);
568     g_snprintf(c, 32, "%d", val);
570     repr->setAttribute(key, c);
571     return true;
574 /**
575  * Set a property attribute to \a val [slightly rounded], in the format
576  * required for CSS properties: in particular, it never uses exponent
577  * notation.
578  */
579 unsigned int
580 sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
582     g_return_val_if_fail(repr != NULL, FALSE);
583     g_return_val_if_fail(key != NULL, FALSE);
585     Inkscape::CSSOStringStream os;
586     os << val;
588     repr->setAttribute(key, os.str().c_str());
589     return true;
592 /**
593  * For attributes where an exponent is allowed.
594  *
595  * Not suitable for property attributes (fill-opacity, font-size etc.).
596  */
597 unsigned int
598 sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
600     g_return_val_if_fail(repr != NULL, FALSE);
601     g_return_val_if_fail(key != NULL, FALSE);
603     Inkscape::SVGOStringStream os;
604     os << val;
606     repr->setAttribute(key, os.str().c_str());
607     return true;
610 unsigned sp_repr_set_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point const & val)
612     g_return_val_if_fail(repr != NULL, FALSE);
613     g_return_val_if_fail(key != NULL, FALSE);
615     Inkscape::SVGOStringStream os;
616     os << val[Geom::X] << "," << val[Geom::Y];
618     repr->setAttribute(key, os.str().c_str());
619     return true;
622 unsigned int
623 sp_repr_get_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point *val)
625     g_return_val_if_fail(repr != NULL, FALSE);
626     g_return_val_if_fail(key != NULL, FALSE);
627     g_return_val_if_fail(val != NULL, FALSE);
629     gchar const *v = repr->attribute(key);
631     gchar ** strarray = g_strsplit(v, ",", 2);
633     if (strarray && strarray[0] && strarray[1]) {
634         double newx, newy;
635         newx = g_ascii_strtod(strarray[0], NULL);
636         newy = g_ascii_strtod(strarray[1], NULL);
637         g_strfreev (strarray);
638         *val = Geom::Point(newx, newy);
639         return TRUE;
640     }
642     g_strfreev (strarray);
643     return FALSE;
646 /*
647   Local Variables:
648   mode:c++
649   c-file-style:"stroustrup"
650   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
651   indent-tabs-mode:nil
652   fill-column:99
653   End:
654 */
655 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :