18#include <libxml/parser.h>
19#include <libxml/xinclude.h>
40#include <glibmm/miscutils.h>
52static gint
sp_repr_qualified_name (gchar *p, gint
len, xmlNsPtr ns,
const xmlChar *
name,
const gchar *default_ns, std::map<std::string, std::string> &prefix_map);
54 bool add_whitespace, gchar
const *default_ns,
55 int inlineattrs,
int indent,
56 gchar
const *old_href_abs_base,
57 gchar
const *new_href_abs_base);
61 Glib::QueryQuark elide_prefix,
62 const AttributeVector & attributes,
63 int inlineattrs,
int indent,
64 gchar
const *old_href_abs_base,
65 gchar
const *new_href_abs_base);
79 for (
unsigned char & k : firstFew)
93 int setFile(
char const * filename );
97 static int readCb(
void * context,
char * buffer,
int len );
98 static int closeCb(
void * context );
100 char const* getEncoding()
const {
return encoding; }
101 int read(
char * buffer,
int len );
104 const char* filename;
107 unsigned char firstFew[4];
113int XmlSource::setFile(
char const *filename)
117 this->filename = filename;
122 memset( firstFew, 0,
sizeof(firstFew) );
124 size_t some = fread( firstFew, 1, 4, fp );
127 if ( (some >= 2) && (firstFew[0] == 0x1f) && (firstFew[1] == 0x8b) ) {
135 memset( firstFew, 0,
sizeof(firstFew) );
138 while ( some < 4 && single >= 0 )
140 single = gzin->get();
142 firstFew[some++] = 0x0ff & single;
150 if ( (some >= 2) &&(firstFew[0] == 0xfe) && (firstFew[1] == 0xff) ) {
151 encoding = g_strdup(
"UTF-16BE");
153 }
else if ( (some >= 2) && (firstFew[0] == 0xff) && (firstFew[1] == 0xfe) ) {
154 encoding = g_strdup(
"UTF-16LE");
156 }
else if ( (some >= 3) && (firstFew[0] == 0xef) && (firstFew[1] == 0xbb) && (firstFew[2] == 0xbf) ) {
157 encoding = g_strdup(
"UTF-8");
162 memmove( firstFew, firstFew + encSkip, (some - encSkip) );
173xmlDocPtr XmlSource::readXml()
175 int parse_options = XML_PARSE_HUGE | XML_PARSE_RECOVER;
178 bool allowNetAccess = prefs->
getBool(
"/options/externalresources/xml/allow_net_access",
false);
179 if (!allowNetAccess) parse_options |= XML_PARSE_NONET;
181 return xmlReadIO(readCb, closeCb,
this, filename, getEncoding(), parse_options);
184int XmlSource::readCb(
void * context,
char * buffer,
int len )
189 XmlSource* self =
static_cast<XmlSource*
>(context);
190 retVal = self->read( buffer,
len );
195int XmlSource::closeCb(
void * context)
198 XmlSource* self =
static_cast<XmlSource*
>(context);
204int XmlSource::read(
char *buffer,
int len )
209 if ( firstFewLen > 0 ) {
210 int some = (
len < firstFewLen) ?
len : firstFewLen;
211 memcpy( buffer, firstFew, some );
212 if (
len < firstFewLen ) {
213 memmove( firstFew, firstFew + some, (firstFewLen - some) );
219 while ( (
static_cast<int>(got) <
len) && (single >= 0) )
221 single = gzin->get();
223 buffer[got++] = 0x0ff & single;
229 got = fread( buffer, 1,
len, fp );
234 }
else if ( ferror(fp) ) {
243int XmlSource::close()
276 xmlDocPtr doc =
nullptr;
279 xmlSubstituteEntitiesDefault(1);
281 g_return_val_if_fail(filename !=
nullptr, NULL);
283 g_warning(
"Can't open file: %s (doesn't exist)", filename);
292 gsize bytesWritten = 0;
293 GError* error =
nullptr;
295 gchar* localFilename = g_filename_from_utf8(filename, -1, &bytesRead, &bytesWritten, &error);
296 g_return_val_if_fail(localFilename !=
nullptr, NULL);
302 if (src.setFile(filename) == 0) {
304 if (xinclude && doc && doc->properties && xmlXIncludeProcessFlags(doc, XML_PARSE_NOXINCNODE) < 0) {
305 g_warning(
"XInclude processing failed for %s", filename);
315 g_free(localFilename);
329 xmlSubstituteEntitiesDefault(1);
331 g_return_val_if_fail (buffer !=
nullptr, NULL);
333 int parser_options = XML_PARSE_HUGE | XML_PARSE_RECOVER;
334 parser_options |= XML_PARSE_NONET;
338 doc = xmlReadMemory (
const_cast<gchar *
>(buffer), length,
nullptr,
nullptr, parser_options);
358struct compare_quark_ids {
359 bool operator()(Glib::QueryQuark
const &a, Glib::QueryQuark
const &b)
const {
360 return a.id() < b.id();
368typedef std::map<Glib::QueryQuark, Glib::QueryQuark, Inkscape::compare_quark_ids> PrefixMap;
370Glib::QueryQuark qname_prefix(Glib::QueryQuark qname) {
371 static PrefixMap prefix_map;
372 PrefixMap::iterator iter = prefix_map.find(qname);
373 if ( iter != prefix_map.end() ) {
374 return (*iter).second;
376 gchar
const *name_string=g_quark_to_string(qname);
377 gchar
const *prefix_end=strchr(name_string,
':');
379 Glib::Quark prefix=Glib::ustring(name_string, prefix_end);
380 prefix_map.insert(PrefixMap::value_type(qname, prefix));
392void promote_to_namespace(
Node *repr,
const gchar *prefix) {
395 if (!qname_prefix(code).
id()) {
396 gchar *svg_name = g_strconcat(prefix,
":", g_quark_to_string(code),
nullptr);
401 promote_to_namespace(
child, prefix);
413void repair_namespace(
Node *repr,
const gchar *prefix) {
415 gchar *svg_name =
nullptr;
416 if (strncmp(repr->
name(),
"ns:", 3) == 0) {
417 svg_name = g_strconcat(prefix,
":", repr->
name() + 3,
nullptr);
418 }
else if (strncmp(repr->
name(),
"svg0:", 5) == 0) {
419 svg_name = g_strconcat(prefix,
":", repr->
name() + 5,
nullptr);
426 repair_namespace(
child, prefix);
438 if (doc ==
nullptr) {
441 xmlNodePtr
node=xmlDocGetRootElement (doc);
442 if (
node ==
nullptr) {
446 std::map<std::string, std::string> prefix_map;
452 if (
node->
type == XML_ELEMENT_NODE) {
470 if (
root !=
nullptr) {
473 if (!strcmp(
root->name(),
"ns:svg") || !strcmp(
root->name(),
"svg0:svg")) {
474 g_warning(
"Detected broken namespace \"%s\" in the SVG file, attempting to work around it",
root->name());
475 repair_namespace(
root,
"svg");
476 }
else if ( default_ns && !strchr(
root->name(),
':') ) {
477 if ( !strcmp(default_ns, SP_SVG_NS_URI) ) {
478 promote_to_namespace(
root,
"svg");
480 if ( !strcmp(default_ns, INKSCAPE_EXTENSION_URI) ) {
481 promote_to_namespace(
root, INKSCAPE_EXTENSION_NS_NC);
489 if ( !strcmp(
root->name(),
"svg:svg" ) ) {
491 bool clean = prefs->
getBool(
"/options/svgoutput/check_on_reading");
503 const xmlChar *prefix;
506 prefix =
reinterpret_cast<const xmlChar*
>(
sp_xml_ns_uri_prefix(
reinterpret_cast<const gchar*
>(ns->href),
507 reinterpret_cast<const char*
>(ns->prefix)) );
508 prefix_map[
reinterpret_cast<const char*
>(prefix)] =
reinterpret_cast<const char*
>(ns->href);
519 return g_snprintf (p,
len,
"%s:%s",
reinterpret_cast<const gchar*
>(prefix),
name);
521 return g_snprintf (p,
len,
"%s",
name);
540 bool preserve = (xmlNodeGetSpacePreserve (
node->
parent) == 1);
543 for (p =
node->
content; *p && g_ascii_isspace (*p) && !preserve; p++)
552 node->
type == XML_CDATA_SECTION_NODE );
555 if (
node->
type == XML_COMMENT_NODE) {
564 if (
node->
type == XML_ENTITY_DECL) {
572 for (prop =
node->properties; prop !=
nullptr; prop = prop->next) {
573 if (prop->children) {
575 repr->
setAttribute(
c,
reinterpret_cast<gchar*
>(prop->children->content));
597 gchar
const *default_ns,
598 gchar
const *old_href_abs_base,
599 gchar
const *new_href_abs_base)
602 bool inlineattrs = prefs->
getBool(
"/options/svgoutput/inlineattrs");
603 int indent = prefs->
getInt(
"/options/svgoutput/indent", 2);
606 out->
writeString(
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" );
608 const gchar *str =
static_cast<Node *
>(doc)->attribute(
"doctype");
614 repr; repr = repr->
next())
619 old_href_abs_base, new_href_abs_base);
622 old_href_abs_base, new_href_abs_base);
646 gchar
const *
const old_href_abs_base,
647 gchar
const *
const new_href_abs_base)
670 gchar
const *old_base, gchar
const *for_filename)
678 size_t const filename_len = strlen(filename);
679 compress = ( filename_len > 5
680 && strcasecmp(
".svgz", filename + filename_len - 5) == 0 );
685 if (file ==
nullptr) {
689 std::string old_href_abs_base;
690 std::string new_href_abs_base;
693 old_href_abs_base = old_base;
694 if (!Glib::path_is_absolute(old_href_abs_base)) {
695 old_href_abs_base = Glib::build_filename(Glib::get_current_dir(), old_href_abs_base);
700 if (Glib::path_is_absolute(for_filename)) {
701 new_href_abs_base = Glib::path_get_dirname(for_filename);
703 std::string
const cwd = Glib::get_current_dir();
704 std::string
const for_abs_filename = Glib::build_filename(cwd, for_filename);
705 new_href_abs_base = Glib::path_get_dirname(for_abs_filename);
712 sp_repr_save_stream(doc, file, default_ns, compress, old_href_abs_base.c_str(), new_href_abs_base.c_str());
714 if (fclose (file) != 0) {
734 for (; *val !=
'\0'; val++) {
740 case '\n': out.
writeString( attr ?
" " :
"\n" );
break;
749 if ( indentLevel > 16 ) {
752 if (addWhitespace && indent) {
753 for (gint i = 0; i < indentLevel; i++) {
754 for (gint j = 0; j < indent; j++) {
760 out.
printf(
"<!--%s-->", val);
769typedef std::map<Glib::QueryQuark, gchar const *, Inkscape::compare_quark_ids> LocalNameMap;
770typedef std::map<Glib::QueryQuark, Inkscape::Util::ptr_shared, Inkscape::compare_quark_ids> NSMap;
772gchar
const *qname_local_name(Glib::QueryQuark qname) {
773 static LocalNameMap local_name_map;
774 LocalNameMap::iterator iter = local_name_map.find(qname);
775 if ( iter != local_name_map.end() ) {
776 return (*iter).second;
778 gchar
const *name_string=g_quark_to_string(qname);
779 gchar
const *prefix_end=strchr(name_string,
':');
781 return prefix_end + 1;
788void add_ns_map_entry(NSMap &ns_map, Glib::QueryQuark prefix) {
792 static const Glib::QueryQuark xml_prefix(
"xml");
794 NSMap::iterator iter=ns_map.find(prefix);
795 if ( iter == ns_map.end() ) {
799 ns_map.insert(NSMap::value_type(prefix,
share_unsafe(uri)));
800 }
else if ( prefix != xml_prefix ) {
801 g_warning(
"No namespace known for normalized prefix %s", g_quark_to_string(prefix));
804 ns_map.insert(NSMap::value_type(prefix, ptr_shared()));
809void populate_ns_map(NSMap &ns_map,
Node &repr) {
811 add_ns_map_entry(ns_map, qname_prefix(repr.
code()));
812 for (
const auto & iter : repr.attributeList() )
814 Glib::QueryQuark prefix=qname_prefix(iter.key);
816 add_ns_map_entry(ns_map, prefix);
822 populate_ns_map(ns_map, *
child);
830 bool add_whitespace, gchar
const *default_ns,
831 int inlineattrs,
int indent,
832 gchar
const *
const old_href_base,
833 gchar
const *
const new_href_base)
837 g_assert(repr !=
nullptr);
841 bool clean = prefs->
getBool(
"/options/svgoutput/check_on_writing");
845 bool sort = !prefs->
getBool(
"/options/svgoutput/disable_optimizations") && prefs->
getBool(
"/options/svgoutput/sort_attributes");
848 Glib::QueryQuark xml_prefix=g_quark_from_static_string(
"xml");
851 populate_ns_map(ns_map, *repr);
853 Glib::QueryQuark elide_prefix=
GQuark(0);
854 if ( default_ns && ns_map.find(
GQuark(0)) == ns_map.end() ) {
861 for (
auto iter : ns_map)
863 Glib::QueryQuark prefix=iter.first;
864 ptr_shared ns_uri=iter.second;
867 if ( prefix != xml_prefix ) {
868 if ( elide_prefix == prefix ) {
870 attributes.emplace_back(g_quark_from_static_string(
"xmlns"), ns_uri);
873 Glib::ustring attr_name=
"xmlns:";
874 attr_name.append(g_quark_to_string(prefix));
875 GQuark key = g_quark_from_string(attr_name.c_str());
877 attributes.emplace_back(
key, ns_uri);
887 inlineattrs, indent, old_href_base, new_href_base);
891 bool add_whitespace, Glib::QueryQuark elide_prefix,
892 int inlineattrs,
int indent,
893 gchar
const *
const old_href_base,
894 gchar
const *
const new_href_base)
896 switch (repr->
type()) {
900 if (textnode->is_CData()) {
918 add_whitespace, elide_prefix,
921 old_href_base, new_href_base);
925 g_assert_not_reached();
929 g_assert_not_reached();
935 int inlineattrs,
int indent,
char const *old_href_base,
936 char const *new_href_base)
950 Glib::QueryQuark elide_prefix,
951 const AttributeVector & attributes,
952 int inlineattrs,
int indent,
953 gchar
const *old_href_base,
954 gchar
const *new_href_base )
958 bool const add_whitespace_parent = add_whitespace;
960 g_return_if_fail (repr !=
nullptr);
966 if (add_whitespace && indent) {
968 for (gint j = 0; j < indent; j++) {
975 gchar
const *element_name;
976 if ( elide_prefix == qname_prefix(code) ) {
977 element_name = qname_local_name(code);
979 element_name = g_quark_to_string(code);
981 out.
printf(
"<%s", element_name );
985 if (strcmp(repr->
name(),
"svg:text") == 0 ||
986 strcmp(repr->
name(),
"svg:flowRoot") == 0) {
987 add_whitespace =
false;
990 gchar
const *xml_space_attr = repr->
attribute(
"xml:space");
991 if (g_strcmp0(xml_space_attr,
"preserve") == 0) {
992 add_whitespace =
false;
993 }
else if (g_strcmp0(xml_space_attr,
"default") == 0) {
994 add_whitespace =
true;
998 const auto rbd = rebase_href_attrs(old_href_base, new_href_base, attributes);
999 for (
const auto &iter : rbd) {
1004 for ( gint j = 0 ; j < indent ; j++ ) {
1010 out.
printf(
" %s=\"", g_quark_to_string(iter.key));
1025 if (loose && add_whitespace) {
1030 add_whitespace, elide_prefix, inlineattrs, indent,
1031 old_href_base, new_href_base);
1034 if (loose && add_whitespace && indent) {
1036 for ( gint j = 0 ; j < indent ; j++ ) {
1041 out.
printf(
"</%s>", element_name );
1046 if (add_whitespace_parent) {
TODO: insert short description here.
void sp_attribute_clean_tree(Node *repr)
Remove or warn about inappropriate attributes and useless stype properties.
Utility functions related to parsing and validation of XML attributes.
void sp_attribute_sort_tree(Node &repr)
Sort attributes by name.
TODO: insert short description here.
Cairo::RefPtr< Cairo::Region > clean
This class is for sending a stream to a destination file.
This class is for gzip-compressing data going to the destination OutputStream.
Class for placing a Writer on an open OutputStream.
void close() override
Close the underlying OutputStream.
This class is for sending a stream to a Glib::ustring.
virtual Glib::ustring & getString()
This interface and its descendants are for unicode character-oriented output.
virtual Writer & printf(char const *fmt,...) G_GNUC_PRINTF(2
virtual Writer & writeString(const char *str)=0
virtual Writer virtual Writer & writeChar(char val)=0
Preference storage class.
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
Key-value pair representing an attribute.
Interface for refcounted XML nodes.
virtual Node * parent()=0
Get the parent of this node.
virtual Node * next()=0
Get the next sibling of this node.
virtual void setCodeUnsafe(int code)=0
Set the integer GQuark code for the name of the node.
virtual int code() const =0
Get the integer code corresponding to the node's name.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
virtual char const * name() const =0
Get the name of the element node.
virtual const AttributeVector & attributeList() const =0
Get a list of the node's attributes.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual Node * firstChild()=0
Get the first child of this node.
virtual void setContent(char const *value)=0
Set the content of a text or comment node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual char const * content() const =0
Get the content of a text or comment node.
virtual NodeType type() const =0
Get the type of the node.
Inkscape::Extension::Extension: Frontend to certain, possibly pluggable, actions.
Zlib-enabled input and output streams.
Inkscape::XML::Node * node
TODO: insert short description here.
static R & release(R &r)
Decrements the reference count of a anchored object.
bool file_test(char const *utf8name, GFileTest test)
void dump_fopen_call(char const *utf8name, char const *id)
FILE * fopen_utf8name(char const *utf8name, char const *mode)
Open a file with g_fopen().
ptr_shared share_unsafe(char const *string)
ptr_shared share_string(char const *string)
NodeType
Enumeration containing all supported node types.
@ DOCUMENT_NODE
Top-level document node. Do not confuse with the root node.
@ PI_NODE
Processing instruction node, e.g. <?xml version="1.0" encoding="utf-8" standalone="no"?...
@ COMMENT_NODE
Comment node, e.g. <!– some comment –>.
@ ELEMENT_NODE
Regular element node, e.g. <group />.
@ TEXT_NODE
Text node, e.g. "Some text" in <group>Some text</group> is represented by a text node.
AttributeVector rebase_href_attrs(char const *old_abs_base, char const *new_abs_base, const AttributeVector &attributes)
Change relative xlink:href attributes to be relative to new_abs_base instead of old_abs_base.
std::vector< AttributeRecord, Inkscape::GC::Alloc< AttributeRecord > > AttributeVector
Helper class to stream background task notifications as a series of messages.
static cairo_user_data_key_t key
Singleton class to access the preferences file in a convenient way.
TODO: insert short description here.
Document * sp_repr_read_file(const gchar *filename, const gchar *default_ns, bool xinclude)
Reads XML from a file, and returns the Document.
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)
bool sp_repr_save_rebased_file(Document *doc, gchar const *const filename, gchar const *default_ns, gchar const *old_base, gchar const *for_filename)
Returns true if file successfully saved.
static void repr_write_comment(Writer &out, const gchar *val, bool addWhitespace, gint indentLevel, int indent)
Document * sp_repr_read_mem(const gchar *buffer, gint length, const gchar *default_ns)
Reads and parses XML from a buffer, returning it as an Document.
Document * sp_repr_do_read(xmlDocPtr doc, const gchar *default_ns)
Reads in a XML file to create a Document.
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 gint sp_repr_qualified_name(gchar *p, gint len, xmlNsPtr ns, const xmlChar *name, const gchar *default_ns, std::map< std::string, std::string > &prefix_map)
Document * sp_repr_read_buf(const Glib::ustring &buf, const gchar *default_ns)
Reads and parses XML from a buffer, returning it as an Document.
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)
Glib::ustring sp_repr_save_buf(Document *doc)
Glib::ustring sp_repr_write_buf(Node *repr, int indent_level, bool add_whitespace, Glib::QueryQuark elide_prefix, int inlineattrs, int indent, char const *old_href_base, char const *new_href_base)
static void repr_quote_write(Writer &out, const gchar *val, bool attr)
bool sp_repr_save_file(Document *doc, gchar const *const filename, gchar const *default_ns)
Returns true iff file successfully saved.
static Node * sp_repr_svg_read_node(Document *xml_doc, xmlNodePtr node, const gchar *default_ns, std::map< std::string, std::string > &prefix_map)
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)
static void sp_repr_write_stream_element(Node *repr, Writer &out, gint indent_level, bool add_whitespace, Glib::QueryQuark elide_prefix, const AttributeVector &attributes, int inlineattrs, int indent, gchar const *old_href_abs_base, gchar const *new_href_abs_base)
gchar const * sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
gchar const * sp_xml_ns_prefix_uri(gchar const *prefix)
C facade to Inkscape::XML::Node.
Inkscape::XML::Node * sp_repr_document_first_child(Inkscape::XML::Document const *doc)
Inkscape::XML::SimpleDocument - generic XML document implementation.
static unsigned indent_level
Interface for XML documents.
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
virtual Node * createComment(char const *content)=0
virtual Node * createPI(char const *target, char const *content)=0
Text node implementation.
This should be the only way that we provide sources/sinks to any input/output stream.