~mc.../inkscape/inkscape

« back to all changes in this revision

Viewing changes to src/xml/repr-io.cpp

  • Committer: mental
  • Date: 2006-01-16 02:36:01 UTC
  • Revision ID: mental@users.sourceforge.net-20060116023601-wkr0h7edl5veyudq
moving trunk for module inkscape

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_REPR_IO_C__
 
2
 
 
3
/*
 
4
 * Dirty DOM-like  tree
 
5
 *
 
6
 * Authors:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *   bulia byak <buliabyak@users.sf.net>
 
9
 *
 
10
 * Copyright (C) 1999-2002 Lauris Kaplinski
 
11
 *
 
12
 * Released under GNU GPL, read the file 'COPYING' for more information
 
13
 */
 
14
 
 
15
#ifdef HAVE_CONFIG_H
 
16
# include <config.h>
 
17
#endif
 
18
 
 
19
 
 
20
 
 
21
#include "xml/repr.h"
 
22
#include "xml/attribute-record.h"
 
23
 
 
24
#include "io/sys.h"
 
25
#include "io/uristream.h"
 
26
#include "io/gzipstream.h"
 
27
 
 
28
 
 
29
using Inkscape::IO::Writer;
 
30
using Inkscape::Util::List;
 
31
using Inkscape::Util::cons;
 
32
using Inkscape::XML::Document;
 
33
using Inkscape::XML::Node;
 
34
using Inkscape::XML::AttributeRecord;
 
35
 
 
36
static Document *sp_repr_do_read (xmlDocPtr doc, const gchar *default_ns);
 
37
static Node *sp_repr_svg_read_node (xmlNodePtr node, const gchar *default_ns, GHashTable *prefix_map);
 
38
static gint sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar *default_ns, GHashTable *prefix_map);
 
39
static void sp_repr_write_stream_root_element (Node *repr, Writer &out, gboolean add_whitespace, gchar const *default_ns);
 
40
static void sp_repr_write_stream (Node *repr, Writer &out, gint indent_level, gboolean add_whitespace, Glib::QueryQuark elide_prefix);
 
41
static void sp_repr_write_stream_element (Node *repr, Writer &out, gint indent_level, gboolean add_whitespace, Glib::QueryQuark elide_prefix, List<AttributeRecord const> attributes);
 
42
 
 
43
#ifdef HAVE_LIBWMF
 
44
static xmlDocPtr sp_wmf_convert (const char * file_name);
 
45
static char * sp_wmf_image_name (void * context);
 
46
#endif /* HAVE_LIBWMF */
 
47
 
 
48
 
 
49
class XmlSource
 
50
{
 
51
public:
 
52
    XmlSource()
 
53
        : filename(0),
 
54
          fp(0),
 
55
          first(false),
 
56
          dummy("x"),
 
57
          instr(0),
 
58
          gzin(0)
 
59
    {
 
60
    }
 
61
    virtual ~XmlSource()
 
62
    {
 
63
        close();
 
64
    }
 
65
 
 
66
    void setFile( char const * filename );
 
67
 
 
68
    static int readCb( void * context, char * buffer, int len);
 
69
    static int closeCb(void * context);
 
70
 
 
71
 
 
72
    int read( char * buffer, int len );
 
73
    int close();
 
74
private:
 
75
    const char* filename;
 
76
    FILE* fp;
 
77
    bool first;
 
78
    Inkscape::URI dummy;
 
79
    Inkscape::IO::UriInputStream* instr;
 
80
    Inkscape::IO::GzipInputStream* gzin;
 
81
};
 
82
 
 
83
void XmlSource::setFile( char const * filename ) {
 
84
    this->filename = filename;
 
85
    fp = Inkscape::IO::fopen_utf8name(filename, "r");
 
86
    first = true;
 
87
}
 
88
 
 
89
 
 
90
int XmlSource::readCb( void * context, char * buffer, int len )
 
91
{
 
92
    int retVal = -1;
 
93
    if ( context ) {
 
94
        XmlSource* self = static_cast<XmlSource*>(context);
 
95
        retVal = self->read( buffer, len );
 
96
    }
 
97
    return retVal;
 
98
}
 
99
 
 
100
int XmlSource::closeCb(void * context)
 
101
{
 
102
    if ( context ) {
 
103
        XmlSource* self = static_cast<XmlSource*>(context);
 
104
        self->close();
 
105
    }
 
106
    return 0;
 
107
}
 
108
 
 
109
int XmlSource::read( char *buffer, int len )
 
110
{
 
111
    int retVal = 0;
 
112
    size_t got = 0;
 
113
 
 
114
    if ( first ) {
 
115
        first = false;
 
116
        char tmp[] = {0,0};
 
117
        size_t some = fread( tmp, 1, 2, fp );
 
118
 
 
119
        if ( (some >= 2) && (tmp[0] == 0x1f) && ((unsigned char)(tmp[1]) == 0x8b) ) {
 
120
            //g_message(" the file being read is gzip'd. extract it");
 
121
            fclose(fp);
 
122
            fp = 0;
 
123
            fp = Inkscape::IO::fopen_utf8name(filename, "r");
 
124
            instr = new Inkscape::IO::UriInputStream(fp, dummy);
 
125
            gzin = new Inkscape::IO::GzipInputStream(*instr);
 
126
            int single = 0;
 
127
            while ( (int)got < len && single >= 0 )
 
128
            {
 
129
                single = gzin->get();
 
130
                if ( single >= 0 ) {
 
131
                    buffer[got++] = 0x0ff & single;
 
132
                } else {
 
133
                    break;
 
134
                }
 
135
            }
 
136
            //g_message(" extracted %d bytes this pass", got );
 
137
        } else {
 
138
            memcpy( buffer, tmp, some );
 
139
            got = some;
 
140
        }
 
141
    } else if ( gzin ) {
 
142
        int single = 0;
 
143
        while ( (int)got < len && single >= 0 )
 
144
        {
 
145
            single = gzin->get();
 
146
            if ( single >= 0 ) {
 
147
                buffer[got++] = 0x0ff & single;
 
148
            } else {
 
149
                break;
 
150
            }
 
151
        }
 
152
        //g_message(" extracted %d bytes this pass  b", got );
 
153
    } else {
 
154
        got = fread( buffer, 1, len, fp );
 
155
    }
 
156
 
 
157
    if ( feof(fp) ) {
 
158
        retVal = got;
 
159
    }
 
160
    else if ( ferror(fp) ) {
 
161
        retVal = -1;
 
162
    }
 
163
    else {
 
164
        retVal = got;
 
165
    }
 
166
 
 
167
    return retVal;
 
168
}
 
169
 
 
170
int XmlSource::close()
 
171
{
 
172
    if ( gzin ) {
 
173
        gzin->close();
 
174
        delete gzin;
 
175
        gzin = 0;
 
176
    }
 
177
    if ( instr ) {
 
178
        instr->close();
 
179
        fp = 0;
 
180
        delete instr;
 
181
        instr = 0;
 
182
    }
 
183
    if ( fp ) {
 
184
        fclose(fp);
 
185
        fp = 0;
 
186
    }
 
187
    return 0;
 
188
}
 
189
 
 
190
/**
 
191
 * Reads XML from a file, including WMF files, and returns the Document.
 
192
 * The default namespace can also be specified, if desired.
 
193
 */
 
194
Document *
 
195
sp_repr_read_file (const gchar * filename, const gchar *default_ns)
 
196
{
 
197
    xmlDocPtr doc = 0;
 
198
    Document * rdoc = 0;
 
199
 
 
200
    xmlSubstituteEntitiesDefault(1);
 
201
 
 
202
    g_return_val_if_fail (filename != NULL, NULL);
 
203
    g_return_val_if_fail (Inkscape::IO::file_test( filename, G_FILE_TEST_EXISTS ), NULL);
 
204
 
 
205
    // TODO: bulia, please look over
 
206
    gsize bytesRead = 0;
 
207
    gsize bytesWritten = 0;
 
208
    GError* error = NULL;
 
209
    // TODO: need to replace with our own fopen and reading
 
210
    gchar* localFilename = g_filename_from_utf8 ( filename,
 
211
                                 -1,  &bytesRead,  &bytesWritten, &error);
 
212
    g_return_val_if_fail( localFilename != NULL, NULL );
 
213
 
 
214
    Inkscape::IO::dump_fopen_call( filename, "N" );
 
215
 
 
216
    XmlSource src;
 
217
    src.setFile(filename);
 
218
 
 
219
    xmlDocPtr doubleDoc = xmlReadIO( XmlSource::readCb,
 
220
                                     XmlSource::closeCb,
 
221
                                     &src,
 
222
                                     localFilename,
 
223
                                     NULL, //"UTF-8",
 
224
                                     XML_PARSE_NOENT );
 
225
 
 
226
 
 
227
#ifdef HAVE_LIBWMF
 
228
    if (strlen (localFilename) > 4) {
 
229
        if ( (strcmp (localFilename + strlen (localFilename) - 4,".wmf") == 0)
 
230
          || (strcmp (localFilename + strlen (localFilename) - 4,".WMF") == 0))
 
231
            doc = sp_wmf_convert (localFilename);
 
232
        else
 
233
            doc = xmlParseFile (localFilename);
 
234
    }
 
235
    else {
 
236
        doc = xmlParseFile (localFilename);
 
237
    }
 
238
#else /* !HAVE_LIBWMF */
 
239
    //doc = xmlParseFile (localFilename);
 
240
#endif /* !HAVE_LIBWMF */
 
241
 
 
242
    //rdoc = sp_repr_do_read (doc, default_ns);
 
243
    rdoc = sp_repr_do_read (doubleDoc, default_ns);
 
244
    if (doc)
 
245
        xmlFreeDoc (doc);
 
246
 
 
247
    if ( localFilename != NULL )
 
248
        g_free (localFilename);
 
249
 
 
250
    if ( doubleDoc != NULL )
 
251
    {
 
252
        xmlFreeDoc( doubleDoc );
 
253
    }
 
254
 
 
255
    return rdoc;
 
256
}
 
257
 
 
258
/**
 
259
 * Reads and parses XML from a buffer, returning it as an Document
 
260
 */
 
261
Document *
 
262
sp_repr_read_mem (const gchar * buffer, gint length, const gchar *default_ns)
 
263
{
 
264
    xmlDocPtr doc;
 
265
    Document * rdoc;
 
266
 
 
267
    xmlSubstituteEntitiesDefault(1);
 
268
 
 
269
    g_return_val_if_fail (buffer != NULL, NULL);
 
270
 
 
271
    doc = xmlParseMemory ((gchar *) buffer, length);
 
272
 
 
273
    rdoc = sp_repr_do_read (doc, default_ns);
 
274
    if (doc)
 
275
        xmlFreeDoc (doc);
 
276
    return rdoc;
 
277
}
 
278
 
 
279
namespace Inkscape {
 
280
 
 
281
struct compare_quark_ids {
 
282
    bool operator()(Glib::QueryQuark const &a, Glib::QueryQuark const &b) {
 
283
        return a.id() < b.id();
 
284
    }
 
285
};
 
286
 
 
287
}
 
288
 
 
289
namespace {
 
290
 
 
291
typedef std::map<Glib::QueryQuark, Glib::QueryQuark, Inkscape::compare_quark_ids> PrefixMap;
 
292
 
 
293
Glib::QueryQuark qname_prefix(Glib::QueryQuark qname) {
 
294
    static PrefixMap prefix_map;
 
295
    PrefixMap::iterator iter = prefix_map.find(qname);
 
296
    if ( iter != prefix_map.end() ) {
 
297
        return (*iter).second;
 
298
    } else {
 
299
        gchar const *name_string=g_quark_to_string(qname);
 
300
        gchar const *prefix_end=strchr(name_string, ':');
 
301
        if (prefix_end) {
 
302
            Glib::Quark prefix=Glib::ustring(name_string, prefix_end);
 
303
            prefix_map.insert(PrefixMap::value_type(qname, prefix));
 
304
            return prefix;
 
305
        } else {
 
306
            return GQuark(0);
 
307
        }
 
308
    }
 
309
}
 
310
 
 
311
}
 
312
 
 
313
namespace {
 
314
 
 
315
void promote_to_svg_namespace(Node *repr) {
 
316
    if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
 
317
        GQuark code = repr->code();
 
318
        if (!qname_prefix(code).id()) {
 
319
            gchar *svg_name = g_strconcat("svg:", g_quark_to_string(code), NULL);
 
320
            repr->setCodeUnsafe(g_quark_from_string(svg_name));
 
321
            g_free(svg_name);
 
322
        }
 
323
        for ( Node *child = sp_repr_children(repr) ; child ; child = sp_repr_next(child) ) {
 
324
            promote_to_svg_namespace(child);
 
325
        }
 
326
    }
 
327
}
 
328
 
 
329
}
 
330
 
 
331
/**
 
332
 * Reads in a XML file to create a Document
 
333
 */
 
334
Document *
 
335
sp_repr_do_read (xmlDocPtr doc, const gchar *default_ns)
 
336
{
 
337
    if (doc == NULL) return NULL;
 
338
    xmlNodePtr node=xmlDocGetRootElement (doc);
 
339
    if (node == NULL) return NULL;
 
340
 
 
341
    GHashTable * prefix_map;
 
342
    prefix_map = g_hash_table_new (g_str_hash, g_str_equal);
 
343
 
 
344
    GSList *reprs=NULL;
 
345
    Node *root=NULL;
 
346
 
 
347
    for ( node = doc->children ; node != NULL ; node = node->next ) {
 
348
        if (node->type == XML_ELEMENT_NODE) {
 
349
            Node *repr=sp_repr_svg_read_node (node, default_ns, prefix_map);
 
350
            reprs = g_slist_append(reprs, repr);
 
351
 
 
352
            if (!root) {
 
353
                root = repr;
 
354
            } else {
 
355
                root = NULL;
 
356
                break;
 
357
            }
 
358
        } else if ( node->type == XML_COMMENT_NODE ) {
 
359
            Node *comment=sp_repr_svg_read_node(node, default_ns, prefix_map);
 
360
            reprs = g_slist_append(reprs, comment);
 
361
        }
 
362
    }
 
363
 
 
364
    Document *rdoc=NULL;
 
365
 
 
366
    if (root != NULL) {
 
367
        /* promote elements of SVG documents that don't use namespaces
 
368
         * into the SVG namespace */
 
369
        if ( default_ns && !strcmp(default_ns, SP_SVG_NS_URI)
 
370
             && !strcmp(root->name(), "svg") )
 
371
        {
 
372
            promote_to_svg_namespace(root);
 
373
        }
 
374
 
 
375
        rdoc = sp_repr_document_new_list(reprs);
 
376
    }
 
377
 
 
378
    for ( GSList *iter = reprs ; iter ; iter = iter->next ) {
 
379
        Node *repr=(Node *)iter->data;
 
380
        Inkscape::GC::release(repr);
 
381
    }
 
382
    g_slist_free(reprs);
 
383
 
 
384
    g_hash_table_destroy (prefix_map);
 
385
 
 
386
    return rdoc;
 
387
}
 
388
 
 
389
gint
 
390
sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar *default_ns, GHashTable *prefix_map)
 
391
{
 
392
    const xmlChar *prefix;
 
393
    if ( ns && ns->href ) {
 
394
        prefix = (xmlChar*)sp_xml_ns_uri_prefix ((gchar*)ns->href, (char*)ns->prefix);
 
395
        g_hash_table_insert (prefix_map, (gpointer)prefix, (gpointer)ns->href);
 
396
    } else {
 
397
        prefix = NULL;
 
398
    }
 
399
 
 
400
    if (prefix)
 
401
        return g_snprintf (p, len, "%s:%s", (gchar*)prefix, name);
 
402
    else
 
403
        return g_snprintf (p, len, "%s", name);
 
404
}
 
405
 
 
406
static Node *
 
407
sp_repr_svg_read_node (xmlNodePtr node, const gchar *default_ns, GHashTable *prefix_map)
 
