Code

switch pref/repr utility methods to using long long ints; with
[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[9];
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 = NULL;
199     namespaces = &defaults[0];
202 char *
203 sp_xml_ns_auto_prefix(char const *uri)
205     char const *start, *end;
206     char *new_prefix;
207     start = uri;
208     while ((end = strpbrk(start, ":/"))) {
209         start = end + 1;
210     }
211     end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
212     if (end == start) {
213         start = "ns";
214         end = start + 2;
215     }
216     new_prefix = g_strndup(start, end - start);
217     if (sp_xml_ns_prefix_uri(new_prefix)) {
218         char *temp;
219         int counter=0;
220         do {
221             temp = g_strdup_printf("%s%d", new_prefix, counter++);
222         } while (sp_xml_ns_prefix_uri(temp));
223         g_free(new_prefix);
224         new_prefix = temp;
225     }
226     return new_prefix;
229 gchar const *
230 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
232     char const *prefix;
234     if (!uri) return NULL;
236     if (!namespaces) {
237         sp_xml_ns_register_defaults();
238     }
240     GQuark const key = g_quark_from_string(uri);
241     prefix = NULL;
242     for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
243         if ( iter->uri == key ) {
244             prefix = g_quark_to_string(iter->prefix);
245             break;
246         }
247     }
249     if (!prefix) {
250         char *new_prefix;
251         SPXMLNs *ns;
252         if (suggested) {
253             GQuark const prefix_key=g_quark_from_string(suggested);
255             SPXMLNs *found=namespaces;
256             while ( found && found->prefix != prefix_key ) {
257                 found = found->next;
258             }
260             if (found) { // prefix already used?
261                 new_prefix = sp_xml_ns_auto_prefix(uri);
262             } else { // safe to use suggested
263                 new_prefix = g_strdup(suggested);
264             }
265         } else {
266             new_prefix = sp_xml_ns_auto_prefix(uri);
267         }
269         ns = g_new(SPXMLNs, 1);
270         g_assert( ns != NULL );
271         ns->uri = g_quark_from_string(uri);
272         ns->prefix = g_quark_from_string(new_prefix);
274         g_free(new_prefix);
276         ns->next = namespaces;
277         namespaces = ns;
279         prefix = g_quark_to_string(ns->prefix);
280     }
282     return prefix;
285 gchar const *
286 sp_xml_ns_prefix_uri(gchar const *prefix)
288     SPXMLNs *iter;
289     char const *uri;
291     if (!prefix) return NULL;
293     if (!namespaces) {
294         sp_xml_ns_register_defaults();
295     }
297     GQuark const key = g_quark_from_string(prefix);
298     uri = NULL;
299     for ( iter = namespaces ; iter ; iter = iter->next ) {
300         if ( iter->prefix == key ) {
301             uri = g_quark_to_string(iter->uri);
302             break;
303         }
304     }
305     return uri;
308 double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
310     char *result;
312     g_return_val_if_fail(repr != NULL, def);
313     g_return_val_if_fail(key != NULL, def);
315     result = (char *) repr->attribute(key);
317     if (result == NULL) return def;
319     return g_ascii_strtod(result, NULL);
322 long long int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, long long int def)
324     char *result;
326     g_return_val_if_fail(repr != NULL, def);
327     g_return_val_if_fail(key != NULL, def);
329     result = (char *) repr->attribute(key);
331     if (result == NULL) return def;
333     return atoll(result);
336 /** 
337  *  Works for different-parent objects, so long as they have a common ancestor. Return value:
338  *    0    positions are equivalent
339  *    1    first object's position is greater than the second
340  *   -1    first object's position is less than the second
341  */
342 int
343 sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
345     int p1, p2;
346     if (sp_repr_parent(first) == sp_repr_parent(second)) {
347         /* Basic case - first and second have same parent */
348         p1 = first->position();
349         p2 = second->position();
350     } else {
351         /* Special case - the two objects have different parents.  They
352            could be in different groups or on different layers for
353            instance. */
355         // Find the lowest common ancestor(LCA)
356         Inkscape::XML::Node *ancestor = LCA(first, second);
357         g_assert(ancestor != NULL);
359         if (ancestor == first) {
360             return 1;
361         } else if (ancestor == second) {
362             return -1;
363         } else {
364             Inkscape::XML::Node const *to_first = AncetreFils(first, ancestor);
365             Inkscape::XML::Node const *to_second = AncetreFils(second, ancestor);
366             g_assert(sp_repr_parent(to_second) == sp_repr_parent(to_first));
367             p1 = to_first->position();
368             p2 = to_second->position();
369         }
370     }
372     if (p1 > p2) return 1;
373     if (p1 < p2) return -1;
374     return 0;
376     /* effic: Assuming that the parent--child relationship is consistent
377        (i.e. that the parent really does contain first and second among
378        its list of children), it should be equivalent to walk along the
379        children and see which we encounter first (returning 0 iff first
380        == second).
381        
382        Given that this function is used solely for sorting, we can use a
383        similar approach to do the sort: gather the things to be sorted,
384        into an STL vector (to allow random access and faster
385        traversals).  Do a single pass of the parent's children; for each
386        child, do a pass on whatever items in the vector we haven't yet
387        encountered.  If the child is found, then swap it to the
388        beginning of the yet-unencountered elements of the vector.
389        Continue until no more than one remains unencountered.  --
390        pjrm */
393 /**
394  * lookup child by \a key, \a value.
395  */
396 Inkscape::XML::Node *
397 sp_repr_lookup_child(Inkscape::XML::Node *repr,
398                      gchar const *key,
399                      gchar const *value)
401     g_return_val_if_fail(repr != NULL, NULL);
402     for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
403         gchar const *child_value = child->attribute(key);
404         if ( child_value == value ||
405              value && child_value && !strcmp(child_value, value) )
406         {
407             return child;
408         }
409     }
410     return NULL;
413 /**
414  *  \brief   Recursively find the Inkscape::XML::Node matching the given XML name.
415  *  \return  A pointer to the matching Inkscape::XML::Node
416  *  \param   repr    The Inkscape::XML::Node to start from
417  *  \param   name    The desired XML name
418  *  
419  */
420 Inkscape::XML::Node *
421 sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
423     g_return_val_if_fail(repr != NULL, NULL);
424     g_return_val_if_fail(name != NULL, NULL);
426     GQuark const quark = g_quark_from_string(name);
428     if ( (GQuark)repr->code() == quark ) return repr;
429     if ( maxdepth == 0 ) return NULL;
431     // maxdepth == -1 means unlimited
432     if ( maxdepth == -1 ) maxdepth = 0;
434     Inkscape::XML::Node *found = NULL;
435     for (Inkscape::XML::Node *child = repr->firstChild() ; child && !found; child = child->next() ) {
436         found = sp_repr_lookup_name( child, name, maxdepth-1 );
437     }
439     return found;
442 /**
443  * Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
444  * the attr is not set.
445  *
446  * \return TRUE if the attr was set, FALSE otherwise.
447  */
448 unsigned int
449 sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
451     gchar const *v;
453     g_return_val_if_fail(repr != NULL, FALSE);
454     g_return_val_if_fail(key != NULL, FALSE);
455     g_return_val_if_fail(val != NULL, FALSE);
457     v = repr->attribute(key);
459     if (v != NULL) {
460         if (!g_strcasecmp(v, "true") ||
461             !g_strcasecmp(v, "yes" ) ||
462             !g_strcasecmp(v, "y"   ) ||
463             (atoi(v) != 0)) {
464             *val = TRUE;
465         } else {
466             *val = FALSE;
467         }
468         return TRUE;
469     } else {
470         *val = FALSE;
471         return FALSE;
472     }
475 unsigned int
476 sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
478     gchar const *v;
480     g_return_val_if_fail(repr != NULL, FALSE);
481     g_return_val_if_fail(key != NULL, FALSE);
482     g_return_val_if_fail(val != NULL, FALSE);
484     v = repr->attribute(key);
486     if (v != NULL) {
487         *val = atoi(v);
488         return TRUE;
489     }
491     return FALSE;
494 unsigned int
495 sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
497     gchar const *v;
499     g_return_val_if_fail(repr != NULL, FALSE);
500     g_return_val_if_fail(key != NULL, FALSE);
501     g_return_val_if_fail(val != NULL, FALSE);
503     v = repr->attribute(key);
505     if (v != NULL) {
506         *val = g_ascii_strtod(v, NULL);
507         return TRUE;
508     }
510     return FALSE;
513 unsigned int
514 sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
516     g_return_val_if_fail(repr != NULL, FALSE);
517     g_return_val_if_fail(key != NULL, FALSE);
519     repr->setAttribute(key, (val) ? "true" : "false");
520     return true;
523 unsigned int
524 sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
526     gchar c[32];
528     g_return_val_if_fail(repr != NULL, FALSE);
529     g_return_val_if_fail(key != NULL, FALSE);
531     g_snprintf(c, 32, "%d", val);
533     repr->setAttribute(key, c);
534     return true;
537 /**
538  * Set a property attribute to \a val [slightly rounded], in the format
539  * required for CSS properties: in particular, it never uses exponent
540  * notation.
541  */
542 unsigned int
543 sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
545     g_return_val_if_fail(repr != NULL, FALSE);
546     g_return_val_if_fail(key != NULL, FALSE);
548     Inkscape::CSSOStringStream os;
549     os << val;
551     repr->setAttribute(key, os.str().c_str());
552     return true;
555 /**
556  * For attributes where an exponent is allowed.
557  *
558  * Not suitable for property attributes (fill-opacity, font-size etc.).
559  */
560 unsigned int
561 sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
563     g_return_val_if_fail(repr != NULL, FALSE);
564     g_return_val_if_fail(key != NULL, FALSE);
566     Inkscape::SVGOStringStream os;
567     os << val;
569     repr->setAttribute(key, os.str().c_str());
570     return true;
574 /*
575   Local Variables:
576   mode:c++
577   c-file-style:"stroustrup"
578   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
579   indent-tabs-mode:nil
580   fill-column:99
581   End:
582 */
583 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :