diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp
index 62d43dd41aa77f7a5d501bece2a1963dca31a278..5f7654ba8e7966467077f4d3735582381c65f733 100644 (file)
--- a/src/xml/repr-io.cpp
+++ b/src/xml/repr-io.cpp
-#define __SP_REPR_IO_C__
-
/*
* Dirty DOM-like tree
*
# include <config.h>
#endif
+#include <cstring>
+#include <string>
#include <stdexcept>
+#include <libxml/parser.h>
+
#include "xml/repr.h"
#include "xml/attribute-record.h"
+#include "xml/rebase-hrefs.h"
#include "xml/simple-document.h"
#include "io/sys.h"
#include "io/uristream.h"
+#include "io/stringstream.h"
#include "io/gzipstream.h"
-#include "prefs-utils.h"
+#include "extension/extension.h"
+
+#include "preferences.h"
using Inkscape::IO::Writer;
using Inkscape::Util::List;
using Inkscape::XML::SimpleDocument;
using Inkscape::XML::Node;
using Inkscape::XML::AttributeRecord;
+using Inkscape::XML::calc_abs_doc_base;
+using Inkscape::XML::rebase_href_attrs;
Document *sp_repr_do_read (xmlDocPtr doc, const gchar *default_ns);
static Node *sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gchar *default_ns, GHashTable *prefix_map);
static gint sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar *default_ns, GHashTable *prefix_map);
-static void sp_repr_write_stream_root_element (Node *repr, Writer &out, bool add_whitespace, gchar const *default_ns, int inlineattrs, int indent);
-static void sp_repr_write_stream (Node *repr, Writer &out, gint indent_level, bool add_whitespace, Glib::QueryQuark elide_prefix, int inlineattrs, int indent);
-static void sp_repr_write_stream_element (Node *repr, Writer &out, gint indent_level, bool add_whitespace, Glib::QueryQuark elide_prefix, List<AttributeRecord const> attributes, int inlineattrs, int indent);
+static void sp_repr_write_stream_root_element(Node *repr, Writer &out,
+ bool add_whitespace, gchar const *default_ns,
+ int inlineattrs, int indent,
+ gchar const *old_href_abs_base,
+ gchar const *new_href_abs_base);
+static void sp_repr_write_stream_element(Node *repr, Writer &out,
+ gint indent_level, bool add_whitespace,
+ Glib::QueryQuark elide_prefix,
+ List<AttributeRecord const> attributes,
+ int inlineattrs, int indent,
+ gchar const *old_href_abs_base,
+ gchar const *new_href_abs_base);
#ifdef HAVE_LIBWMF
static xmlDocPtr sp_wmf_convert (const char * file_name);
got = some;
} else if ( gzin ) {
int single = 0;
- while ( (int)got < len && single >= 0 )
+ while ( (static_cast<int>(got) < len) && (single >= 0) )
{
single = gzin->get();
if ( single >= 0 ) {
&src,
localFilename,
src.getEncoding(),
- XML_PARSE_NOENT );
+ XML_PARSE_NOENT | XML_PARSE_HUGE);
}
}
g_return_val_if_fail (buffer != NULL, NULL);
- doc = xmlParseMemory ((gchar *) buffer, length);
+ doc = xmlParseMemory (const_cast<gchar *>(buffer), length);
rdoc = sp_repr_do_read (doc, default_ns);
- if (doc)
+ if (doc) {
xmlFreeDoc (doc);
+ }
return rdoc;
}
+/**
+ * Reads and parses XML from a buffer, returning it as an Document
+ */
+Document *
+sp_repr_read_buf (const Glib::ustring &buf, const gchar *default_ns)
+{
+ return sp_repr_read_mem(buf.c_str(), buf.size(), default_ns);
+}
+
+
namespace Inkscape {
struct compare_quark_ids {
namespace {
-void promote_to_svg_namespace(Node *repr) {
+void promote_to_namespace(Node *repr, const gchar *prefix) {
if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
GQuark code = repr->code();
if (!qname_prefix(code).id()) {
- gchar *svg_name = g_strconcat("svg:", g_quark_to_string(code), NULL);
+ gchar *svg_name = g_strconcat(prefix, ":", g_quark_to_string(code), NULL);
repr->setCodeUnsafe(g_quark_from_string(svg_name));
g_free(svg_name);
}
for ( Node *child = sp_repr_children(repr) ; child ; child = sp_repr_next(child) ) {
- promote_to_svg_namespace(child);
+ promote_to_namespace(child, prefix);
}
}
}
Document *
sp_repr_do_read (xmlDocPtr doc, const gchar *default_ns)
{
- if (doc == NULL) return NULL;
+ if (doc == NULL) {
+ return NULL;
+ }
xmlNodePtr node=xmlDocGetRootElement (doc);
- if (node == NULL) return NULL;
+ if (node == NULL) {
+ return NULL;
+ }
GHashTable * prefix_map;
prefix_map = g_hash_table_new (g_str_hash, g_str_equal);
root = NULL;
break;
}
- } else if ( node->type == XML_COMMENT_NODE ) {
- Node *comment=sp_repr_svg_read_node(rdoc, node, default_ns, prefix_map);
- rdoc->appendChild(comment);
- Inkscape::GC::release(comment);
+ } else if ( node->type == XML_COMMENT_NODE || node->type == XML_PI_NODE ) {
+ Node *repr=sp_repr_svg_read_node(rdoc, node, default_ns, prefix_map);
+ rdoc->appendChild(repr);
+ Inkscape::GC::release(repr);
}
}
if (root != NULL) {
- /* promote elements of SVG documents that don't use namespaces
- * into the SVG namespace */
- if ( default_ns && !strcmp(default_ns, SP_SVG_NS_URI)
- && !strcmp(root->name(), "svg") )
- {
- promote_to_svg_namespace(root);
+ /* promote elements of some XML documents that don't use namespaces
+ * into their default namespace */
+ if ( default_ns && !strchr(root->name(), ':') ) {
+ if ( !strcmp(default_ns, SP_SVG_NS_URI) ) {
+ promote_to_namespace(root, "svg");
+ }
+ if ( !strcmp(default_ns, INKSCAPE_EXTENSION_URI) ) {
+ promote_to_namespace(root, INKSCAPE_EXTENSION_NS_NC);
+ }
}
}
}
gint
-sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar *default_ns, GHashTable *prefix_map)
+sp_repr_qualified_name (gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar */*default_ns*/, GHashTable *prefix_map)
{
const xmlChar *prefix;
if ( ns && ns->href ) {
- prefix = (xmlChar*)sp_xml_ns_uri_prefix ((gchar*)ns->href, (char*)ns->prefix);
- g_hash_table_insert (prefix_map, (gpointer)prefix, (gpointer)ns->href);
+ prefix = reinterpret_cast<const xmlChar*>( sp_xml_ns_uri_prefix(reinterpret_cast<const gchar*>(ns->href),
+ reinterpret_cast<const char*>(ns->prefix)) );
+ void* p0 = reinterpret_cast<gpointer>(const_cast<xmlChar *>(prefix));
+ void* p1 = reinterpret_cast<gpointer>(const_cast<xmlChar *>(ns->href));
+ g_hash_table_insert( prefix_map, p0, p1 );
} else {
prefix = NULL;
}
- if (prefix)
- return g_snprintf (p, len, "%s:%s", (gchar*)prefix, name);
- else
+ if (prefix) {
+ return g_snprintf (p, len, "%s:%s", reinterpret_cast<const gchar*>(prefix), name);
+ } else {
return g_snprintf (p, len, "%s", name);
+ }
}
static Node *
@@ -443,8 +482,9 @@ sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gchar *default_
if (node->type == XML_TEXT_NODE || node->type == XML_CDATA_SECTION_NODE) {
- if (node->content == NULL || *(node->content) == '\0')
+ if (node->content == NULL || *(node->content) == '\0') {
return NULL; // empty text node
+ }
bool preserve = (xmlNodeGetSpacePreserve (node) == 1);
@@ -456,13 +496,21 @@ sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gchar *default_
return NULL; // we do not preserve all-whitespace nodes unless we are asked to
}
- return xml_doc->createTextNode((const gchar *)node->content);
+ return xml_doc->createTextNode(reinterpret_cast<gchar *>(node->content));
}
- if (node->type == XML_COMMENT_NODE)
- return xml_doc->createComment((const gchar *)node->content);
+ if (node->type == XML_COMMENT_NODE) {
+ return xml_doc->createComment(reinterpret_cast<gchar *>(node->content));
+ }
+
+ if (node->type == XML_PI_NODE) {
+ return xml_doc->createPI(reinterpret_cast<const gchar *>(node->name),
+ reinterpret_cast<const gchar *>(node->content));
+ }
- if (node->type == XML_ENTITY_DECL) return NULL;
+ if (node->type == XML_ENTITY_DECL) {
+ return NULL;
+ }
sp_repr_qualified_name (c, 256, node->ns, node->name, default_ns, prefix_map);
repr = xml_doc->createElement(c);
@@ -471,13 +519,14 @@ sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gchar *default_
for (prop = node->properties; prop != NULL; prop = prop->next) {
if (prop->children) {
sp_repr_qualified_name (c, 256, prop->ns, prop->name, default_ns, prefix_map);
- repr->setAttribute(c, (gchar*)prop->children->content);
+ repr->setAttribute(c, reinterpret_cast<gchar*>(prop->children->content));
/* TODO remember prop->ns->prefix if prop->ns != NULL */
}
}
- if (node->content)
- repr->setContent((gchar*)node->content);
+ if (node->content) {
+ repr->setContent(reinterpret_cast<gchar*>(node->content));
+ }
child = node->xmlChildrenNode;
for (child = node->xmlChildrenNode; child != NULL; child = child->next) {
@@ -491,114 +540,193 @@ sp_repr_svg_read_node (Document *xml_doc, xmlNodePtr node, const gchar *default_
return repr;
}
-void
-sp_repr_save_stream (Document *doc, FILE *fp, gchar const *default_ns, bool compress)
-{
- Node *repr;
- const gchar *str;
- Inkscape::URI dummy("x");
- Inkscape::IO::UriOutputStream bout(fp, dummy);
- Inkscape::IO::GzipOutputStream *gout = compress ? new Inkscape::IO::GzipOutputStream(bout) : NULL;
- Inkscape::IO::OutputStreamWriter *out = compress ? new Inkscape::IO::OutputStreamWriter( *gout ) : new Inkscape::IO::OutputStreamWriter( bout );
-
- int inlineattrs = prefs_get_int_attribute("options.svgoutput", "inlineattrs", 0);
- int indent = prefs_get_int_attribute("options.svgoutput", "indent", 2);
+static void
+sp_repr_save_writer(Document *doc, Inkscape::IO::Writer *out,
+ gchar const *default_ns,
+ gchar const *old_href_abs_base,
+ gchar const *new_href_abs_base)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool inlineattrs = prefs->getBool("/options/svgoutput/inlineattrs");
+ int indent = prefs->getInt("/options/svgoutput/indent", 2);
/* fixme: do this The Right Way */
out->writeString( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" );
- str = ((Node *)doc)->attribute("doctype");
+ const gchar *str = static_cast<Node *>(doc)->attribute("doctype");
if (str) {
out->writeString( str );
}
- repr = sp_repr_document_first_child(doc);
- for ( repr = sp_repr_document_first_child(doc) ;
- repr ; repr = sp_repr_next(repr) )
+ for (Node *repr = sp_repr_document_first_child(doc);
+ repr; repr = sp_repr_next(repr))
{
- if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
- sp_repr_write_stream_root_element(repr, *out, TRUE, default_ns, inlineattrs, indent);
- } else if ( repr->type() == Inkscape::XML::COMMENT_NODE ) {
- sp_repr_write_stream(repr, *out, 0, TRUE, GQuark(0), inlineattrs, indent);
- out->writeChar( '\n' );
+ Inkscape::XML::NodeType const node_type = repr->type();
+ if ( node_type == Inkscape::XML::ELEMENT_NODE ) {
+ sp_repr_write_stream_root_element(repr, *out, TRUE, default_ns, inlineattrs, indent,
+ old_href_abs_base, new_href_abs_base);
} else {
- sp_repr_write_stream(repr, *out, 0, TRUE, GQuark(0), inlineattrs, indent);
+ sp_repr_write_stream(repr, *out, 0, TRUE, GQuark(0), inlineattrs, indent,
+ old_href_abs_base, new_href_abs_base);
+ if ( node_type == Inkscape::XML::COMMENT_NODE ) {
+ out->writeChar('\n');
+ }
}
}
- if ( out ) {
- delete out;
- out = NULL;
- }
- if ( gout ) {
- delete gout;
- gout = NULL;
- }
}
-/* Returns TRUE if file successfully saved; FALSE if not
+
+
+
+Glib::ustring
+sp_repr_save_buf(Document *doc)
+{
+ Inkscape::IO::StringOutputStream souts;
+ Inkscape::IO::OutputStreamWriter outs(souts);
+
+ sp_repr_save_writer(doc, &outs, SP_INKSCAPE_NS_URI, 0, 0);
+
+ outs.close();
+ Glib::ustring buf = souts.getString();
+
+ return buf;
+}
+
+
+
+
+
+void
+sp_repr_save_stream(Document *doc, FILE *fp, gchar const *default_ns, bool compress,
+ gchar const *const old_href_abs_base,
+ gchar const *const new_href_abs_base)
+{
+ Inkscape::URI dummy("x");
+ Inkscape::IO::UriOutputStream bout(fp, dummy);
+ Inkscape::IO::GzipOutputStream *gout = compress ? new Inkscape::IO::GzipOutputStream(bout) : NULL;
+ Inkscape::IO::OutputStreamWriter *out = compress ? new Inkscape::IO::OutputStreamWriter( *gout ) : new Inkscape::IO::OutputStreamWriter( bout );
+
+ sp_repr_save_writer(doc, out, default_ns, old_href_abs_base, new_href_abs_base);
+
+ delete out;
+ delete gout;
+}
+
+
+
+/**
+ * Returns true iff file successfully saved.
+ *
+ * \param filename The actual file to do I/O to, which might be a temp file.
+ *
+ * \param for_filename The base URI [actually filename] to assume for purposes of rewriting
+ * xlink:href attributes.
*/
bool
-sp_repr_save_file (Document *doc, const gchar *filename,
- gchar const *default_ns)
+sp_repr_save_rebased_file(Document *doc, gchar const *const filename, gchar const *default_ns,
+ gchar const *old_base, gchar const *for_filename)
{
- if (filename == NULL) {
- return FALSE;
+ if (!filename) {
+ return false;
}
- bool compress = false;
+
+ bool compress;
{
- if (strlen (filename) > 5) {
- gchar tmp[] = {0,0,0,0,0,0};
- strncpy( tmp, filename + strlen (filename) - 5, 6 );
- tmp[5] = 0;
- if ( strcasecmp(".svgz", tmp ) == 0 )
- {
- //g_message("TIME TO COMPRESS THE OUTPUT FOR SVGZ");
- compress = true;
- }
- }
+ size_t const filename_len = strlen(filename);
+ compress = ( filename_len > 5
+ && strcasecmp(".svgz", filename + filename_len - 5) == 0 );
}
Inkscape::IO::dump_fopen_call( filename, "B" );
FILE *file = Inkscape::IO::fopen_utf8name(filename, "w");
if (file == NULL) {
- return FALSE;
+ return false;
}
- sp_repr_save_stream (doc, file, default_ns, compress);
+ gchar *old_href_abs_base = NULL;
+ gchar *new_href_abs_base = NULL;
+ if (for_filename) {
+ old_href_abs_base = calc_abs_doc_base(old_base);
+ if (g_path_is_absolute(for_filename)) {
+ new_href_abs_base = g_path_get_dirname(for_filename);
+ } else {
+ gchar *const cwd = g_get_current_dir();
+ gchar *const for_abs_filename = g_build_filename(cwd, for_filename, NULL);
+ g_free(cwd);
+ new_href_abs_base = g_path_get_dirname(for_abs_filename);
+ g_free(for_abs_filename);
+ }
+
+ /* effic: Once we're confident that we never need (or never want) to resort
+ * to using sodipodi:absref instead of the xlink:href value,
+ * then we should do `if streq() { free them and set both to NULL; }'. */
+ }
+ sp_repr_save_stream(doc, file, default_ns, compress, old_href_abs_base, new_href_abs_base);
+
+ g_free(old_href_abs_base);
+ g_free(new_href_abs_base);
if (fclose (file) != 0) {
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
-void
-sp_repr_print (Node * repr)
+/**
+ * Returns true iff file successfully saved.
+ */
+bool
+sp_repr_save_file(Document *doc, gchar const *const filename, gchar const *default_ns)
{
- Inkscape::IO::StdOutputStream bout;
- Inkscape::IO::OutputStreamWriter out(bout);
-
- sp_repr_write_stream (repr, out, 0, TRUE, GQuark(0), 0, 2);
-
- return;
+ return sp_repr_save_rebased_file(doc, filename, default_ns, NULL, NULL);
}
+
/* (No doubt this function already exists elsewhere.) */
static void
repr_quote_write (Writer &out, const gchar * val)
{
- if (!val) return;
+ if (val) {
+ for (; *val != '\0'; val++) {
+ switch (*val) {
+ case '"': out.writeString( """ ); break;
+ case '&': out.writeString( "&" ); break;
+ case '<': out.writeString( "<" ); break;
+ case '>': out.writeString( ">" ); break;
+ default: out.writeChar( *val ); break;
+ }
+ }
+ }
+}
+
+static void repr_write_comment( Writer &out, const gchar * val, bool addWhitespace, gint indentLevel, int indent )
+{
+ if ( indentLevel > 16 ) {
+ indentLevel = 16;
+ }
+ if (addWhitespace && indent) {
+ for (gint i = 0; i < indentLevel; i++) {
+ for (gint j = 0; j < indent; j++) {
+ out.writeString(" ");
+ }
+ }
+ }
- for (; *val != '\0'; val++) {
- switch (*val) {
- case '"': out.writeString( """ ); break;
- case '&': out.writeString( "&" ); break;
- case '<': out.writeString( "<" ); break;
- case '>': out.writeString( ">" ); break;
- default: out.writeChar( *val ); break;
+ out.writeString("<!--");
+ // WARNING out.printf() and out.writeString() are *NOT* non-ASCII friendly.
+ if (val) {
+ for (const gchar* cur = val; *cur; cur++ ) {
+ out.writeChar(*cur);
}
+ } else {
+ out.writeString(" ");
+ }
+ out.writeString("-->");
+
+ if (addWhitespace) {
+ out.writeString("\n");
}
}
}
-void
-sp_repr_write_stream_root_element (Node *repr, Writer &out, bool add_whitespace, gchar const *default_ns,
- int inlineattrs, int indent)
+static void
+sp_repr_write_stream_root_element(Node *repr, Writer &out,
+ bool add_whitespace, gchar const *default_ns,
+ int inlineattrs, int indent,
+ gchar const *const old_href_base,
+ gchar const *const new_href_base)
{
using Inkscape::Util::ptr_shared;
+
g_assert(repr != NULL);
Glib::QueryQuark xml_prefix=g_quark_from_static_string("xml");
@@ -705,38 +837,65 @@ sp_repr_write_stream_root_element (Node *repr, Writer &out, bool add_whitespace,
}
}
- return sp_repr_write_stream_element(repr, out, 0, add_whitespace, elide_prefix, attributes, inlineattrs, indent);
+ return sp_repr_write_stream_element(repr, out, 0, add_whitespace, elide_prefix, attributes,
+ inlineattrs, indent, old_href_base, new_href_base);
}
-void
-sp_repr_write_stream (Node *repr, Writer &out, gint indent_level,
- bool add_whitespace, Glib::QueryQuark elide_prefix, int inlineattrs, int indent)
+void sp_repr_write_stream( Node *repr, Writer &out, gint indent_level,
+ bool add_whitespace, Glib::QueryQuark elide_prefix,
+ int inlineattrs, int indent,
+ gchar const *const old_href_base,
+ gchar const *const new_href_base)
{
- if (repr->type() == Inkscape::XML::TEXT_NODE) {
- repr_quote_write (out, repr->content());
- } else if (repr->type() == Inkscape::XML::COMMENT_NODE) {
- out.printf( "<!--%s-->", repr->content() );
- } else if (repr->type() == Inkscape::XML::ELEMENT_NODE) {
- sp_repr_write_stream_element(repr, out, indent_level, add_whitespace, elide_prefix, repr->attributeList(), inlineattrs, indent);
- } else {
- g_assert_not_reached();
+ switch (repr->type()) {
+ case Inkscape::XML::TEXT_NODE: {
+ repr_quote_write( out, repr->content() );
+ break;
+ }
+ case Inkscape::XML::COMMENT_NODE: {
+ repr_write_comment( out, repr->content(), add_whitespace, indent_level, indent );
+ break;
+ }
+ case Inkscape::XML::PI_NODE: {
+ out.printf( "<?%s %s?>", repr->name(), repr->content() );
+ break;
+ }
+ case Inkscape::XML::ELEMENT_NODE: {
+ sp_repr_write_stream_element( repr, out, indent_level,
+ add_whitespace, elide_prefix,
+ repr->attributeList(),
+ inlineattrs, indent,
+ old_href_base, new_href_base);
+ break;
+ }
+ case Inkscape::XML::DOCUMENT_NODE: {
+ g_assert_not_reached();
+ break;
+ }
+ default: {
+ g_assert_not_reached();
+ }
}
}
-void
+
+static void
sp_repr_write_stream_element (Node * repr, Writer & out, gint indent_level,
bool add_whitespace,
Glib::QueryQuark elide_prefix,
List<AttributeRecord const> attributes,
- int inlineattrs, int indent)
+ int inlineattrs, int indent,
+ gchar const *const old_href_base,
+ gchar const *const new_href_base)
{
Node *child;
bool loose;
g_return_if_fail (repr != NULL);
- if ( indent_level > 16 )
+ if ( indent_level > 16 ) {
indent_level = 16;
+ }
if (add_whitespace && indent) {
for (gint i = 0; i < indent_level; i++) {
// for its content and children:
gchar const *xml_space_attr = repr->attribute("xml:space");
if (xml_space_attr != NULL && !strcmp(xml_space_attr, "preserve")) {
- add_whitespace = FALSE;
+ add_whitespace = false;
}
- for ( List<AttributeRecord const> iter = attributes ;
+ for ( List<AttributeRecord const> iter = rebase_href_attrs(old_href_base, new_href_base,
+ attributes);
iter ; ++iter )
{
if (!inlineattrs) {
out.writeString( "\n" );
}
for (child = repr->firstChild(); child != NULL; child = child->next()) {
- sp_repr_write_stream (child, out, (loose) ? (indent_level + 1) : 0, add_whitespace, elide_prefix, inlineattrs, indent);
+ sp_repr_write_stream(child, out, ( loose ? indent_level + 1 : 0 ),
+ add_whitespace, elide_prefix, inlineattrs, indent,
+ old_href_base, new_href_base);
}
if (loose && add_whitespace && indent) {
fill-column:99
End:
*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :