~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/xml/repr-util.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_REPR_UTIL_C__
 
2
 
 
3
/** \file
 
4
 * Miscellaneous helpers for reprs.
 
5
 */
 
6
 
 
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
 */
 
17
 
 
18
#include "config.h"
 
19
 
 
20
#include <math.h>
 
21
 
 
22
#if HAVE_STRING_H
 
23
# include <cstring>
 
24
#endif
 
25
 
 
26
#if HAVE_STDLIB_H
 
27
# include <cstdlib>
 
28
#endif
 
29
 
 
30
 
 
31
#include <glib.h>
 
32
#include <2geom/point.h>
 
33
#include "svg/stringstream.h"
 
34
#include "svg/css-ostringstream.h"
 
35
 
 
36
#include "xml/repr.h"
 
37
#include "xml/repr-sorting.h"
 
38
 
 
39
 
 
40
#define OSB_NS_URI "http://www.openswatchbook.org/uri/2009/osb"
 
41
 
 
42
 
 
43
struct SPXMLNs {
 
44
    SPXMLNs *next;
 
45
    unsigned int uri, prefix;
 
46
};
 
47
 
 
48
/*#####################
 
49
# DEFINITIONS
 
50
#####################*/
 
51
 
 
52
#ifndef FALSE
 
53
# define FALSE 0
 
54
#endif
 
55
 
 
56
#ifndef TRUE
 
57
# define TRUE (!FALSE)
 
58
#endif
 
59
 
 
60
#ifndef MAX
 
61
# define MAX(a,b) (((a) < (b)) ? (b) : (a))
 
62
#endif
 
63
 
 
64
/*#####################
 
65
# FORWARD DECLARATIONS
 
66
#####################*/
 
67
 
 
68
static void sp_xml_ns_register_defaults();
 
69
static char *sp_xml_ns_auto_prefix(char const *uri);
 
70
 
 
71
/*#####################
 
72
# UTILITY
 
73
#####################*/
 
74
 
 
75
/**
 
76
 * Locale-independent double to string conversion
 
77
 */
 
78
unsigned int
 
79
sp_xml_dtoa(gchar *buf, double val, unsigned int tprec, unsigned int fprec, unsigned int padf)
 
80
{
 
81
    double dival, fval, epsilon;
 
82
    int idigits, ival, i;
 
83
    i = 0;
 
84
    if (val < 0.0) {
 
85
        buf[i++] = '-';
 
86
        val = -val;
 
87
    }
 
88
    /* Determine number of integral digits */
 
89
    if (val >= 1.0) {
 
90
        idigits = (int) floor(log10(val));
 
91
    } else {
 
92
        idigits = 0;
 
93
    }
 
94
    /* Determine the actual number of fractional digits */
 
95
    fprec = MAX(fprec, tprec - idigits);
 
96
    /* Find epsilon */
 
97
    epsilon = 0.5 * pow(10.0, - (double) fprec);
 
98
    /* Round value */
 
99
    val += epsilon;
 
100
    /* Extract integral and fractional parts */
 
101
    dival = floor(val);
 
102
    ival = (int) dival;
 
103
    fval = val - dival;
 
104
    /* Write integra */
 
105
    if (ival > 0) {
 
106
        char c[32];
 
107
        int j;
 
108
        j = 0;
 
109
        while (ival > 0) {
 
110
            c[32 - (++j)] = '0' + (ival % 10);
 
111
            ival /= 10;
 
112
        }
 
113
        memcpy(buf + i, &c[32 - j], j);
 
114
        i += j;
 
115
        tprec -= j;
 
116
    } else {
 
117
        buf[i++] = '0';
 
118
        tprec -= 1;
 
119
    }
 
120
    if ((fprec > 0) && (padf || (fval > epsilon))) {
 
121
        buf[i++] = '.';
 
122
        while ((fprec > 0) && (padf || (fval > epsilon))) {
 
123
            fval *= 10.0;
 
124
            dival = floor(fval);
 
125
            fval -= dival;
 
126
            buf[i++] = '0' + (int) dival;
 
127
            fprec -= 1;
 
128
        }
 
129
 
 
130
    }
 
131
    buf[i] = 0;
 
132
    return i;
 
133
}
 
134
 
 
135
 
 
136
 
 
137
 
 
138
 
 
139
/*#####################
 
140
# MAIN
 
141
#####################*/
 
142
 
 
143
/**
 
144
 * SPXMLNs
 
145
 */
 
146
 
 
147
static SPXMLNs *namespaces=NULL;
 
148
 
 
149
/*
 
150
 * There are the prefixes to use for the XML namespaces defined
 
151
 * in repr.h
 
152
 */
 
153
static void
 
154
sp_xml_ns_register_defaults()
 
155
{
 
156
    static SPXMLNs defaults[11];
 
157
 
 
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];
 
161
 
 
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];
 
165
 
 
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];
 
169
 
 
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];
 
173
 
 
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];
 
177
 
 
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];
 
181
 
 
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];
 
185
 
 
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];
 
189
 
 
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:
 
195
 
 
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];
 
199
 
 
200
    // "Duck prion"
 
201
    // This URL became widespread due to a bug in versions <= 0.43
 
202
 
 
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];
 
206
 
 
207
    // This namespace URI is being phased out by Creative Commons
 
208
 
 
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;
 
212
 
 
213
    namespaces = &defaults[0];
 
214
}
 
215
 
 
216
char *
 
217
sp_xml_ns_auto_prefix(char const *uri)
 
218
{
 
219
    char const *start, *end;
 
220
    char *new_prefix;
 
221
    start = uri;
 
222
    while ((end = strpbrk(start, ":/"))) {
 
223
        start = end + 1;
 
224
    }
 
225
    end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
 
226
    if (end == start) {
 
227
        start = "ns";
 
228
        end = start + 2;
 
229
    }
 
230
    new_prefix = g_strndup(start, end - start);
 
231
    if (sp_xml_ns_prefix_uri(new_prefix)) {
 
232
        char *temp;
 
233
        int counter=0;
 
234
        do {
 
235
            temp = g_strdup_printf("%s%d", new_prefix, counter++);
 
236
        } while (sp_xml_ns_prefix_uri(temp));
 
237
        g_free(new_prefix);
 
238
        new_prefix = temp;
 
239
    }
 
240
    return new_prefix;
 
241
}
 
242
 
 
243
gchar const *
 
244
sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
 
245
{
 
246
    char const *prefix;
 
247
 
 
248
    if (!uri) return NULL;
 
249
 
 
250
    if (!namespaces) {
 
251
        sp_xml_ns_register_defaults();
 
252
    }
 
253
 
 
254
    GQuark const key = g_quark_from_string(uri);
 
255
    prefix = NULL;
 
256
    for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
 
257
        if ( iter->uri == key ) {
 
258
            prefix = g_quark_to_string(iter->prefix);
 
259
            break;
 
260
        }
 
261
    }
 
262
 
 
263
    if (!prefix) {
 
264
        char *new_prefix;
 
265
        SPXMLNs *ns;
 
266
        if (suggested) {
 
267
            GQuark const prefix_key=g_quark_from_string(suggested);
 
268
 
 
269
            SPXMLNs *found=namespaces;
 
270
            while ( found && found->prefix != prefix_key ) {
 
271
                found = found->next;
 
272
            }
 
273
 
 
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);
 
278
            }
 
279
        } else {
 
280
            new_prefix = sp_xml_ns_auto_prefix(uri);
 
281
        }
 
282
 
 
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);
 
287
 
 
288
        g_free(new_prefix);
 
289
 
 
290
        ns->next = namespaces;
 
291
        namespaces = ns;
 
292
 
 
293
        prefix = g_quark_to_string(ns->prefix);
 
294
    }
 
295
 
 
296
    return prefix;
 
297
}
 
298
 
 
299
gchar const *
 
300
sp_xml_ns_prefix_uri(gchar const *prefix)
 
301
{
 
302
    SPXMLNs *iter;
 
303
    char const *uri;
 
304
 
 
305
    if (!prefix) return NULL;
 
306
 
 
307
    if (!namespaces) {
 
308
        sp_xml_ns_register_defaults();
 
309
    }
 
310
 
 
311
    GQuark const key = g_quark_from_string(prefix);
 
312
    uri = NULL;
 
313
    for ( iter = namespaces ; iter ; iter = iter->next ) {
 
314
        if ( iter->prefix == key ) {
 
315
            uri = g_quark_to_string(iter->uri);
 
316
            break;
 
317
        }
 
318
    }
 
319
    return uri;
 
320
}
 
321
 
 
322
double sp_repr_get_double_attribute(Inkscape::XML::Node *repr, char const *key, double def)
 
323
{
 
324
    char *result;
 
325
 
 
326
    g_return_val_if_fail(repr != NULL, def);
 
327
    g_return_val_if_fail(key != NULL, def);
 
328
 
 
329
    result = (char *) repr->attribute(key);
 
330
 
 
331
    if (result == NULL) return def;
 
332
 
 
333
    return g_ascii_strtod(result, NULL);
 
334
}
 
335
 
 
336
long long int sp_repr_get_int_attribute(Inkscape::XML::Node *repr, char const *key, long long int def)
 
337
{
 
338
    char *result;
 
339
 
 
340
    g_return_val_if_fail(repr != NULL, def);
 
341
    g_return_val_if_fail(key != NULL, def);
 
342
 
 
343
    result = (char *) repr->attribute(key);
 
344
 
 
345
    if (result == NULL) return def;
 
346
 
 
347
    return atoll(result);
 
348
}
 
349
 
 
350
/** 
 
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
 
356
 */
 
357
int
 
358
sp_repr_compare_position(Inkscape::XML::Node *first, Inkscape::XML::Node *second)
 
359
{
 
360
    int p1, p2;
 
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();
 
365
    } else {
 
366
        /* Special case - the two objects have different parents.  They
 
367
           could be in different groups or on different layers for
 
368
           instance. */
 
369
 
 
370
        // Find the lowest common ancestor(LCA)
 
371
        Inkscape::XML::Node *ancestor = LCA(first, second);
 
372
        g_assert(ancestor != NULL);
 
373
 
 
374
        if (ancestor == first) {
 
375
            return 1;
 
376
        } else if (ancestor == second) {
 
377
            return -1;
 
378
        } else {
 
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();
 
384
        }
 
385
    }
 
386
 
 
387
    if (p1 > p2) return 1;
 
388
    if (p1 < p2) return -1;
 
389
    return 0;
 
390
 
 
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
 
395
       == second).
 
396
       
 
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.  --
 
405
       pjrm */
 
406
}
 
407
 
 
408
/**
 
409
 * @brief Find an element node using an unique attribute
 
410
 *
 
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.
 
413
 *
 
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
 
418
 */
 
419
Inkscape::XML::Node *
 
420
sp_repr_lookup_child(Inkscape::XML::Node *repr,
 
421
                     gchar const *key,
 
422
                     gchar const *value)
 
423
{
 
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)) )
 
429
        {
 
430
            return child;
 
431
        }
 
432
    }
 
433
    return NULL;
 
434
}
 
435
 
 
436
/**
 
437
 * @brief Find an element node with the given name
 
438
 *
 
439
 * This function searches the descendants of the specified node depth-first for
 
440
 * the first XML node with the specified name.
 
441
 *
 
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
 
447
 */
 
448
Inkscape::XML::Node *
 
449
sp_repr_lookup_name( Inkscape::XML::Node *repr, gchar const *name, gint maxdepth )
 
450
{
 
451
    g_return_val_if_fail(repr != NULL, NULL);
 
452
    g_return_val_if_fail(name != NULL, NULL);
 
453
 
 
454
    GQuark const quark = g_quark_from_string(name);
 
455
 
 
456
    if ( (GQuark)repr->code() == quark ) return repr;
 
457
    if ( maxdepth == 0 ) return NULL;
 
458
 
 
459
    // maxdepth == -1 means unlimited
 
460
    if ( maxdepth == -1 ) maxdepth = 0;
 
461
 
 
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 );
 
465
    }
 
466
 
 
467
    return found;
 
468
}
 
469
 
 
470
/**
 
471
 * Determine if the node is a 'title', 'desc' or 'metadata' element.
 
472
 */
 
473
bool
 
474
sp_repr_is_meta_element(const Inkscape::XML::Node *node)
 
475
{
 
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;
 
483
    return false;
 
484
}
 
485
 
 
486
/**
 
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.
 
489
 *
 
490
 * \return TRUE if the attr was set, FALSE otherwise.
 
491
 */
 
492
unsigned int
 
493
sp_repr_get_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int *val)
 
494
{
 
495
    gchar const *v;
 
496
 
 
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);
 
500
 
 
501
    v = repr->attribute(key);
 
502
 
 
503
    if (v != NULL) {
 
504
        if (!g_strcasecmp(v, "true") ||
 
505
            !g_strcasecmp(v, "yes" ) ||
 
506
            !g_strcasecmp(v, "y"   ) ||
 
507
            (atoi(v) != 0)) {
 
508
            *val = TRUE;
 
509
        } else {
 
510
            *val = FALSE;
 
511
        }
 
512
        return TRUE;
 
513
    } else {
 
514
        *val = FALSE;
 
515
        return FALSE;
 
516
    }
 
517
}
 
518
 
 
519
unsigned int
 
520
sp_repr_get_int(Inkscape::XML::Node *repr, gchar const *key, int *val)
 
521
{
 
522
    gchar const *v;
 
523
 
 
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);
 
527
 
 
528
    v = repr->attribute(key);
 
529
 
 
530
    if (v != NULL) {
 
531
        *val = atoi(v);
 
532
        return TRUE;
 
533
    }
 
534
 
 
535
    return FALSE;
 
536
}
 
537
 
 
538
unsigned int
 
539
sp_repr_get_double(Inkscape::XML::Node *repr, gchar const *key, double *val)
 
540
{
 
541
    gchar const *v;
 
542
 
 
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);
 
546
 
 
547
    v = repr->attribute(key);
 
548
 
 
549
    if (v != NULL) {
 
550
        *val = g_ascii_strtod(v, NULL);
 
551
        return TRUE;
 
552
    }
 
553
 
 
554
    return FALSE;
 
555
}
 
556
 
 
557
unsigned int
 
558
sp_repr_set_boolean(Inkscape::XML::Node *repr, gchar const *key, unsigned int val)
 
559
{
 
560
    g_return_val_if_fail(repr != NULL, FALSE);
 
561
    g_return_val_if_fail(key != NULL, FALSE);
 
562
 
 
563
    repr->setAttribute(key, (val) ? "true" : "false");
 
564
    return true;
 
565
}
 
566
 
 
567
unsigned int
 
568
sp_repr_set_int(Inkscape::XML::Node *repr, gchar const *key, int val)
 
569
{
 
570
    gchar c[32];
 
571
 
 
572
    g_return_val_if_fail(repr != NULL, FALSE);
 
573
    g_return_val_if_fail(key != NULL, FALSE);
 
574
 
 
575
    g_snprintf(c, 32, "%d", val);
 
576
 
 
577
    repr->setAttribute(key, c);
 
578
    return true;
 
579
}
 
580
 
 
581
/**
 
582
 * Set a property attribute to \a val [slightly rounded], in the format
 
583
 * required for CSS properties: in particular, it never uses exponent
 
584
 * notation.
 
585
 */
 
586
unsigned int
 
587
sp_repr_set_css_double(Inkscape::XML::Node *repr, gchar const *key, double val)
 
588
{
 
589
    g_return_val_if_fail(repr != NULL, FALSE);
 
590
    g_return_val_if_fail(key != NULL, FALSE);
 
591
 
 
592
    Inkscape::CSSOStringStream os;
 
593
    os << val;
 
594
 
 
595
    repr->setAttribute(key, os.str().c_str());
 
596
    return true;
 
597
}
 
598
 
 
599
/**
 
600
 * For attributes where an exponent is allowed.
 
601
 *
 
602
 * Not suitable for property attributes (fill-opacity, font-size etc.).
 
603
 */
 
604
unsigned int
 
605
sp_repr_set_svg_double(Inkscape::XML::Node *repr, gchar const *key, double val)
 
606
{
 
607
    g_return_val_if_fail(repr != NULL, FALSE);
 
608
    g_return_val_if_fail(key != NULL, FALSE);
 
609
 
 
610
    Inkscape::SVGOStringStream os;
 
611
    os << val;
 
612
 
 
613
    repr->setAttribute(key, os.str().c_str());
 
614
    return true;
 
615
}
 
616
 
 
617
unsigned sp_repr_set_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point const & val)
 
618
{
 
619
    g_return_val_if_fail(repr != NULL, FALSE);
 
620
    g_return_val_if_fail(key != NULL, FALSE);
 
621
 
 
622
    Inkscape::SVGOStringStream os;
 
623
    os << val[Geom::X] << "," << val[Geom::Y];
 
624
 
 
625
    repr->setAttribute(key, os.str().c_str());
 
626
    return true;
 
627
}
 
628
 
 
629
unsigned int
 
630
sp_repr_get_point(Inkscape::XML::Node *repr, gchar const *key, Geom::Point *val)
 
631
{
 
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);
 
635
 
 
636
    gchar const *v = repr->attribute(key);
 
637
 
 
638
    gchar ** strarray = g_strsplit(v, ",", 2);
 
639
 
 
640
    if (strarray && strarray[0] && strarray[1]) {
 
641
        double newx, newy;
 
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);
 
646
        return TRUE;
 
647
    }
 
648
 
 
649
    g_strfreev (strarray);
 
650
    return FALSE;
 
651
}
 
652
 
 
653
/*
 
654
  Local Variables:
 
655
  mode:c++
 
656
  c-file-style:"stroustrup"
 
657
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
658
  indent-tabs-mode:nil
 
659
  fill-column:99
 
660
  End:
 
661
*/
 
662
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :