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
24
#include "xml/attribute-record.h"
25
#include "xml/rebase-hrefs.h"
26
#include "xml/simple-document.h"
29
#include "io/uristream.h"
30
#include "io/stringstream.h"
31
#include "io/gzipstream.h"
33
#include "extension/extension.h"
35
#include "preferences.h"
37
using Inkscape::IO::Writer;
38
using Inkscape::Util::List;
39
using Inkscape::Util::cons;
40
using Inkscape::XML::Document;
41
using Inkscape::XML::SimpleDocument;
42
using Inkscape::XML::Node;
43
using Inkscape::XML::AttributeRecord;
44
using Inkscape::XML::calc_abs_doc_base;
45
using Inkscape::XML::rebase_href_attrs;
47
Document *sp_repr_do_read (xmlDocPtr doc, const gchar *default_ns);
48
static Node *sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gchar *default_ns, GHashTable *prefix_map);
49
static gint sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar *default_ns, GHashTable *prefix_map);
50
static void sp_repr_write_stream_root_element(Node *repr, Writer &out,
51
bool add_whitespace, gchar const *default_ns,
52
int inlineattrs, int indent,
53
gchar const *old_href_abs_base,
54
gchar const *new_href_abs_base);
55
static void sp_repr_write_stream_element(Node *repr, Writer &out,
56
gint indent_level, bool add_whitespace,
57
Glib::QueryQuark elide_prefix,
58
List<AttributeRecord const> attributes,
59
int inlineattrs, int indent,
60
gchar const *old_href_abs_base,
61
gchar const *new_href_abs_base);
64
static xmlDocPtr sp_wmf_convert (const char * file_name);
65
static char * sp_wmf_image_name (void * context);
66
#endif /* HAVE_LIBWMF */
91
int setFile( char const * filename );
93
static int readCb( void * context, char * buffer, int len );
94
static int closeCb( void * context );
96
char const* getEncoding() const { return encoding; }
97
int read( char * buffer, int len );
100
const char* filename;
103
unsigned char firstFew[4];
106
Inkscape::IO::UriInputStream* instr;
107
Inkscape::IO::GzipInputStream* gzin;
110
int XmlSource::setFile(char const *filename)
114
this->filename = filename;
116
fp = Inkscape::IO::fopen_utf8name(filename, "r");
118
// First peek in the file to see what it is
119
memset( firstFew, 0, sizeof(firstFew) );
121
size_t some = fread( firstFew, 1, 4, fp );
123
// first check for compression
124
if ( (some >= 2) && (firstFew[0] == 0x1f) && (firstFew[1] == 0x8b) ) {
125
//g_message(" the file being read is gzip'd. extract it");
128
fp = Inkscape::IO::fopen_utf8name(filename, "r");
129
instr = new Inkscape::IO::UriInputStream(fp, dummy);
130
gzin = new Inkscape::IO::GzipInputStream(*instr);
132
memset( firstFew, 0, sizeof(firstFew) );
135
while ( some < 4 && single >= 0 )
137
single = gzin->get();
139
firstFew[some++] = 0x0ff & single;
147
if ( (some >= 2) &&(firstFew[0] == 0xfe) && (firstFew[1] == 0xff) ) {
148
encoding = g_strdup("UTF-16BE");
150
} else if ( (some >= 2) && (firstFew[0] == 0xff) && (firstFew[1] == 0xfe) ) {
151
encoding = g_strdup("UTF-16LE");
153
} else if ( (some >= 3) && (firstFew[0] == 0xef) && (firstFew[1] == 0xbb) && (firstFew[2] == 0xbf) ) {
154
encoding = g_strdup("UTF-8");
159
memmove( firstFew, firstFew + encSkip, (some - encSkip) );
164
retVal = 0; // no error
172
int XmlSource::readCb( void * context, char * buffer, int len )
176
XmlSource* self = static_cast<XmlSource*>(context);
177
retVal = self->read( buffer, len );
182
int XmlSource::closeCb(void * context)
185
XmlSource* self = static_cast<XmlSource*>(context);
191
int XmlSource::read( char *buffer, int len )
196
if ( firstFewLen > 0 ) {
197
int some = (len < firstFewLen) ? len : firstFewLen;
198
memcpy( buffer, firstFew, some );
199
if ( len < firstFewLen ) {
200
memmove( firstFew, firstFew + some, (firstFewLen - some) );
206
while ( (static_cast<int>(got) < len) && (single >= 0) )
208
single = gzin->get();
210
buffer[got++] = 0x0ff & single;
216
got = fread( buffer, 1, len, fp );
221
} else if ( ferror(fp) ) {
230
int XmlSource::close()
251
* Reads XML from a file, including WMF files, and returns the Document.
252
* The default namespace can also be specified, if desired.
255
sp_repr_read_file (const gchar * filename, const gchar *default_ns)
260
xmlSubstituteEntitiesDefault(1);
262
g_return_val_if_fail (filename != NULL, NULL);
263
g_return_val_if_fail (Inkscape::IO::file_test( filename, G_FILE_TEST_EXISTS ), NULL);
264
/* fixme: A file can disappear at any time, including between now and when we actually try to
265
* open it. Get rid of the above test once we're sure that we correctly handle
268
// TODO: bulia, please look over
270
gsize bytesWritten = 0;
271
GError* error = NULL;
272
// TODO: need to replace with our own fopen and reading
273
gchar* localFilename = g_filename_from_utf8 ( filename,
274
-1, &bytesRead, &bytesWritten, &error);
275
g_return_val_if_fail( localFilename != NULL, NULL );
277
Inkscape::IO::dump_fopen_call( filename, "N" );
280
if (strlen (localFilename) > 4) {
281
if ( (strcmp (localFilename + strlen (localFilename) - 4,".wmf") == 0)
282
|| (strcmp (localFilename + strlen (localFilename) - 4,".WMF") == 0)) {
283
doc = sp_wmf_convert (localFilename);
286
#endif // !HAVE_LIBWMF
291
if ( (src.setFile(filename) == 0) ) {
292
doc = xmlReadIO( XmlSource::readCb,
301
rdoc = sp_repr_do_read( doc, default_ns );
306
if ( localFilename ) {
307
g_free( localFilename );
314
* Reads and parses XML from a buffer, returning it as an Document
317
sp_repr_read_mem (const gchar * buffer, gint length, const gchar *default_ns)
322
xmlSubstituteEntitiesDefault(1);
324
g_return_val_if_fail (buffer != NULL, NULL);
326
doc = xmlParseMemory (const_cast<gchar *>(buffer), length);
328
rdoc = sp_repr_do_read (doc, default_ns);
336
* Reads and parses XML from a buffer, returning it as an Document
339
sp_repr_read_buf (const Glib::ustring &buf, const gchar *default_ns)
341
return sp_repr_read_mem(buf.c_str(), buf.size(), default_ns);
347
struct compare_quark_ids {
348
bool operator()(Glib::QueryQuark const &a, Glib::QueryQuark const &b) const {
349
return a.id() < b.id();
357
typedef std::map<Glib::QueryQuark, Glib::QueryQuark, Inkscape::compare_quark_ids> PrefixMap;
359
Glib::QueryQuark qname_prefix(Glib::QueryQuark qname) {
360
static PrefixMap prefix_map;
361
PrefixMap::iterator iter = prefix_map.find(qname);
362
if ( iter != prefix_map.end() ) {
363
return (*iter).second;
365
gchar const *name_string=g_quark_to_string(qname);
366
gchar const *prefix_end=strchr(name_string, ':');
368
Glib::Quark prefix=Glib::ustring(name_string, prefix_end);
369
prefix_map.insert(PrefixMap::value_type(qname, prefix));
381
void promote_to_namespace(Node *repr, const gchar *prefix) {
382
if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
383
GQuark code = repr->code();
384
if (!qname_prefix(code).id()) {
385
gchar *svg_name = g_strconcat(prefix, ":", g_quark_to_string(code), NULL);
386
repr->setCodeUnsafe(g_quark_from_string(svg_name));
389
for ( Node *child = sp_repr_children(repr) ; child ; child = sp_repr_next(child) ) {
390
promote_to_namespace(child, prefix);
398
* Reads in a XML file to create a Document
401
sp_repr_do_read (xmlDocPtr doc, const gchar *default_ns)
406
xmlNodePtr node=xmlDocGetRootElement (doc);
411
GHashTable * prefix_map;
412
prefix_map = g_hash_table_new (g_str_hash, g_str_equal);
414
Document *rdoc = new Inkscape::XML::SimpleDocument();
417
for ( node = doc->children ; node != NULL ; node = node->next ) {
418
if (node->type == XML_ELEMENT_NODE) {
419
Node *repr=sp_repr_svg_read_node(rdoc, node, default_ns, prefix_map);
420
rdoc->appendChild(repr);
421
Inkscape::GC::release(repr);
429
} else if ( node->type == XML_COMMENT_NODE || node->type == XML_PI_NODE ) {
430
Node *repr=sp_repr_svg_read_node(rdoc, node, default_ns, prefix_map);
431
rdoc->appendChild(repr);
432
Inkscape::GC::release(repr);
437
/* promote elements of some XML documents that don't use namespaces
438
* into their default namespace */
439
if ( default_ns && !strchr(root->name(), ':') ) {
440
if ( !strcmp(default_ns, SP_SVG_NS_URI) ) {
441
promote_to_namespace(root, "svg");
443
if ( !strcmp(default_ns, INKSCAPE_EXTENSION_URI) ) {
444
promote_to_namespace(root, INKSCAPE_EXTENSION_NS_NC);
449
g_hash_table_destroy (prefix_map);
455
sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar */*default_ns*/, GHashTable *prefix_map)
457
const xmlChar *prefix;
458
if ( ns && ns->href ) {
459
prefix = reinterpret_cast<const xmlChar*>( sp_xml_ns_uri_prefix(reinterpret_cast<const gchar*>(ns->href),
460
reinterpret_cast<const char*>(ns->prefix)) );
461
void* p0 = reinterpret_cast<gpointer>(const_cast<xmlChar *>(prefix));
462
void* p1 = reinterpret_cast<gpointer>(const_cast<xmlChar *>(ns->href));
463
g_hash_table_insert( prefix_map, p0, p1 );
469
return g_snprintf (p, len, "%s:%s", reinterpret_cast<const gchar*>(prefix), name);
471
return g_snprintf (p, len, "%s", name);
476
sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gchar *default_ns, GHashTable *prefix_map)
483
if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE) {
485
if (node->content == NULL || *(node->content) == '\0') {
486
return NULL; // empty text node
489
bool preserve = (xmlNodeGetSpacePreserve (node) == 1);
492
for (p = node->content; *p && g_ascii_isspace (*p) && !preserve; p++)
493
; // skip all whitespace
495
if (!(*p)) { // this is an all-whitespace node, and preserve == default
496
return NULL; // we do not preserve all-whitespace nodes unless we are asked to
499
return xml_doc->createTextNode(reinterpret_cast<gchar *>(node->content));
502
if (node->type == XML_COMMENT_NODE) {
503
return xml_doc->createComment(reinterpret_cast<gchar *>(node->content));
506
if (node->type == XML_PI_NODE) {
507
return xml_doc->createPI(reinterpret_cast<const gchar *>(node->name),
508
reinterpret_cast<const gchar *>(node->content));
511
if (node->type == XML_ENTITY_DECL) {
515
sp_repr_qualified_name (c, 256, node->ns, node->name, default_ns, prefix_map);
516
repr = xml_doc->createElement(c);
517
/* TODO remember node->ns->prefix if node->ns != NULL */
519
for (prop = node->properties; prop != NULL; prop = prop->next) {
520
if (prop->children) {
521
sp_repr_qualified_name (c, 256, prop->ns, prop->name, default_ns, prefix_map);
522
repr->setAttribute(c, reinterpret_cast<gchar*>(prop->children->content));
523
/* TODO remember prop->ns->prefix if prop->ns != NULL */
528
repr->setContent(reinterpret_cast<gchar*>(node->content));
531
child = node->xmlChildrenNode;
532
for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
533
crepr = sp_repr_svg_read_node (xml_doc, child, default_ns, prefix_map);
535
repr->appendChild(crepr);
536
Inkscape::GC::release(crepr);
545
sp_repr_save_writer(Document *doc, Inkscape::IO::Writer *out,
546
gchar const *default_ns,
547
gchar const *old_href_abs_base,
548
gchar const *new_href_abs_base)
550
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
551
bool inlineattrs = prefs->getBool("/options/svgoutput/inlineattrs");
552
int indent = prefs->getInt("/options/svgoutput/indent", 2);
554
/* fixme: do this The Right Way */
555
out->writeString( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" );
557
const gchar *str = static_cast<Node *>(doc)->attribute("doctype");
559
out->writeString( str );
562
for (Node *repr = sp_repr_document_first_child(doc);
563
repr; repr = sp_repr_next(repr))
565
Inkscape::XML::NodeType const node_type = repr->type();
566
if ( node_type == Inkscape::XML::ELEMENT_NODE ) {
567
sp_repr_write_stream_root_element(repr, *out, TRUE, default_ns, inlineattrs, indent,
568
old_href_abs_base, new_href_abs_base);
570
sp_repr_write_stream(repr, *out, 0, TRUE, GQuark(0), inlineattrs, indent,
571
old_href_abs_base, new_href_abs_base);
572
if ( node_type == Inkscape::XML::COMMENT_NODE ) {
573
out->writeChar('\n');
583
sp_repr_save_buf(Document *doc)
585
Inkscape::IO::StringOutputStream souts;
586
Inkscape::IO::OutputStreamWriter outs(souts);
588
sp_repr_save_writer(doc, &outs, SP_INKSCAPE_NS_URI, 0, 0);
591
Glib::ustring buf = souts.getString();
601
sp_repr_save_stream(Document *doc, FILE *fp, gchar const *default_ns, bool compress,
602
gchar const *const old_href_abs_base,
603
gchar const *const new_href_abs_base)
605
Inkscape::URI dummy("x");
606
Inkscape::IO::UriOutputStream bout(fp, dummy);
607
Inkscape::IO::GzipOutputStream *gout = compress ? new Inkscape::IO::GzipOutputStream(bout) : NULL;
608
Inkscape::IO::OutputStreamWriter *out = compress ? new Inkscape::IO::OutputStreamWriter( *gout ) : new Inkscape::IO::OutputStreamWriter( bout );
610
sp_repr_save_writer(doc, out, default_ns, old_href_abs_base, new_href_abs_base);
619
* Returns true iff file successfully saved.
621
* \param filename The actual file to do I/O to, which might be a temp file.
623
* \param for_filename The base URI [actually filename] to assume for purposes of rewriting
624
* xlink:href attributes.
627
sp_repr_save_rebased_file(Document *doc, gchar const *const filename, gchar const *default_ns,
628
gchar const *old_base, gchar const *for_filename)
636
size_t const filename_len = strlen(filename);
637
compress = ( filename_len > 5
638
&& strcasecmp(".svgz", filename + filename_len - 5) == 0 );
641
Inkscape::IO::dump_fopen_call( filename, "B" );
642
FILE *file = Inkscape::IO::fopen_utf8name(filename, "w");
647
gchar *old_href_abs_base = NULL;
648
gchar *new_href_abs_base = NULL;
650
old_href_abs_base = calc_abs_doc_base(old_base);
651
if (g_path_is_absolute(for_filename)) {
652
new_href_abs_base = g_path_get_dirname(for_filename);
654
gchar *const cwd = g_get_current_dir();
655
gchar *const for_abs_filename = g_build_filename(cwd, for_filename, NULL);
657
new_href_abs_base = g_path_get_dirname(for_abs_filename);
658
g_free(for_abs_filename);
661
/* effic: Once we're confident that we never need (or never want) to resort
662
* to using sodipodi:absref instead of the xlink:href value,
663
* then we should do `if streq() { free them and set both to NULL; }'. */
665
sp_repr_save_stream(doc, file, default_ns, compress, old_href_abs_base, new_href_abs_base);
667
g_free(old_href_abs_base);
668
g_free(new_href_abs_base);
670
if (fclose (file) != 0) {
678
* Returns true iff file successfully saved.
681
sp_repr_save_file(Document *doc, gchar const *const filename, gchar const *default_ns)
683
return sp_repr_save_rebased_file(doc, filename, default_ns, NULL, NULL);
687
/* (No doubt this function already exists elsewhere.) */
689
repr_quote_write (Writer &out, const gchar * val)
692
for (; *val != '\0'; val++) {
694
case '"': out.writeString( """ ); break;
695
case '&': out.writeString( "&" ); break;
696
case '<': out.writeString( "<" ); break;
697
case '>': out.writeString( ">" ); break;
698
default: out.writeChar( *val ); break;
704
static void repr_write_comment( Writer &out, const gchar * val, bool addWhitespace, gint indentLevel, int indent )
706
if ( indentLevel > 16 ) {
709
if (addWhitespace && indent) {
710
for (gint i = 0; i < indentLevel; i++) {
711
for (gint j = 0; j < indent; j++) {
712
out.writeString(" ");
717
out.writeString("<!--");
718
// WARNING out.printf() and out.writeString() are *NOT* non-ASCII friendly.
720
for (const gchar* cur = val; *cur; cur++ ) {
724
out.writeString(" ");
726
out.writeString("-->");
729
out.writeString("\n");
735
typedef std::map<Glib::QueryQuark, gchar const *, Inkscape::compare_quark_ids> LocalNameMap;
736
typedef std::map<Glib::QueryQuark, Inkscape::Util::ptr_shared<char>, Inkscape::compare_quark_ids> NSMap;
738
gchar const *qname_local_name(Glib::QueryQuark qname) {
739
static LocalNameMap local_name_map;
740
LocalNameMap::iterator iter = local_name_map.find(qname);
741
if ( iter != local_name_map.end() ) {
742
return (*iter).second;
744
gchar const *name_string=g_quark_to_string(qname);
745
gchar const *prefix_end=strchr(name_string, ':');
747
return prefix_end + 1;
754
void add_ns_map_entry(NSMap &ns_map, Glib::QueryQuark prefix) {
755
using Inkscape::Util::ptr_shared;
756
using Inkscape::Util::share_unsafe;
758
static const Glib::QueryQuark xml_prefix("xml");
760
NSMap::iterator iter=ns_map.find(prefix);
761
if ( iter == ns_map.end() ) {
763
gchar const *uri=sp_xml_ns_prefix_uri(g_quark_to_string(prefix));
765
ns_map.insert(NSMap::value_type(prefix, share_unsafe(uri)));
766
} else if ( prefix != xml_prefix ) {
767
g_warning("No namespace known for normalized prefix %s", g_quark_to_string(prefix));
770
ns_map.insert(NSMap::value_type(prefix, ptr_shared<char>()));
775
void populate_ns_map(NSMap &ns_map, Node &repr) {
776
if ( repr.type() == Inkscape::XML::ELEMENT_NODE ) {
777
add_ns_map_entry(ns_map, qname_prefix(repr.code()));
778
for ( List<AttributeRecord const> iter=repr.attributeList() ;
781
Glib::QueryQuark prefix=qname_prefix(iter->key);
783
add_ns_map_entry(ns_map, prefix);
786
for ( Node *child=sp_repr_children(&repr) ;
787
child ; child = sp_repr_next(child) )
789
populate_ns_map(ns_map, *child);
797
sp_repr_write_stream_root_element(Node *repr, Writer &out,
798
bool add_whitespace, gchar const *default_ns,
799
int inlineattrs, int indent,
800
gchar const *const old_href_base,
801
gchar const *const new_href_base)
803
using Inkscape::Util::ptr_shared;
805
g_assert(repr != NULL);
806
Glib::QueryQuark xml_prefix=g_quark_from_static_string("xml");
809
populate_ns_map(ns_map, *repr);
811
Glib::QueryQuark elide_prefix=GQuark(0);
812
if ( default_ns && ns_map.find(GQuark(0)) == ns_map.end() ) {
813
elide_prefix = g_quark_from_string(sp_xml_ns_uri_prefix(default_ns, NULL));
816
List<AttributeRecord const> attributes=repr->attributeList();
817
for ( NSMap::iterator iter=ns_map.begin() ; iter != ns_map.end() ; ++iter )
819
Glib::QueryQuark prefix=(*iter).first;
820
ptr_shared<char> ns_uri=(*iter).second;
823
if ( prefix != xml_prefix ) {
824
if ( elide_prefix == prefix ) {
825
attributes = cons(AttributeRecord(g_quark_from_static_string("xmlns"), ns_uri), attributes);
828
Glib::ustring attr_name="xmlns:";
829
attr_name.append(g_quark_to_string(prefix));
830
GQuark key = g_quark_from_string(attr_name.c_str());
831
attributes = cons(AttributeRecord(key, ns_uri), attributes);
834
// if there are non-namespaced elements, we can't globally
835
// use a default namespace
836
elide_prefix = GQuark(0);
840
return sp_repr_write_stream_element(repr, out, 0, add_whitespace, elide_prefix, attributes,
841
inlineattrs, indent, old_href_base, new_href_base);
844
void sp_repr_write_stream( Node *repr, Writer &out, gint indent_level,
845
bool add_whitespace, Glib::QueryQuark elide_prefix,
846
int inlineattrs, int indent,
847
gchar const *const old_href_base,
848
gchar const *const new_href_base)
850
switch (repr->type()) {
851
case Inkscape::XML::TEXT_NODE: {
852
repr_quote_write( out, repr->content() );
855
case Inkscape::XML::COMMENT_NODE: {
856
repr_write_comment( out, repr->content(), add_whitespace, indent_level, indent );
859
case Inkscape::XML::PI_NODE: {
860
out.printf( "<?%s %s?>", repr->name(), repr->content() );
863
case Inkscape::XML::ELEMENT_NODE: {
864
sp_repr_write_stream_element( repr, out, indent_level,
865
add_whitespace, elide_prefix,
866
repr->attributeList(),
868
old_href_base, new_href_base);
871
case Inkscape::XML::DOCUMENT_NODE: {
872
g_assert_not_reached();
876
g_assert_not_reached();
883
sp_repr_write_stream_element (Node * repr, Writer & out, gint indent_level,
885
Glib::QueryQuark elide_prefix,
886
List<AttributeRecord const> attributes,
887
int inlineattrs, int indent,
888
gchar const *const old_href_base,
889
gchar const *const new_href_base)
894
g_return_if_fail (repr != NULL);
896
if ( indent_level > 16 ) {
900
if (add_whitespace && indent) {
901
for (gint i = 0; i < indent_level; i++) {
902
for (gint j = 0; j < indent; j++) {
903
out.writeString(" ");
908
GQuark code = repr->code();
909
gchar const *element_name;
910
if ( elide_prefix == qname_prefix(code) ) {
911
element_name = qname_local_name(code);
913
element_name = g_quark_to_string(code);
915
out.printf( "<%s", element_name );
917
// if this is a <text> element, suppress formatting whitespace
918
// for its content and children:
919
gchar const *xml_space_attr = repr->attribute("xml:space");
920
if (xml_space_attr != NULL && !strcmp(xml_space_attr, "preserve")) {
921
add_whitespace = false;
924
for ( List<AttributeRecord const> iter = rebase_href_attrs(old_href_base, new_href_base,
929
out.writeString("\n");
931
for ( gint i = 0 ; i < indent_level + 1 ; i++ ) {
932
for ( gint j = 0 ; j < indent ; j++ ) {
933
out.writeString(" ");
938
out.printf(" %s=\"", g_quark_to_string(iter->key));
939
repr_quote_write(out, iter->value);
944
for (child = repr->firstChild() ; child != NULL; child = child->next()) {
945
if (child->type() == Inkscape::XML::TEXT_NODE) {
950
if (repr->firstChild()) {
951
out.writeString( ">" );
952
if (loose && add_whitespace) {
953
out.writeString( "\n" );
955
for (child = repr->firstChild(); child != NULL; child = child->next()) {
956
sp_repr_write_stream(child, out, ( loose ? indent_level + 1 : 0 ),
957
add_whitespace, elide_prefix, inlineattrs, indent,
958
old_href_base, new_href_base);
961
if (loose && add_whitespace && indent) {
962
for (gint i = 0; i < indent_level; i++) {
963
for ( gint j = 0 ; j < indent ; j++ ) {
964
out.writeString(" ");
968
out.printf( "</%s>", element_name );
970
out.writeString( " />" );
973
// text elements cannot nest, so we can output newline
974
// after closing text
976
if (add_whitespace || !strcmp (repr->name(), "svg:text")) {
977
out.writeString( "\n" );
985
c-file-style:"stroustrup"
986
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
991
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :