Code

added fix from Dale Harvey to expand incomplete JIDs specified in user
[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[7];
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 = NULL;
182     namespaces = &defaults[0];
185 char *
186 sp_xml_ns_auto_prefix(char const *uri)
188     char const *start, *end;
189     char *new_prefix;
190     start = uri;
191     while ((end = strpbrk(start, ":/"))) {
192         start = end + 1;
193     }
194     end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
195     if (end == start) {
196         start = "ns";
197         end = start + 2;
198     }
199     new_prefix = g_strndup(start, end - start);
200     if (sp_xml_ns_prefix_uri(new_prefix)) {
201         char *temp;
202         int counter=0;
203         do {
204             temp = g_strdup_printf("%s%d", new_prefix, counter++);
205         } while (sp_xml_ns_prefix_uri(temp));
206         g_free(new_prefix);
207         new_prefix = temp;
208     }
209     return new_prefix;
212 gchar const *
213 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
215     SPXMLNs *iter;
216     char const *prefix;
218     if (!uri) return NULL;
220     if (!namespaces) {
221         sp_xml_ns_register_defaults();
222     }
224     GQuark const key = g_quark_from_string(uri);
225     prefix = NULL;
226     for ( iter = namespaces ; iter ; iter = iter->next ) {
227         if ( iter->uri == key ) {
228             prefix = g_quark_to_string(iter->prefix);
229             break;
230         }
231     }
232     if (!prefix) {
233         char const *new_prefix;
234         SPXMLNs *ns;
235         if (suggested) {
236             new_prefix = suggested;
237         } else {
238             new_prefix = sp_xml_ns_auto_prefix(uri);
239         }
240         ns = g_new(SPXMLNs, 1);
241         if (ns) {
242             ns->uri = g_quark_from_string(uri);
243             ns->prefix = g_quark_from_string(new_prefix);
244             ns->next = namespaces;
245             namespaces = ns;
246             prefix = g_quark_to_string(ns->prefix);
247         }
248         if (!suggested) {
249             g_free((char *)new_prefix);
250         }
251     }
252     return prefix;
255 gchar const *
256 sp_xml_ns_prefix_uri(gchar const *prefix)
258     SPXMLNs *iter;
259     char const *uri;
261     if (!prefix) return NULL;
263     if (!namespaces) {
264         sp_xml_ns_register_defaults();
265     }
267     GQuark const key = g_quark_from_string(prefix);
268     uri = NULL;
269     for ( iter = namespaces ; iter ; iter = iter->next ) {
270         if ( iter->prefix == key ) {
271             uri = g_quark_to_string(iter->uri);
272             break;
273         }
274     }
275     return uri;
278 double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
280     char *result;
282     g_return_val_if_fail(repr != NULL, def);
283     g_return_val_if_fail(key != NULL, def);
285     result = (char *) repr->attribute(key);
287     if (result == NULL) return def;
289     return g_ascii_strtod(result, NULL);
292 int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int def)
294     char *result;
296     g_return_val_if_fail(repr != NULL, def);
297     g_return_val_if_fail(key != NULL, def);
299     result = (char *) repr->attribute(key);
301     if (result == NULL) return def;
303     return atoi(result);
306 /** 
307  *  Works for different-parent objects, so long as they have a common ancestor. Return value:
308  *    0    positions are equivalent
309  *    1    first object's position is greater than the second
310  *   -1    first object's position is less than the second
311  */
312 int
313 sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
315     int p1, p2;
316     if (sp_repr_parent(first) == sp_repr_parent(second)) {
317         /* Basic case - first and second have same parent */
318         p1 = first->position();
319         p2 = second->position();
320     } else {
321         /* Special case - the two objects have different parents.  They
322            could be in different groups or on different layers for
323            instance. */
325         // Find the lowest common ancestor(LCA)
326         Inkscape::XML::Node *ancestor = LCA(first, second);
327         g_assert(ancestor != NULL);
329         if (ancestor == first) {
330             return 1;
331         } else if (ancestor == second) {
332             return -1;
333         } else {
334             Inkscape::XML::Node const *to_first = AncetreFils(first, ancestor);
335             Inkscape::XML::Node const *to_second = AncetreFils(second, ancestor);
336             g_assert(sp_repr_parent(to_second) == sp_repr_parent(to_first));
337             p1 = to_first->position();
338             p2 = to_second->position();
339         }
340     }
342     if (p1 > p2) return 1;
343     if (p1 < p2) return -1;
344     return 0;
346     /* effic: Assuming that the parent--child relationship is consistent
347        (i.e. that the parent really does contain first and second among
348        its list of children), it should be equivalent to walk along the
349        children and see which we encounter first (returning 0 iff first
350        == second).
351        
352        Given that this function is used solely for sorting, we can use a
353        similar approach to do the sort: gather the things to be sorted,
354        into an STL vector (to allow random access and faster
355        traversals).  Do a single pass of the parent's children; for each
356        child, do a pass on whatever items in the vector we haven't yet
357        encountered.  If the child is found, then swap it to the
358        beginning of the yet-unencountered elements of the vector.
359        Continue until no more than one remains unencountered.  --
360        pjrm */
363 /**
364  * lookup child by \a key, \a value.
365  */
366 Inkscape::XML::Node *
367 sp_repr_lookup_child(Inkscape::XML::Node *repr,
368                      gchar const *key,
369                      gchar const *value)
371     g_return_val_if_fail(repr != NULL, NULL);
372     for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
373         gchar const *child_value = child->attribute(key);
374         if ( child_value == value ||
375              value && child_value && !strcmp(child_value, value) )
376         {
377             return child;
378         }
379     }
380     return NULL;
383 /**
384  *  \brief   Recursively find the Inkscape::XML::Node matching the given XML name.
385  *  \return  A pointer to the matching Inkscape::XML::Node
386  *  \param   repr    The Inkscape::XML::Node to start from
387  *  \param   name    The desired XML name
388  *  
389  */
390 Inkscape::XML::Node *
391 sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
393     g_return_val_if_fail(repr != NULL, NULL);
394     g_return_val_if_fail(name != NULL, NULL);
396     GQuark const quark = g_quark_from_string(name);
398     if ( (GQuark)repr->code() == quark ) return repr;
399     if ( maxdepth == 0 ) return NULL;
401     // maxdepth == -1 means unlimited
402     if ( maxdepth == -1 ) maxdepth = 0;
404     Inkscape::XML::Node *found = NULL;
405     for (Inkscape::XML::Node *child = repr->firstChild() ; child && !found; child = child->next() ) {
406         found = sp_repr_lookup_name( child, name, maxdepth-1 );
407     }
409     return found;
412 /**
413  * Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
414  * the attr is not set.
415  *
416  * \return TRUE if the attr was set, FALSE otherwise.
417  */
418 unsigned int
419 sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
421     gchar const *v;
423     g_return_val_if_fail(repr != NULL, FALSE);
424     g_return_val_if_fail(key != NULL, FALSE);
425     g_return_val_if_fail(val != NULL, FALSE);
427     v = repr->attribute(key);
429     if (v != NULL) {
430         if (!g_strcasecmp(v, "true") ||
431             !g_strcasecmp(v, "yes" ) ||
432             !g_strcasecmp(v, "y"   ) ||
433             (atoi(v) != 0)) {
434             *val = TRUE;
435         } else {
436             *val = FALSE;
437         }
438         return TRUE;
439     } else {
440         *val = FALSE;
441         return FALSE;
442     }
445 unsigned int
446 sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
448     gchar const *v;
450     g_return_val_if_fail(repr != NULL, FALSE);
451     g_return_val_if_fail(key != NULL, FALSE);
452     g_return_val_if_fail(val != NULL, FALSE);
454     v = repr->attribute(key);
456     if (v != NULL) {
457         *val = atoi(v);
458         return TRUE;
459     }
461     return FALSE;
464 unsigned int
465 sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
467     gchar const *v;
469     g_return_val_if_fail(repr != NULL, FALSE);
470     g_return_val_if_fail(key != NULL, FALSE);
471     g_return_val_if_fail(val != NULL, FALSE);
473     v = repr->attribute(key);
475     if (v != NULL) {
476         *val = g_ascii_strtod(v, NULL);
477         return TRUE;
478     }
480     return FALSE;
483 unsigned int
484 sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
486     g_return_val_if_fail(repr != NULL, FALSE);
487     g_return_val_if_fail(key != NULL, FALSE);
489     repr->setAttribute(key, (val) ? "true" : "false");
490     return true;
493 unsigned int
494 sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
496     gchar c[32];
498     g_return_val_if_fail(repr != NULL, FALSE);
499     g_return_val_if_fail(key != NULL, FALSE);
501     g_snprintf(c, 32, "%d", val);
503     repr->setAttribute(key, c);
504     return true;
507 /**
508  * Set a property attribute to \a val [slightly rounded], in the format
509  * required for CSS properties: in particular, it never uses exponent
510  * notation.
511  */
512 unsigned int
513 sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
515     g_return_val_if_fail(repr != NULL, FALSE);
516     g_return_val_if_fail(key != NULL, FALSE);
518     Inkscape::CSSOStringStream os;
519     os << val;
521     repr->setAttribute(key, os.str().c_str());
522     return true;
525 /**
526  * For attributes where an exponent is allowed.
527  *
528  * Not suitable for property attributes (fill-opacity, font-size etc.).
529  */
530 unsigned int
531 sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
533     g_return_val_if_fail(repr != NULL, FALSE);
534     g_return_val_if_fail(key != NULL, FALSE);
536     Inkscape::SVGOStringStream os;
537     os << val;
539     repr->setAttribute(key, os.str().c_str());
540     return true;
544 /*
545   Local Variables:
546   mode:c++
547   c-file-style:"stroustrup"
548   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
549   indent-tabs-mode:nil
550   fill-column:99
551   End:
552 */
553 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :