[gnome-epub-thumbnailer] main: Add support for reading the metafile



commit 3a3992b42c5444e1f2cd504b644c1844970b72c5
Author: Bastien Nocera <hadess hadess net>
Date:   Sat Jul 13 10:48:14 2013 +0200

    main: Add support for reading the metafile
    
    To locate the rootfile, and parsing the rootfile to know the
    path of the cover image.

 configure.ac             |    2 +-
 gnome-epub-thumbnailer.c |  167 ++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 156 insertions(+), 13 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index aacb5e2..6912fc8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,7 +12,7 @@ AC_PROG_INSTALL
 AC_PROG_SED
 AM_PROG_CC_C_O
 
-PKG_CHECK_MODULES(THUMBNAILER, gdk-pixbuf-2.0 gio-2.0 libarchive)
+PKG_CHECK_MODULES(THUMBNAILER, gdk-pixbuf-2.0 gio-2.0 libarchive libxml-2.0)
 
 AC_CONFIG_FILES([Makefile])
 AC_OUTPUT
diff --git a/gnome-epub-thumbnailer.c b/gnome-epub-thumbnailer.c
index 8e6733b..93e09ac 100644
--- a/gnome-epub-thumbnailer.c
+++ b/gnome-epub-thumbnailer.c
@@ -23,9 +23,18 @@
 #include <glib.h>
 #include <gio/gio.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
+
 #include <archive.h>
 #include <archive_entry.h>
 
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#define METAFILE_NAMESPACE "urn:oasis:names:tc:opendocument:xmlns:container"
+#define OPF_NAMESPACE "http://www.idpf.org/2007/opf";
+
 static int output_size = 64;
 static gboolean g_fatal_warnings = FALSE;
 static char **filenames = NULL;
@@ -37,17 +46,6 @@ static const GOptionEntry entries[] = {
        { NULL }
 };
 
-static char *
-get_cover_path_from_metafile (const char *metafile,
-                             gsize       length)
-{
-       //FIXME implement
-       //
-       //This means, finding the cover in the metafile, OR
-       //finding the OPF root file in the metafile, and parsing that to find the cover image
-       return NULL;
-}
-
 static int
 regex_matches (gconstpointer a,
               gconstpointer b)
@@ -107,6 +105,145 @@ file_get_zipped_contents (const char   *filename,
        return ret;
 }
 
+static xmlDocPtr
+open_doc (const char *data,
+         gsize       length,
+         const char *root_name)
+{
+       xmlDocPtr doc;
+
+       if (data == NULL)
+               return NULL;
+
+       doc = xmlParseMemory (data, length);
+       if (doc == NULL)
+               doc = xmlRecoverMemory (data, length);
+
+       if(!doc ||
+          !doc->children ||
+          !doc->children->name ||
+          g_ascii_strcasecmp ((char *)doc->children->name, root_name) != 0) {
+               if (doc != NULL)
+                       xmlFreeDoc (doc);
+               return NULL;
+       }
+
+       return doc;
+}
+
+static char *
+get_prop_for_xpath (xmlDocPtr           doc,
+                   xmlXPathContextPtr  xpath_ctx,
+                   const char         *path,
+                   const char         *name)
+{
+       xmlXPathObjectPtr xpath_obj;
+       xmlNodePtr cur;
+       char *ret;
+
+       xpath_obj = xmlXPathEvalExpression (BAD_CAST (path), xpath_ctx);
+       if (xpath_obj == NULL)
+               return NULL;
+       if (xpath_obj->nodesetval == NULL) {
+               xmlXPathFreeObject (xpath_obj);
+               return NULL;
+       }
+       cur = xpath_obj->nodesetval->nodeTab[0];
+       ret = g_strdup ((const char *) xmlGetProp (cur, name));
+       xmlXPathFreeObject (xpath_obj);
+
+       return ret;
+}
+
+static char *
+get_root_file_from_metafile (const char *metafile,
+                            gsize       length)
+{
+       char *root_file;
+       xmlDocPtr doc;
+       xmlXPathContextPtr xpath_ctx;
+
+       doc = open_doc (metafile, length, "container");
+       if (!doc)
+               return NULL;
+
+       xpath_ctx = xmlXPathNewContext(doc);
+       xmlXPathRegisterNs (xpath_ctx, BAD_CAST ("ns"), BAD_CAST (METAFILE_NAMESPACE));
+
+       root_file = get_prop_for_xpath (doc, xpath_ctx, "//ns:container/ns:rootfiles/ns:rootfile", 
"full-path");
+
+       xmlXPathFreeContext(xpath_ctx);
+       xmlFreeDoc (doc);
+
+       return root_file;
+}
+
+static char *
+resolve_cover_path (const char *cover_path,
+                   const char *root_path)
+{
+       char *dirname;
+       char *ret;
+
+       dirname = g_path_get_dirname (root_path);
+       ret = g_build_filename (dirname, cover_path, NULL);
+       g_free (dirname);
+
+       return ret;
+}
+
+static char *
+get_cover_path_from_root_file (const char *metafile,
+                              gsize       length,
+                              const char *input_filename)
+{
+       xmlDocPtr doc;
+       xmlXPathContextPtr xpath_ctx;
+       char *root_path;
+       char *root_file;
+       gsize root_length;
+       char *content_name;
+       char *xpath;
+       char *cover_path, *full_cover_path;
+
+       cover_path = NULL;
+
+       root_path = get_root_file_from_metafile (metafile, length);
+       if (!root_path)
+               return NULL;
+
+       root_file = file_get_zipped_contents (input_filename, (GCompareFunc) g_strcmp0, root_path, 
&root_length);
+
+       doc = open_doc (root_file, root_length, "package");
+       g_free (root_file);
+       if (!doc) {
+               g_free (root_path);
+               return NULL;
+       }
+
+       xpath_ctx = xmlXPathNewContext(doc);
+       xmlXPathRegisterNs (xpath_ctx, BAD_CAST ("ns"), BAD_CAST (OPF_NAMESPACE));
+
+       content_name = get_prop_for_xpath (doc, xpath_ctx, "//ns:package/ns:metadata/ns:meta[ name='cover']", 
"content");
+       if (!content_name)
+               goto bail;
+
+       xpath = g_strdup_printf ("//ns:package/ns:manifest/ns:item[ id='%s']", content_name);
+       g_free (content_name);
+       cover_path = get_prop_for_xpath (doc, xpath_ctx, xpath, "href");
+       g_free (xpath);
+
+       full_cover_path = resolve_cover_path (cover_path, root_path);
+       g_free (cover_path);
+       cover_path = full_cover_path;
+
+bail:
+       xmlXPathFreeContext(xpath_ctx);
+       xmlFreeDoc (doc);
+
+       return cover_path;
+}
+
 int main (int argc, char **argv)
 {
        char *input_filename;
@@ -154,7 +291,7 @@ int main (int argc, char **argv)
 
        /* Look for the cover in the metafile */
        metafile = file_get_zipped_contents (input_filename, (GCompareFunc) g_strcmp0, 
"META-INF/container.xml", &length);
-       cover_path = get_cover_path_from_metafile (metafile, length);
+       cover_path = get_cover_path_from_root_file (metafile, length, input_filename);
        g_free (metafile);
        if (cover_path != NULL) {
                cover_data = file_get_zipped_contents (input_filename, (GCompareFunc) g_strcmp0, cover_path, 
&length);
@@ -174,6 +311,12 @@ int main (int argc, char **argv)
        pixbuf = gdk_pixbuf_new_from_stream_at_scale (mem_stream, output_size, -1, TRUE, NULL, NULL);
        g_object_unref (mem_stream);
 
+       if (!pixbuf) {
+               g_warning ("Couldn't open embedded cover image for file '%s'",
+                          filenames[0]);
+               return 1;
+       }
+
        if (gdk_pixbuf_save (pixbuf, output, "png", &error, NULL) == FALSE) {
                g_warning ("Couldn't save the thumbnail '%s' for file '%s': %s", output, filenames[0], 
error->message);
                g_error_free (error);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]