+
+ xmlFreeDoc(xinc_doc);
+
+ return TRUE;
+}
+
+/**
+ * @brief Replace all GTK <xi:include> elements in a tree by their data.
+ *
+ * @param node
+ * A node pointer, to the root of the tree.
+ *
+ * @return TRUE if we replaced any <xi:include> element nodes, and
+ * FALSE otherwise.
+ *
+ */
+static gboolean process_child_xincludes(xmlNode* a_node) {
+ gboolean result = FALSE;
+ xmlNode* cur_node = a_node;
+ xmlNode* next_node;
+
+ g_assert(cur_node == NULL || cur_node->type == XML_ELEMENT_NODE);
+
+ while (cur_node) {
+ if (!xmlStrcmp(cur_node->name, BAD_CAST "include")) {
+ /* process_one_xinclude() clobbers this node, so we need
+ to get its successor before calling that function. */
+ next_node = xmlNextElementSibling(cur_node);
+ if (process_one_xinclude(cur_node)) {
+ result = TRUE;
+ }
+ cur_node = next_node;
+ continue;
+ }
+
+ if (process_child_xincludes(xmlFirstElementChild(cur_node))) {
+ result = TRUE;
+ }
+ cur_node = xmlNextElementSibling(cur_node);
+ }
+
+ return result;
+}
+
+
+/**
+ * @brief Process GTK <xi:include> elements in an SVG buffer.
+ *
+ * GTK is very cute. Its gtk-encode-symbolic-svg tool wraps your SVG
+ * in its own boilerplate, but then rather than including your SVG
+ * data verbatim, it includes it via a sketchy XInclude that looks
+ * like,
+ *
+ * <xi:include href="data:text/xml;base64,PD94bWwgd..."/>
+ *
+ * Librsvg knows how to parse that, but libxml2 doesn't (the latter
+ * can handle an XInclude, just not a base64-encoded data reference).
+ * Fortunately, you can read the source to gtk-encode-symbolic-svg,
+ * and just see what the format of that line will be. Here we're going
+ * to parse out the base64 data, decode it, strip out its opening
+ * <xml> and <svg> tags, and then replace the original <xi:include>
+ * element with the result.
+ *
+ * @param buffer
+ * A buffer containing SVG file data.
+ *
+ * @param buf_size
+ * The size of @c buffer (which may not be NULL-terminated).
+ *
+ * @param new_size
+ * A pointer to the size of the new buffer, valid only if the
+ * return value is non-NULL.
+ *
+ * @return A pointer to a buffer where the <xi:include> has been
+ * processed. If no replacements were made, the result will be @c
+ * NULL; otherwise, you are expected to @c free it when you are done.
+ */
+static gchar* process_gtk_symbolic_svg_xinclude(const gchar* buffer,
+ gsize buf_size,
+ gsize* new_size) {
+
+ xmlDoc* doc = xmlReadMemory(buffer,buf_size,"symbolic.xml",NULL,0);
+ if (doc == NULL) {
+ return NULL;
+ }
+
+ xmlNode* root_element = xmlDocGetRootElement(doc);
+ if (root_element == NULL) {
+ return NULL;
+ }
+
+ gchar* result = NULL;
+ if (process_child_xincludes(root_element)) {
+ /* If we actually replaced something, we need to return the new
+ document in a buffer. */
+ xmlChar *xmlbuf;
+ int xmlbuf_size;
+ xmlDocDumpFormatMemory(doc, &xmlbuf, &xmlbuf_size, 1);
+ /* We're going to free() this later on with g_free() instead of
+ xmlFree(), so the two "byte" types had better be the same
+ size. */
+ g_assert(sizeof(xmlChar) == sizeof(gchar));
+ *new_size = (gsize)xmlbuf_size;
+ result = (gchar*)xmlbuf;
+ }
+
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ return result;