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;
129 }
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()
151 {
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];
187 }
189 char *
190 sp_xml_ns_auto_prefix(char const *uri)
191 {
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;
214 }
216 gchar const *
217 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
218 {
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;
270 }
272 gchar const *
273 sp_xml_ns_prefix_uri(gchar const *prefix)
274 {
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;
293 }
295 double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
296 {
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);
307 }
309 int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int def)
310 {
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);
321 }
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)
331 {
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).
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 */
378 }
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)
387 {
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;
398 }
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 )
409 {
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;
427 }
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)
437 {
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 }
460 }
462 unsigned int
463 sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
464 {
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;
479 }
481 unsigned int
482 sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
483 {
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;
498 }
500 unsigned int
501 sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
502 {
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;
508 }
510 unsigned int
511 sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
512 {
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;
522 }
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)
531 {
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;
540 }
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)
549 {
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;
558 }
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 :