1
#define __SP_REPR_IO_C__
7
* Lauris Kaplinski <lauris@kaplinski.com>
8
* bulia byak <buliabyak@users.sf.net>
10
* Copyright (C) 1999-2002 Lauris Kaplinski
12
* Released under GNU GPL, read the file 'COPYING' for more information
22
#include "xml/attribute-record.h"
25
#include "io/uristream.h"
26
#include "io/gzipstream.h"
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;
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);
44
static xmlDocPtr sp_wmf_convert (const char * file_name);
45
static char * sp_wmf_image_name (void * context);
46
#endif /* HAVE_LIBWMF */
66
void setFile( char const * filename );
68
static int readCb( void * context, char * buffer, int len);
69
static int closeCb(void * context);
72
int read( char * buffer, int len );
79
Inkscape::IO::UriInputStream* instr;
80
Inkscape::IO::GzipInputStream* gzin;
83
void XmlSource::setFile( char const * filename ) {
84
this->filename = filename;
85
fp = Inkscape::IO::fopen_utf8name(filename, "r");
90
int XmlSource::readCb( void * context, char * buffer, int len )
94
XmlSource* self = static_cast<XmlSource*>(context);
95
retVal = self->read( buffer, len );
100
int XmlSource::closeCb(void * context)
103
XmlSource* self = static_cast<XmlSource*>(context);
109
int XmlSource::read( char *buffer, int len )
117
size_t some = fread( tmp, 1, 2, fp );
119
if ( (some >= 2) && (tmp[0] == 0x1f) && ((unsigned char)(tmp[1]) == 0x8b) ) {
120
//g_message(" the file being read is gzip'd. extract it");
123
fp = Inkscape::IO::fopen_utf8name(filename, "r");
124
instr = new Inkscape::IO::UriInputStream(fp, dummy);
125
gzin = new Inkscape::IO::GzipInputStream(*instr);
127
while ( (int)got < len && single >= 0 )
129
single = gzin->get();
131
buffer[got++] = 0x0ff & single;
136
//g_message(" extracted %d bytes this pass", got );
138
memcpy( buffer, tmp, some );
143
while ( (int)got < len && single >= 0 )
145
single = gzin->get();
147
buffer[got++] = 0x0ff & single;
152
//g_message(" extracted %d bytes this pass b", got );
154
got = fread( buffer, 1, len, fp );
160
else if ( ferror(fp) ) {
170
int XmlSource::close()
191
* Reads XML from a file, including WMF files, and returns the Document.
192
* The default namespace can also be specified, if desired.
195
sp_repr_read_file (const gchar * filename, const gchar *default_ns)
200
xmlSubstituteEntitiesDefault(1);
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);
205
// TODO: bulia, please look over
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 );
214
Inkscape::IO::dump_fopen_call( filename, "N" );
217
src.setFile(filename);
219
xmlDocPtr doubleDoc = xmlReadIO( XmlSource::readCb,
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);
233
doc = xmlParseFile (localFilename);
236
doc = xmlParseFile (localFilename);
238
#else /* !HAVE_LIBWMF */
239
//doc = xmlParseFile (localFilename);
240
#endif /* !HAVE_LIBWMF */
242
//rdoc = sp_repr_do_read (doc, default_ns);
243
rdoc = sp_repr_do_read (doubleDoc, default_ns);
247
if ( localFilename != NULL )
248
g_free (localFilename);
250
if ( doubleDoc != NULL )
252
xmlFreeDoc( doubleDoc );
259
* Reads and parses XML from a buffer, returning it as an Document
262
sp_repr_read_mem (const gchar * buffer, gint length, const gchar *default_ns)
267
xmlSubstituteEntitiesDefault(1);
269
g_return_val_if_fail (buffer != NULL, NULL);
271
doc = xmlParseMemory ((gchar *) buffer, length);
273
rdoc = sp_repr_do_read (doc, default_ns);
281
struct compare_quark_ids {
282
bool operator()(Glib::QueryQuark const &a, Glib::QueryQuark const &b) {
283
return a.id() < b.id();
291
typedef std::map<Glib::QueryQuark, Glib::QueryQuark, Inkscape::compare_quark_ids> PrefixMap;
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;
299
gchar const *name_string=g_quark_to_string(qname);
300
gchar const *prefix_end=strchr(name_string, ':');
302
Glib::Quark prefix=Glib::ustring(name_string, prefix_end);
303
prefix_map.insert(PrefixMap::value_type(qname, prefix));
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));
323
for ( Node *child = sp_repr_children(repr) ; child ; child = sp_repr_next(child) ) {
324
promote_to_svg_namespace(child);
332
* Reads in a XML file to create a Document
335
sp_repr_do_read (xmlDocPtr doc, const gchar *default_ns)
337
if (doc == NULL) return NULL;
338
xmlNodePtr node=xmlDocGetRootElement (doc);
339
if (node == NULL) return NULL;
341
GHashTable * prefix_map;
342
prefix_map = g_hash_table_new (g_str_hash, g_str_equal);
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);
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);
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") )
372
promote_to_svg_namespace(root);
375
rdoc = sp_repr_document_new_list(reprs);
378
for ( GSList *iter = reprs ; iter ; iter = iter->next ) {
379
Node *repr=(Node *)iter->data;
380
Inkscape::GC::release(repr);
384
g_hash_table_destroy (prefix_map);
390
sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar *default_ns, GHashTable *prefix_map)
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);
401
return g_snprintf (p, len, "%s:%s", (gchar*)prefix, name);
403
return g_snprintf (p, len, "%s", name);
407
sp_repr_svg_read_node (xmlNodePtr node, const gchar *default_ns, GHashTable *prefix_map)
414
if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE) {
416
if (node->content == NULL || *(node->content) == '\0')
417
return NULL; // empty text node
419
bool preserve = (xmlNodeGetSpacePreserve (node) == 1);
422
for (p = node->content; *p && g_ascii_isspace (*p) && !preserve; p++)
423
; // skip all whitespace
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
429
Node *rdoc = sp_repr_new_text((const gchar *)node->content);
433
if (node->type == XML_COMMENT_NODE)
434
return sp_repr_new_comment((const gchar *)node->content);
436
if (node->type == XML_ENTITY_DECL) return NULL;
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 */
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 */
451
repr->setContent((gchar*)node->content);
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);
457
repr->appendChild(crepr);
458
Inkscape::GC::release(crepr);
466
sp_repr_save_stream (Document *doc, FILE *fp, gchar const *default_ns, bool compress)
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 );
476
/* fixme: do this The Right Way */
478
out->writeString( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" );
480
str = ((Node *)doc)->attribute("doctype");
482
out->writeString( str );
485
repr = sp_repr_document_first_child(doc);
486
for ( repr = sp_repr_document_first_child(doc) ;
487
repr ; repr = sp_repr_next(repr) )
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' );
495
sp_repr_write_stream(repr, *out, 0, TRUE, GQuark(0));
508
/* Returns TRUE if file successfully saved; FALSE if not
511
sp_repr_save_file (Document *doc, const gchar *filename,
512
gchar const *default_ns)
514
if (filename == NULL) {
517
bool compress = false;
519
if (strlen (filename) > 5) {
520
gchar tmp[] = {0,0,0,0,0,0};
521
strncpy( tmp, filename + strlen (filename) - 5, 6 );
523
if ( strcasecmp(".svgz", tmp ) == 0 )
525
//g_message("TIME TO COMPRESS THE OUTPUT FOR SVGZ");
531
Inkscape::IO::dump_fopen_call( filename, "B" );
532
FILE *file = Inkscape::IO::fopen_utf8name(filename, "w");
537
sp_repr_save_stream (doc, file, default_ns, compress);
539
if (fclose (file) != 0) {
547
sp_repr_print (Node * repr)
549
Inkscape::IO::StdOutputStream bout;
550
Inkscape::IO::OutputStreamWriter out(bout);
552
sp_repr_write_stream (repr, out, 0, TRUE, GQuark(0));
557
/* (No doubt this function already exists elsewhere.) */
559
repr_quote_write (Writer &out, const gchar * val)
563
for (; *val != '\0'; val++) {
565
case '"': out.writeString( """ ); break;
566
case '&': out.writeString( "&" ); break;
567
case '<': out.writeString( "<" ); break;
568
case '>': out.writeString( ">" ); break;
569
default: out.writeChar( *val ); break;
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;
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;
585
gchar const *name_string=g_quark_to_string(qname);
586
gchar const *prefix_end=strchr(name_string, ':');
588
return prefix_end + 1;
595
void add_ns_map_entry(NSMap &ns_map, Glib::QueryQuark prefix) {
596
using Inkscape::Util::SharedCStringPtr;
598
static const Glib::QueryQuark xml_prefix("xml");
600
NSMap::iterator iter=ns_map.find(prefix);
601
if ( iter == ns_map.end() ) {
603
gchar const *uri=sp_xml_ns_prefix_uri(g_quark_to_string(prefix));
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));
610
ns_map.insert(NSMap::value_type(prefix, SharedCStringPtr()));
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() ;
621
Glib::QueryQuark prefix=qname_prefix(iter->key);
623
add_ns_map_entry(ns_map, prefix);
626
for ( Node *child=sp_repr_children(&repr) ;
627
child ; child = sp_repr_next(child) )
629
populate_ns_map(ns_map, *child);
637
sp_repr_write_stream_root_element (Node *repr, Writer &out, gboolean add_whitespace, gchar const *default_ns)
639
using Inkscape::Util::SharedCStringPtr;
640
g_assert(repr != NULL);
641
Glib::QueryQuark xml_prefix=g_quark_from_static_string("xml");
644
populate_ns_map(ns_map, *repr);
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));
651
List<AttributeRecord const> attributes=repr->attributeList();
652
for ( NSMap::iterator iter=ns_map.begin() ; iter != ns_map.end() ; ++iter )
654
Glib::QueryQuark prefix=(*iter).first;
655
SharedCStringPtr ns_uri=(*iter).second;
658
if ( prefix != xml_prefix ) {
659
if ( elide_prefix == prefix ) {
660
attributes = cons(AttributeRecord(g_quark_from_static_string("xmlns"), ns_uri), attributes);
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);
669
// if there are non-namespaced elements, we can't globally
670
// use a default namespace
671
elide_prefix = GQuark(0);
675
return sp_repr_write_stream_element(repr, out, 0, add_whitespace, elide_prefix, attributes);
679
sp_repr_write_stream (Node *repr, Writer &out, gint indent_level,
680
gboolean add_whitespace, Glib::QueryQuark elide_prefix)
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());
689
g_assert_not_reached();
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)
703
g_return_if_fail (repr != NULL);
705
if ( indent_level > 16 )
708
if (add_whitespace) {
709
for ( i = 0 ; i < indent_level ; i++ ) {
710
out.writeString( " " );
714
GQuark code = repr->code();
715
gchar const *element_name;
716
if ( elide_prefix == qname_prefix(code) ) {
717
element_name = qname_local_name(code);
719
element_name = g_quark_to_string(code);
721
out.printf( "<%s", element_name );
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;
730
for ( List<AttributeRecord const> iter = attributes ;
733
out.writeString("\n");
734
for ( i = 0 ; i < indent_level + 1 ; i++ ) {
735
out.writeString(" ");
737
out.printf(" %s=\"", g_quark_to_string(iter->key));
738
repr_quote_write(out, iter->value);
743
for (child = repr->firstChild() ; child != NULL; child = child->next()) {
744
if (child->type() == Inkscape::XML::TEXT_NODE) {
749
if (repr->firstChild()) {
750
out.writeString( ">" );
751
if (loose && add_whitespace) {
752
out.writeString( "\n" );
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);
758
if (loose && add_whitespace) {
759
for (i = 0; i < indent_level; i++) {
760
out.writeString( " " );
763
out.printf( "</%s>", element_name );
765
out.writeString( " />" );
768
// text elements cannot nest, so we can output newline
769
// after closing text
771
if (add_whitespace || !strcmp (repr->name(), "svg:text")) {
772
out.writeString( "\n" );
780
c-file-style:"stroustrup"
781
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
786
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :