[aisleriot] cards: Add svgcrush utility
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [aisleriot] cards: Add svgcrush utility
- Date: Wed, 7 Dec 2011 22:30:58 +0000 (UTC)
commit 41038e7b15b36b7357cfb85053c184314dc56c56
Author: Christian Persch <chpe gnome org>
Date: Wed Dec 7 23:09:24 2011 +0100
cards: Add svgcrush utility
Add noinst utility programme that takes a SVG with embedded (data:image/png;base64)
PNGs, uses pngcrush to crush these and re-save the SVG. Need to "make svgcrush" manually
to build it, it's not hooked up to any targets. E.g.:
$ make svgcrush
$ ./svgcrush bonded.svg bonded-crushed.svg
cards/Makefile.am | 9 ++
cards/svgcrush.c | 273 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 282 insertions(+), 0 deletions(-)
---
diff --git a/cards/Makefile.am b/cards/Makefile.am
index 9af80c5..30cfca3 100644
--- a/cards/Makefile.am
+++ b/cards/Makefile.am
@@ -38,4 +38,13 @@ DISTCLEANFILES = \
%.svgz: %.svg
$(AM_V_GEN)$(XMLLINT) $(XMLLINT_FLAGS) $< | $(GZIP) $(GZIP_FLAGS) > $@
+# SVG crush utility
+
+svgcrush: svgcrush.c Makefile
+ $(AM_V_GEN) $(CC) `$(PKG_CONFIG) --libs --cflags glib-2.0 libxml-2.0` $(AM_CPPFLAGS) $(AM_CFLAGS) $(AM_LDFLAGS) -o $@ $<
+
+EXTRA_DIST += svgcrush.c
+
+# .gitignore
+
-include $(top_srcdir)/git.mk
diff --git a/cards/svgcrush.c b/cards/svgcrush.c
new file mode 100644
index 0000000..11ab99d
--- /dev/null
+++ b/cards/svgcrush.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright  2011 Christian Persch
+ *
+ * This programme is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This programme is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this programme. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/wait.h>
+
+#ifdef KEEP_TEMPFILES
+#define UNLINK(s)
+#else
+#define UNLINK(s) unlink((s))
+#endif
+
+static char *pngcrush;
+
+static char *
+decode (const char *content,
+ gsize *len)
+{
+ const char *comma;
+ char *content_type;
+ char **split;
+ gboolean is_base64;
+ int i;
+
+ content += strlen ("data:");
+ comma = strchr (content, ',');
+ if (comma == NULL)
+ return NULL;
+
+ content_type = g_strndup (content, comma - content);
+ split = g_strsplit (content_type, ";", -1);
+ g_free (content_type);
+ if (split == NULL)
+ return NULL;
+ if (strcmp (split[0], "image/png") != 0) {
+ g_strfreev (split);
+ return NULL;
+ }
+
+ is_base64 = FALSE;
+ for (i = 1; split[i]; ++i)
+ if (strcmp (split[i], "base64") == 0) {
+ is_base64 = TRUE;
+ break;
+ }
+ g_strfreev (split);
+
+ if (!is_base64)
+ return NULL;
+
+ content = comma + 1;
+ return (char *) g_base64_decode (content, len);
+}
+
+static char *
+save (const char *content,
+ gsize *len)
+{
+ char *decoded;
+ char *filename;
+ int fd;
+
+ decoded = decode (content, len);
+ if (decoded == NULL)
+ return NULL;
+
+ filename = g_strdup ("./svg-crush-XXXXXXXX");
+ fd = g_mkstemp (filename);
+ if (fd == -1 ||
+ !g_file_set_contents (filename, decoded, *len, NULL)) {
+ g_free (filename);
+ g_free (decoded);
+ return NULL;
+ }
+
+ g_free (decoded);
+ return filename;
+}
+
+static void
+shutup (gpointer data G_GNUC_UNUSED)
+{
+ close (STDIN_FILENO);
+ close (STDOUT_FILENO);
+ close (STDERR_FILENO);
+}
+
+static char *
+crush (const char *tmp_in,
+ gsize *len)
+{
+ char *tmp_out;
+ char *contents = NULL;
+ int fd, status;
+ gboolean retval;
+ char *argv[4];
+
+ tmp_out = g_strdup ("svg-crush-XXXXXXXX");
+ fd = g_mkstemp (tmp_out);
+ if (fd == -1) {
+ g_free (tmp_out);
+ return NULL;
+ }
+ close (fd);
+
+ argv[0] = pngcrush;
+ argv[1] = (char *) tmp_in;
+ argv[2] = tmp_out;
+ argv[3] = NULL;
+ retval = g_spawn_sync (NULL /* our cwd */,
+ argv,
+ NULL /* envv */,
+ 0,
+ shutup /* pngcrush is very noisy */, NULL,
+ NULL /* stdin */, NULL /* stdout */,
+ &status,
+ NULL);
+ if (!retval || !WIFEXITED (status) || WEXITSTATUS (status) != 0)
+ goto out;
+
+ if (!g_file_get_contents (tmp_out, &contents, len, NULL))
+ contents = NULL;
+
+ /* pngcrush sometimes exits with status 0 but writes a zero-length file */
+ if (*len == 0) {
+ g_free (contents);
+ contents = NULL;
+ }
+
+out:
+ UNLINK (tmp_out);
+ g_free (tmp_out);
+
+ return contents;
+}
+
+static char*
+encode (const char *content,
+ gsize len)
+{
+ char *encoded, *data;
+
+ encoded = g_base64_encode ((const guchar *) content, len);
+ data = g_strconcat ("data:image/png;base64,", encoded, NULL);
+ g_free (encoded);
+
+ return data;
+}
+
+static void
+process (xmlNodePtr node)
+{
+ char *tmp_in;
+ gsize in_len, out_len;
+ char *crushed, *encoded;
+
+ tmp_in = save ((const char *) node->content, &in_len);
+ if (tmp_in == NULL)
+ return;
+
+ crushed = crush (tmp_in, &out_len);
+ if (crushed == NULL)
+ goto out;
+
+ encoded = encode (crushed, out_len);
+ xmlNodeSetContent (node, (const xmlChar *) encoded);
+ g_free (encoded);
+
+ g_print ("OUT %" G_GSIZE_FORMAT " / IN %" G_GSIZE_FORMAT " = %.1f%%\n",
+ out_len, in_len, 100. * (double) out_len / (double) in_len);
+
+out:
+ UNLINK (tmp_in);
+ g_free (tmp_in);
+}
+
+static gboolean
+transform (xmlDocPtr doc)
+{
+ gboolean retval = FALSE;
+ xmlXPathContextPtr ctx;
+ xmlXPathObjectPtr obj;
+ xmlNodeSetPtr set;
+ xmlNodePtr *nodes;
+ int i, n_nodes;
+
+ ctx = xmlXPathNewContext (doc);
+
+ obj = xmlXPathEvalExpression ((const xmlChar *) "//*/@*[namespace-uri()='http://www.w3.org/1999/xlink']", ctx);
+ if (obj == NULL || obj->type != XPATH_NODESET)
+ goto out;
+
+ set = obj->nodesetval;
+ nodes = set->nodeTab;
+ n_nodes = set->nodeNr;
+
+ for (i = 0; i < n_nodes; ++i) {
+ xmlNodePtr child = nodes[i]->children;
+
+ if (child == NULL ||
+ child->content == NULL)
+ continue;
+
+ process (child);
+ }
+
+ retval = TRUE;
+
+out:
+ if (obj)
+ xmlXPathFreeObject (obj);
+
+ xmlXPathFreeContext (ctx);
+
+ return retval;
+}
+
+int main (int argc, char *argv[])
+{
+ int retval = 1;
+ xmlDoc *doc;
+
+ if (argc != 3) {
+ g_printerr ("Usage: %s INPUT-FILE OUTPUT-FILE\n", argv[0]);
+ return 1;
+ }
+
+ pngcrush = g_find_program_in_path ("pngcrush");
+ if (pngcrush == NULL) {
+ g_printerr ("pngcrush not found in $PATH\n");
+ return 1;
+ }
+
+ doc = xmlReadFile (argv[1], "UTF-8", XML_PARSE_NONET);
+ if (doc == NULL)
+ goto out;
+
+ if (!transform (doc))
+ goto out;
+
+ if (xmlSaveFile (argv[2], doc) == -1)
+ goto out;
+
+ retval = 0;
+
+out:
+ if (doc)
+ xmlFreeDoc (doc);
+ g_free (pngcrush);
+
+ return retval;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]