408
{
 
409
    Node *repr, *crepr;
 
410
    xmlAttrPtr prop;
 
411
    xmlNodePtr child;
 
412
    gchar c[256];
 
413
 
 
414
    if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE) {
 
415
 
 
416
        if (node->content == NULL || *(node->content) == '\0')
 
417
            return NULL; // empty text node
 
418
 
 
419
        bool preserve = (xmlNodeGetSpacePreserve (node) == 1);
 
420
 
 
421
        xmlChar *p;
 
422
        for (p = node->content; *p && g_ascii_isspace (*p) && !preserve; p++)
 
423
            ; // skip all whitespace
 
424
 
 
425
        if (!(*p)) { // this is an all-whitespace node, and preserve == default
 
426
            return NULL; // we do not preserve all-whitespace nodes unless we are asked to
 
427
        }
 
428
 
 
429
        Node *rdoc = sp_repr_new_text((const gchar *)node->content);
 
430
        return rdoc;
 
431
    }
 
432
 
 
433
    if (node->type == XML_COMMENT_NODE)
 
434
        return sp_repr_new_comment((const gchar *)node->content);
 
435
 
 
436
    if (node->type == XML_ENTITY_DECL) return NULL;
 
437
 
 
438
    sp_repr_qualified_name (c, 256, node->ns, node->name, default_ns, prefix_map);
 
439
    repr = sp_repr_new (c);
 
440
    /* TODO remember node->ns->prefix if node->ns != NULL */
 
441
 
 
442
    for (prop = node->properties; prop != NULL; prop = prop->next) {
 
443
        if (prop->children) {
 
444
            sp_repr_qualified_name (c, 256, prop->ns, prop->name, default_ns, prefix_map);
 
445
            repr->setAttribute(c, (gchar*)prop->children->content);
 
446
            /* TODO remember prop->ns->prefix if prop->ns != NULL */
 
447
        }
 
448
    }
 
449
 
 
450
    if (node->content)
 
451
        repr->setContent((gchar*)node->content);
 
452
 
 
453
    child = node->xmlChildrenNode;
 
454
    for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
 
455
        crepr = sp_repr_svg_read_node (child, default_ns, prefix_map);
 
456
        if (crepr) {
 
457
            repr->appendChild(crepr);
 
458
            Inkscape::GC::release(crepr);
 
459
        }
 
460
    }
 
461
 
 
462
    return repr;
 
463
}
 
464
 
 
465
void
 
466
sp_repr_save_stream (Document *doc, FILE *fp, gchar const *default_ns, bool compress)
 
467
{
 
468
    Node *repr;
 
469
    const gchar *str;
 
470
 
 
471
    Inkscape::URI dummy("x");
 
472
    Inkscape::IO::UriOutputStream bout(fp, dummy);
 
473
    Inkscape::IO::GzipOutputStream *gout = compress ? new Inkscape::IO::GzipOutputStream(bout) : NULL;
 
474
    Inkscape::IO::OutputStreamWriter *out  = compress ? new Inkscape::IO::OutputStreamWriter( *gout ) : new Inkscape::IO::OutputStreamWriter( bout );
 
475
 
 
476
    /* fixme: do this The Right Way */
 
477
 
 
478
    out->writeString( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" );
 
479
 
 
480
    str = ((Node *)doc)->attribute("doctype");
 
481
    if (str) {
 
482
        out->writeString( str );
 
483
    }
 
484
 
 
485
    repr = sp_repr_document_first_child(doc);
 
486
    for ( repr = sp_repr_document_first_child(doc) ;
 
487
          repr ; repr = sp_repr_next(repr) )
 
488
    {
 
489
        if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
 
490
            sp_repr_write_stream_root_element(repr, *out, TRUE, default_ns);
 
491
        } else if ( repr->type() == Inkscape::XML::COMMENT_NODE ) {
 
492
            sp_repr_write_stream(repr, *out, 0, TRUE, GQuark(0));
 
493
            out->writeChar( '\n' );
 
494
        } else {
 
495
            sp_repr_write_stream(repr, *out, 0, TRUE, GQuark(0));
 
496
        }
 
497
    }
 
498
    if ( out ) {
 
499
        delete out;
 
500
        out = NULL;
 
501
    }
 
502
    if ( gout ) {
 
503
        delete gout;
 
504
        gout = NULL;
 
505
    }
 
506
}
 
507
 
 
508
/* Returns TRUE if file successfully saved; FALSE if not
 
509
 */
 
510
gboolean
 
511
sp_repr_save_file (Document *doc, const gchar *filename,
 
512
                   gchar const *default_ns)
 
513
{
 
514
    if (filename == NULL) {
 
515
        return FALSE;
 
516
    }
 
517
    bool compress = false;
 
518
    {
 
519
        if (strlen (filename) > 5) {
 
520
            gchar tmp[] = {0,0,0,0,0,0};
 
521
            strncpy( tmp, filename + strlen (filename) - 5, 6 );
 
522
            tmp[5] = 0;
 
523
            if ( strcasecmp(".svgz", tmp ) == 0 )
 
524
            {
 
525
                //g_message("TIME TO COMPRESS THE OUTPUT FOR SVGZ");
 
526
                compress = true;
 
527
            }
 
528
        }
 
529
    }
 
530
 
 
531
    Inkscape::IO::dump_fopen_call( filename, "B" );
 
532
    FILE *file = Inkscape::IO::fopen_utf8name(filename, "w");
 
533
    if (file == NULL) {
 
534
        return FALSE;
 
535
    }
 
536
 
 
537
    sp_repr_save_stream (doc, file, default_ns, compress);
 
538
 
 
539
    if (fclose (file) != 0) {
 
540
        return FALSE;
 
541
    }
 
542
 
 
543
    return TRUE;
 
544
}
 
545
 
 
546
void
 
547
sp_repr_print (Node * repr)
 
548
{
 
549
    Inkscape::IO::StdOutputStream bout;
 
550
    Inkscape::IO::OutputStreamWriter out(bout);
 
551
 
 
552
    sp_repr_write_stream (repr, out, 0, TRUE, GQuark(0));
 
553
 
 
554
    return;
 
555
}
 
556
 
 
557
/* (No doubt this function already exists elsewhere.) */
 
558
static void
 
559
repr_quote_write (Writer &out, const gchar * val)
 
560
{
 
561
    if (!val) return;
 
562
 
 
563
    for (; *val != '\0'; val++) {
 
564
        switch (*val) {
 
565
        case '"': out.writeString( "&quot;" ); break;
 
566
        case '&': out.writeString( "&amp;" ); break;
 
567
        case '<': out.writeString( "&lt;" ); break;
 
568
        case '>': out.writeString( "&gt;" ); break;
 
569
        default: out.writeChar( *val ); break;
 
570
        }
 
571
    }
 
572
}
 
573
 
 
574
namespace {
 
575
 
 
576
typedef std::map<Glib::QueryQuark, gchar const *, Inkscape::compare_quark_ids> LocalNameMap;
 
577
typedef std::map<Glib::QueryQuark, Inkscape::Util::SharedCStringPtr, Inkscape::compare_quark_ids> NSMap;
 
578
 
 
579
gchar const *qname_local_name(Glib::QueryQuark qname) {
 
580
    static LocalNameMap local_name_map;
 
581
    LocalNameMap::iterator iter = local_name_map.find(qname);
 
582
    if ( iter != local_name_map.end() ) {
 
583
        return (*iter).second;
 
584
    } else {
 
585
        gchar const *name_string=g_quark_to_string(qname);
 
586
        gchar const *prefix_end=strchr(name_string, ':');
 
587
        if (prefix_end) {
 
588
            return prefix_end + 1;
 
589
        } else {
 
590
            return name_string;
 
591
        }
 
592
    }
 
593
}
 
594
 
 
595
void add_ns_map_entry(NSMap &ns_map, Glib::QueryQuark prefix) {
 
596
    using Inkscape::Util::SharedCStringPtr;
 
597
 
 
598
    static const Glib::QueryQuark xml_prefix("xml");
 
599
 
 
600
    NSMap::iterator iter=ns_map.find(prefix);
 
601
    if ( iter == ns_map.end() ) {
 
602
        if (prefix.id()) {
 
603
            gchar const *uri=sp_xml_ns_prefix_uri(g_quark_to_string(prefix));
 
604
            if (uri) {
 
605
                ns_map.insert(NSMap::value_type(prefix, SharedCStringPtr::coerce(uri)));
 
606
            } else if ( prefix != xml_prefix ) {
 
607
                g_warning("No namespace known for normalized prefix %s", g_quark_to_string(prefix));
 
608
            }
 
609
        } else {
 
610
            ns_map.insert(NSMap::value_type(prefix, SharedCStringPtr()));
 
611
        }
 
612
    }
 
613
}
 
614
 
 
615
void populate_ns_map(NSMap &ns_map, Node &repr) {
 
616
    if ( repr.type() == Inkscape::XML::ELEMENT_NODE ) {
 
617
        add_ns_map_entry(ns_map, qname_prefix(repr.code()));
 
618
        for ( List<AttributeRecord const> iter=repr.attributeList() ;
 
619
              iter ; ++iter )
 
620
        {
 
621
            Glib::QueryQuark prefix=qname_prefix(iter->key);
 
622
            if (prefix.id()) {
 
623
                add_ns_map_entry(ns_map, prefix);
 
624
            }
 
625
        }
 
626
        for ( Node *child=sp_repr_children(&repr) ;
 
627
              child ; child = sp_repr_next(child) )
 
628
        {
 
629
            populate_ns_map(ns_map, *child);
 
630
        }
 
631
    }
 
632
}
 
633
 
 
634
}
 
635
 
 
636
void
 
637
sp_repr_write_stream_root_element (Node *repr, Writer &out, gboolean add_whitespace, gchar const *default_ns)
 
638
{
 
639
    using Inkscape::Util::SharedCStringPtr;
 
640
    g_assert(repr != NULL);
 
641
    Glib::QueryQuark xml_prefix=g_quark_from_static_string("xml");
 
642
 
 
643
    NSMap ns_map;
 
644
    populate_ns_map(ns_map, *repr);
 
645
 
 
646
    Glib::QueryQuark elide_prefix=GQuark(0);
 
647
    if ( default_ns && ns_map.find(GQuark(0)) == ns_map.end() ) {
 
648
        elide_prefix = g_quark_from_string(sp_xml_ns_uri_prefix(default_ns, NULL));
 
649
    }
 
650
 
 
651
    List<AttributeRecord const> attributes=repr->attributeList();
 
652
    for ( NSMap::iterator iter=ns_map.begin() ; iter != ns_map.end() ; ++iter ) 
 
653
    {
 
654
        Glib::QueryQuark prefix=(*iter).first;
 
655
        SharedCStringPtr ns_uri=(*iter).second;
 
656
 
 
657
        if (prefix.id()) {
 
658
            if ( prefix != xml_prefix ) {
 
659
                if ( elide_prefix == prefix ) {
 
660
                    attributes = cons(AttributeRecord(g_quark_from_static_string("xmlns"), ns_uri), attributes);
 
661
                }
 
662
 
 
663
                Glib::ustring attr_name="xmlns:";
 
664
                attr_name.append(g_quark_to_string(prefix));
 
665
                GQuark key = g_quark_from_string(attr_name.c_str());
 
666
                attributes = cons(AttributeRecord(key, ns_uri), attributes);
 
667
            }
 
668
        } else {
 
669
            // if there are non-namespaced elements, we can't globally
 
670
            // use a default namespace
 
671
            elide_prefix = GQuark(0);
 
672
        }
 
673
    }
 
674
 
 
675
    return sp_repr_write_stream_element(repr, out, 0, add_whitespace, elide_prefix, attributes);
 
676
}
 
677
 
 
678
void
 
679
sp_repr_write_stream (Node *repr, Writer &out, gint indent_level,
 
680
                      gboolean add_whitespace, Glib::QueryQuark elide_prefix)
 
681
{
 
682
    if (repr->type() == Inkscape::XML::TEXT_NODE) {
 
683
        repr_quote_write (out, repr->content());
 
684
    } else if (repr->type() == Inkscape::XML::COMMENT_NODE) {
 
685
        out.printf( "<!--%s-->", repr->content() );
 
686
    } else if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
 
687
        sp_repr_write_stream_element(repr, out, indent_level, add_whitespace, elide_prefix, repr->attributeList());
 
688
    } else {
 
689
        g_assert_not_reached();
 
690
    }
 
691
}
 
692
 
 
693
void
 
694
sp_repr_write_stream_element (Node * repr, Writer & out, gint indent_level,
 
695
                              gboolean add_whitespace,
 
696
                              Glib::QueryQuark elide_prefix,
 
697
                              List<AttributeRecord const> attributes)
 
698
{
 
699
    Node *child;
 
700
    gboolean loose;
 
701
    gint i;
 
702
 
 
703
    g_return_if_fail (repr != NULL);
 
704
 
 
705
    if ( indent_level > 16 )
 
706
        indent_level = 16;
 
707
 
 
708
    if (add_whitespace) {
 
709
        for ( i = 0 ; i < indent_level ; i++ ) {
 
710
            out.writeString( "  " );
 
711
        }
 
712
    }
 
713
 
 
714
    GQuark code = repr->code();
 
715
    gchar const *element_name;
 
716
    if ( elide_prefix == qname_prefix(code) ) {
 
717
        element_name = qname_local_name(code);
 
718
    } else {
 
719
        element_name = g_quark_to_string(code);
 
720
    }
 
721
    out.printf( "<%s", element_name );
 
722
 
 
723
    // if this is a <text> element, suppress formatting whitespace
 
724
    // for its content and children:
 
725
    gchar const *xml_space_attr = repr->attribute("xml:space");
 
726
    if (xml_space_attr != NULL && !strcmp(xml_space_attr, "preserve")) {
 
727
        add_whitespace = FALSE;
 
728
    }
 
729
 
 
730
    for ( List<AttributeRecord const> iter = attributes ;
 
731
          iter ; ++iter )
 
732
    {
 
733
        out.writeString("\n");
 
734
        for ( i = 0 ; i < indent_level + 1 ; i++ ) {
 
735
            out.writeString("  ");
 
736
        }
 
737
        out.printf(" %s=\"", g_quark_to_string(iter->key));
 
738
        repr_quote_write(out, iter->value);
 
739
        out.writeChar('"');
 
740
    }
 
741
 
 
742
    loose = TRUE;
 
743
    for (child = repr->firstChild() ; child != NULL; child = child->next()) {
 
744
        if (child->type() == Inkscape::XML::TEXT_NODE) {
 
745
            loose = FALSE;
 
746
            break;
 
747
        }
 
748
    }
 
749
    if (repr->firstChild()) {
 
750
        out.writeString( ">" );
 
751
        if (loose && add_whitespace) {
 
752
            out.writeString( "\n" );
 
753
        }
 
754
        for (child = repr->firstChild(); child != NULL; child = child->next()) {
 
755
            sp_repr_write_stream (child, out, (loose) ? (indent_level + 1) : 0, add_whitespace, elide_prefix);
 
756
        }
 
757
 
 
758
        if (loose && add_whitespace) {
 
759
            for (i = 0; i < indent_level; i++) {
 
760
                out.writeString( "  " );
 
761
            }
 
762
        }
 
763
        out.printf( "</%s>", element_name );
 
764
    } else {
 
765
        out.writeString( " />" );
 
766
    }
 
767
 
 
768
    // text elements cannot nest, so we can output newline
 
769
    // after closing text
 
770
 
 
771
    if (add_whitespace || !strcmp (repr->name(), "svg:text")) {
 
772
        out.writeString( "\n" );
 
773
    }
 
774
}
 
775
 
 
776
 
 
777
/*
 
778
  Local Variables:
 
779
  mode:c++
 
780
  c-file-style:"stroustrup"
 
781
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
782
  indent-tabs-mode:nil
 
783
  fill-column:99
 
784
  End:
 
785
*/
 
786
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :