1
#define __SP_REPR_UTIL_C__
4
* Miscellaneous helpers for reprs.
9
* Lauris Kaplinski <lauris@ximian.com>
11
* Copyright (C) 1999-2000 Lauris Kaplinski
12
* Copyright (C) 2000-2001 Ximian, Inc.
13
* g++ port Copyright (C) 2003 Nathan Hurst
15
* Licensed under GNU GPL
32
#include <2geom/point.h>
33
#include "svg/stringstream.h"
34
#include "svg/css-ostringstream.h"
37
#include "xml/repr-sorting.h"
40
#define OSB_NS_URI "http://www.openswatchbook.org/uri/2009/osb"
45
unsigned int uri, prefix;
48
/*#####################
50
#####################*/
57
# define TRUE (!FALSE)
61
# define MAX(a,b) (((a) < (b)) ? (b) : (a))
64
/*#####################
65
# FORWARD DECLARATIONS
66
#####################*/
68
static void sp_xml_ns_register_defaults();
69
static char *sp_xml_ns_auto_prefix(char const *uri);
71
/*#####################
73
#####################*/
76
* Locale-independent double to string conversion
79
sp_xml_dtoa(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
81
double dival, fval, epsilon;
88
/* Determine number of integral digits */
90
idigits = (int) floor(log10(val));
94
/* Determine the actual number of fractional digits */
95
fprec = MAX(fprec, tprec - idigits);
97
epsilon = 0.5 * pow(10.0, - (double) fprec);
100
/* Extract integral and fractional parts */
110
c[32 - (++j)] = '0' + (ival % 10);
113
memcpy(buf + i, &c[32 - j], j);
120
if ((fprec > 0) && (padf || (fval > epsilon))) {
122
while ((fprec > 0) && (padf || (fval > epsilon))) {
126
buf[i++] = '0' + (int) dival;
139
/*#####################
141
#####################*/
147
static SPXMLNs *namespaces=NULL;
150
* There are the prefixes to use for the XML namespaces defined
154
sp_xml_ns_register_defaults()
156
static SPXMLNs defaults[11];
158
defaults[0].uri = g_quark_from_static_string(SP_SODIPODI_NS_URI);
159
defaults[0].prefix = g_quark_from_static_string("sodipodi");
160
defaults[0].next = &defaults[1];
162
defaults[1].uri = g_quark_from_static_string(SP_XLINK_NS_URI);
163
defaults[1].prefix = g_quark_from_static_string("xlink");
164
defaults[1].next = &defaults[2];
166
defaults[2].uri = g_quark_from_static_string(SP_SVG_NS_URI);
167
defaults[2].prefix = g_quark_from_static_string("svg");
168
defaults[2].next = &defaults[3];
170
defaults[3].uri = g_quark_from_static_string(SP_INKSCAPE_NS_URI);
171
defaults[3].prefix = g_quark_from_static_string("inkscape");
172
defaults[3].next = &defaults[4];
174
defaults[4].uri = g_quark_from_static_string(SP_RDF_NS_URI);
175
defaults[4].prefix = g_quark_from_static_string("rdf");
176
defaults[4].next = &defaults[5];
178
defaults[5].uri = g_quark_from_static_string(SP_CC_NS_URI);
179
defaults[5].prefix = g_quark_from_static_string("cc");
180
defaults[5].next = &defaults[6];
182
defaults[6].uri = g_quark_from_static_string(SP_DC_NS_URI);
183
defaults[6].prefix = g_quark_from_static_string("dc");
184
defaults[6].next = &defaults[7];
186
defaults[7].uri = g_quark_from_static_string(OSB_NS_URI);
187
defaults[7].prefix = g_quark_from_static_string("osb");
188
defaults[7].next = &defaults[8];
190
// Inkscape versions prior to 0.44 would write this namespace
191
// URI instead of the correct sodipodi namespace; by adding this
192
// entry to the table last (where it gets used for URI -> prefix
193
// lookups, but not prefix -> URI lookups), we effectively transfer
194
// elements in this namespace to the correct sodipodi namespace:
196
defaults[8].uri = g_quark_from_static_string(SP_BROKEN_SODIPODI_NS_URI);
197
defaults[8].prefix = g_quark_from_static_string("sodipodi");
198
defaults[8].next = &defaults[9];
201
// This URL became widespread due to a bug in versions <= 0.43
203
defaults[9].uri = g_quark_from_static_string("http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd");
204
defaults[9].prefix = g_quark_from_static_string("sodipodi");
205
defaults[9].next = &defaults[10];
207
// This namespace URI is being phased out by Creative Commons
209
defaults[10].uri = g_quark_from_static_string(SP_OLD_CC_NS_URI);
210
defaults[10].prefix = g_quark_from_static_string("cc");
211
defaults[10].next = NULL;
213
namespaces = &defaults[0];
217
sp_xml_ns_auto_prefix(char const *uri)
219
char const *start, *end;
222
while ((end = strpbrk(start, ":/"))) {
225
end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
230
new_prefix = g_strndup(start, end - start);
231
if (sp_xml_ns_prefix_uri(new_prefix)) {
235
temp = g_strdup_printf("%s%d", new_prefix, counter++);
236
} while (sp_xml_ns_prefix_uri(temp));
244
sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
248
if (!uri) return NULL;
251
sp_xml_ns_register_defaults();
254
GQuark const key = g_quark_from_string(uri);
256
for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
257
if ( iter->uri == key ) {
258
prefix = g_quark_to_string(iter->prefix);
267
GQuark const prefix_key=g_quark_from_string(suggested);
269
SPXMLNs *found=namespaces;
270
while ( found && found->prefix != prefix_key ) {
274
if (found) { // prefix already used?
275
new_prefix = sp_xml_ns_auto_prefix(uri);
276
} else { // safe to use suggested
277
new_prefix = g_strdup(suggested);
280
new_prefix = sp_xml_ns_auto_prefix(uri);
283
ns = g_new(SPXMLNs, 1);
284
g_assert( ns != NULL );
285
ns->uri = g_quark_from_string(uri);
286
ns->prefix = g_quark_from_string(new_prefix);
290
ns->next = namespaces;
293
prefix = g_quark_to_string(ns->prefix);
300
sp_xml_ns_prefix_uri(gchar const *prefix)
305
if (!prefix) return NULL;
308
sp_xml_ns_register_defaults();
311
GQuark const key = g_quark_from_string(prefix);
313
for ( iter = namespaces ; iter ; iter = iter->next ) {
314
if ( iter->prefix == key ) {
315
uri = g_quark_to_string(iter->uri);
322
double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
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 g_ascii_strtod(result, NULL);
336
long long int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, long long int def)
340
g_return_val_if_fail(repr != NULL, def);
341
g_return_val_if_fail(key != NULL, def);
343
result = (char *) repr->attribute(key);
345
if (result == NULL) return def;
347
return atoll(result);
351
* Works for different-parent objects, so long as they have a common ancestor. Return value:
352
* 0 positions are equivalent
353
* 1 first object's position is greater than the second
354
* -1 first object's position is less than the second
355
* @todo Rewrite this function's description to be understandable
358
sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
361
if (sp_repr_parent(first) == sp_repr_parent(second)) {
362
/* Basic case - first and second have same parent */
363
p1 = first->position();
364
p2 = second->position();
366
/* Special case - the two objects have different parents. They
367
could be in different groups or on different layers for
370
// Find the lowest common ancestor(LCA)
371
Inkscape::XML::Node *ancestor = LCA(first, second);
372
g_assert(ancestor != NULL);
374
if (ancestor == first) {
376
} else if (ancestor == second) {
379
Inkscape::XML::Node const *to_first = AncetreFils(first, ancestor);
380
Inkscape::XML::Node const *to_second = AncetreFils(second, ancestor);
381
g_assert(sp_repr_parent(to_second) == sp_repr_parent(to_first));
382
p1 = to_first->position();
383
p2 = to_second->position();
387
if (p1 > p2) return 1;
388
if (p1 < p2) return -1;
391
/* effic: Assuming that the parent--child relationship is consistent
392
(i.e. that the parent really does contain first and second among
393
its list of children), it should be equivalent to walk along the
394
children and see which we encounter first (returning 0 iff first
397
Given that this function is used solely for sorting, we can use a
398
similar approach to do the sort: gather the things to be sorted,
399
into an STL vector (to allow random access and faster
400
traversals). Do a single pass of the parent's children; for each
401
child, do a pass on whatever items in the vector we haven't yet
402
encountered. If the child is found, then swap it to the
403
beginning of the yet-unencountered elements of the vector.
404
Continue until no more than one remains unencountered. --
409
* @brief Find an element node using an unique attribute
411
* This function returns the first child of the specified node that has the attribute
412
* @c key equal to @c value. Note that this function does not recurse.
414
* @param repr The node to start from
415
* @param key The name of the attribute to use for comparisons
416
* @param value The value of the attribute to look for
417
* @relatesalso Inkscape::XML::Node
419
Inkscape::XML::Node *
420
sp_repr_lookup_child(Inkscape::XML::Node *repr,
424
g_return_val_if_fail(repr != NULL, NULL);
425
for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
426
gchar const *child_value = child->attribute(key);
427
if ( (child_value == value) ||
428
(value && child_value && !strcmp(child_value, value)) )
437
* @brief Find an element node with the given name
439
* This function searches the descendants of the specified node depth-first for
440
* the first XML node with the specified name.
442
* @param repr The node to start from
443
* @param name The name of the element node to find
444
* @param maxdepth Maximum search depth, or -1 for an unlimited depth
445
* @return A pointer to the matching Inkscape::XML::Node
446
* @relatesalso Inkscape::XML::Node
448
Inkscape::XML::Node *
449
sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
451
g_return_val_if_fail(repr != NULL, NULL);
452
g_return_val_if_fail(name != NULL, NULL);
454
GQuark const quark = g_quark_from_string(name);
456
if ( (GQuark)repr->code() == quark ) return repr;
457
if ( maxdepth == 0 ) return NULL;
459
// maxdepth == -1 means unlimited
460
if ( maxdepth == -1 ) maxdepth = 0;
462
Inkscape::XML::Node *found = NULL;
463
for (Inkscape::XML::Node *child = repr->firstChild() ; child && !found; child = child->next() ) {
464
found = sp_repr_lookup_name( child, name, maxdepth-1 );
471
* Determine if the node is a 'title', 'desc' or 'metadata' element.
474
sp_repr_is_meta_element(const Inkscape::XML::Node *node)
476
if (node == NULL) return false;
477
if (node->type() != Inkscape::XML::ELEMENT_NODE) return false;
478
gchar const *name = node->name();
479
if (name == NULL) return false;
480
if (!std::strcmp(name, "svg:title")) return true;
481
if (!std::strcmp(name, "svg:desc")) return true;
482
if (!std::strcmp(name, "svg:metadata")) return true;
487
* Parses the boolean value of an attribute "key" in repr and sets val accordingly, or to FALSE if
488
* the attr is not set.
490
* \return TRUE if the attr was set, FALSE otherwise.
493
sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
497
g_return_val_if_fail(repr != NULL, FALSE);
498
g_return_val_if_fail(key != NULL, FALSE);
499
g_return_val_if_fail(val != NULL, FALSE);
501
v = repr->attribute(key);
504
if (!g_strcasecmp(v, "true") ||
505
!g_strcasecmp(v, "yes" ) ||
506
!g_strcasecmp(v, "y" ) ||
520
sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
524
g_return_val_if_fail(repr != NULL, FALSE);
525
g_return_val_if_fail(key != NULL, FALSE);
526
g_return_val_if_fail(val != NULL, FALSE);
528
v = repr->attribute(key);
539
sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
543
g_return_val_if_fail(repr != NULL, FALSE);
544
g_return_val_if_fail(key != NULL, FALSE);
545
g_return_val_if_fail(val != NULL, FALSE);
547
v = repr->attribute(key);
550
*val = g_ascii_strtod(v, NULL);
558
sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
560
g_return_val_if_fail(repr != NULL, FALSE);
561
g_return_val_if_fail(key != NULL, FALSE);
563
repr->setAttribute(key, (val) ? "true" : "false");
568
sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
572
g_return_val_if_fail(repr != NULL, FALSE);
573
g_return_val_if_fail(key != NULL, FALSE);
575
g_snprintf(c, 32, "%d", val);
577
repr->setAttribute(key, c);
582
* Set a property attribute to \a val [slightly rounded], in the format
583
* required for CSS properties: in particular, it never uses exponent
587
sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
589
g_return_val_if_fail(repr != NULL, FALSE);
590
g_return_val_if_fail(key != NULL, FALSE);
592
Inkscape::CSSOStringStream os;
595
repr->setAttribute(key, os.str().c_str());
600
* For attributes where an exponent is allowed.
602
* Not suitable for property attributes (fill-opacity, font-size etc.).
605
sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
607
g_return_val_if_fail(repr != NULL, FALSE);
608
g_return_val_if_fail(key != NULL, FALSE);
610
Inkscape::SVGOStringStream os;
613
repr->setAttribute(key, os.str().c_str());
617
unsigned sp_repr_set_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point const & val)
619
g_return_val_if_fail(repr != NULL, FALSE);
620
g_return_val_if_fail(key != NULL, FALSE);
622
Inkscape::SVGOStringStream os;
623
os << val[Geom::X] << "," << val[Geom::Y];
625
repr->setAttribute(key, os.str().c_str());
630
sp_repr_get_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point *val)
632
g_return_val_if_fail(repr != NULL, FALSE);
633
g_return_val_if_fail(key != NULL, FALSE);
634
g_return_val_if_fail(val != NULL, FALSE);
636
gchar const *v = repr->attribute(key);
638
gchar ** strarray = g_strsplit(v, ",", 2);
640
if (strarray && strarray[0] && strarray[1]) {
642
newx = g_ascii_strtod(strarray[0], NULL);
643
newy = g_ascii_strtod(strarray[1], NULL);
644
g_strfreev (strarray);
645
*val = Geom::Point(newx, newy);
649
g_strfreev (strarray);
656
c-file-style:"stroustrup"
657
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
662
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :