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[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];
183 }
185 char *
186 sp_xml_ns_auto_prefix(char const *uri)
187 {
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;
210 }
212 gchar const *
213 sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
214 {
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;
253 }
255 gchar const *
256 sp_xml_ns_prefix_uri(gchar const *prefix)
257 {
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;
276 }
278 double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
279 {
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);
290 }
292 int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, int def)
293 {
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);
304 }
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)
314 {
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).
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 */
361 }
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)
370 {
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;
381 }
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 )
392 {
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;
410 }
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)
420 {
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 }
443 }
445 unsigned int
446 sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
447 {
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;
462 }
464 unsigned int
465 sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
466 {
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;
481 }
483 unsigned int
484 sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
485 {
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;
491 }
493 unsigned int
494 sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
495 {
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;
505 }
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)
514 {
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;
523 }
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)
532 {
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;
541 }
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 :