#include <stdio.h> /* fprintf, printf */
-#include <string.h> /* memcpy */
+#include <string.h> /* memcpy, memset, strstr */
#include <cairo.h>
#include <gdk/gdk.h>
#define VIEWPORT_WIDTH 512
#define VIEWPORT_HEIGHT 512
+/* The start of an XInclude that was inserted by
+ * gtk-encode-symbolic-svg */
+#define XI_SIGNATURE "<xi:include href=\"data:text/xml;base64,"
+
+
/* Convenient typedefs for libsvgtiny */
typedef struct svgtiny_diagram diagram_t;
typedef struct svgtiny_shape shape_t;
}
+/**
+ * @brief Process certain <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. Life is a little easier because the
+ * closing </svg> tag always immediately follows the XInclude; we can
+ * chop the whole thing off, decode the base64 stuff, and then paste
+ * the result back on the end with its own closing </svg> tag intact.
+ *
+ * @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 char* process_gtk_symbolic_svg_xinclude(const char* buffer,
+ size_t buf_size,
+ size_t* new_size) {
+ char* xi_start;
+ char* xi;
+ char* xi_stop;
+
+ xi_start = strstr(buffer, XI_SIGNATURE);
+ if (xi_start == NULL) {
+ return NULL;
+ }
+
+ xi = xi_start + strlen(XI_SIGNATURE);
+ xi_stop = strstr(xi, "\"");
+ if(xi_stop == NULL) {
+ /* We found the start of an XInclude, but not the end of its
+ base64-encoded data? Play it safe and do nothing. */
+ return NULL;
+ }
+
+ /* g_base64_decode needs a NULL-terminated string, so let's make
+ "xi" into one */
+ *xi_stop = 0;
+ gsize decoded_size;
+ guchar* decoded = g_base64_decode(xi, &decoded_size);
+
+ /* We need another round of processing to strip the <xml> and <svg>
+ * elements out of "decoded", but it's simpler to just overwrite
+ * them with spaces before we proceed. We'll wind up with a document
+ * that has a conspicuous chunk of whitespace in the middle of it,
+ * but whatever. Note that we don't need to worry about the <xml>
+ * element so much, because if one exists, it has to come before the
+ * <svg>. As a result, we just need to strip everything up to the
+ * leading <svg> tag. */
+ guchar* svg_open_start = strstr(decoded, "<svg ");
+ guchar* svg_open_end = strstr(svg_open_start, ">");
+ memset(decoded, ' ', (1 + (svg_open_end - decoded)));
+
+ /* We're going to keep everything up to xi_start. If the <xi:include
+ * started at, say, position three, then this would compute a size
+ * of three. Which is correct: we want to retain buffer[0],
+ * buffer[1], and buffer[2]. */
+ size_t keep_size = xi_start - buffer;
+ *new_size = keep_size + decoded_size;
+
+ char* result = g_malloc(*new_size);
+ memcpy(result, buffer, keep_size);
+ memcpy(result+keep_size, decoded, decoded_size);
+ g_free(decoded);
+ return result;
+}
+
+
static gboolean gdk_pixbuf_stop_load(gpointer data, GError **error) {
SvgTinyContext* context = (SvgTinyContext*)data;
GdkPixbuf* pixbuf = NULL;
gboolean result = TRUE;
GError* sub_error = NULL;
+
+ /* If we're inside of gtk-encode-symbolic-svg right now, we need to
+ process the insane librsvg-specific XInclude directive it hands
+ us before proceeding. */
+ size_t newsize;
+ char* newdata = process_gtk_symbolic_svg_xinclude(context->svg_data,
+ context->svg_data_size,
+ &newsize);
+ if (newdata != NULL) {
+ g_free(context->svg_data);
+ context->svg_data = newdata;
+ context->svg_data_size = newsize;
+ }
+
+ /* OK, we've got an SVG with no XIncludes now. */
pixbuf = gdk_pixbuf_from_svg_buffer(context->svg_data,
context->svg_data_size,
&sub_error);