[gnome-epub-thumbnailer] Add MOBI Thumbnailer
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-epub-thumbnailer] Add MOBI Thumbnailer
- Date: Sun, 21 Jul 2013 19:19:04 +0000 (UTC)
commit 4df9ba37f2f1aa3bb0d05950c4511901b92307a8
Author: Bastien Nocera <hadess hadess net>
Date: Sun Jul 21 15:59:06 2013 +0200
Add MOBI Thumbnailer
Makefile.am | 18 ++-
README | 2 +-
gnome-epub-thumbnailer.doap | 2 +-
gnome-mobi-thumbnailer.c | 245 +++++++++++++++++++++++++++++++++
gnome-mobi-thumbnailer.thumbnailer.in | 4 +
5 files changed, 265 insertions(+), 6 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index b18655a..1ca21f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
-bin_PROGRAMS = gnome-epub-thumbnailer
+bin_PROGRAMS = gnome-epub-thumbnailer gnome-mobi-thumbnailer
gnome_epub_thumbnailer_SOURCES = gnome-epub-thumbnailer.c gnome-thumbnailer-skeleton.c
gnome-thumbnailer-skeleton.h
gnome_epub_thumbnailer_CPPFLAGS = \
@@ -9,11 +9,21 @@ gnome_epub_thumbnailer_CPPFLAGS = \
$(WARN_CFLAGS)
gnome_epub_thumbnailer_LDADD = $(THUMBNAILER_LIBS)
+gnome_mobi_thumbnailer_SOURCES = gnome-mobi-thumbnailer.c gnome-thumbnailer-skeleton.c
gnome-thumbnailer-skeleton.h
+gnome_mobi_thumbnailer_CPPFLAGS = \
+ $(THUMBNAILER_CFLAGS) \
+ -DTHUMBNAILER_RETURNS_DATA \
+ -DTHUMBNAILER_USAGE="\"Thumbnail MOBI books\"" \
+ $(WARN_CFLAGS)
+gnome_mobi_thumbnailer_LDADD = $(THUMBNAILER_LIBS)
+
thumbnailerdir = $(datadir)/thumbnailers/
-thumbnailer_DATA = gnome-epub-thumbnailer.thumbnailer
+thumbnailer_DATA = gnome-epub-thumbnailer.thumbnailer gnome-mobi-thumbnailer.thumbnailer
gnome-epub-thumbnailer.thumbnailer: gnome-epub-thumbnailer.thumbnailer.in Makefile
$(AM_V_GEN) $(SED) -e "s|\ bindir\@|$(bindir)|" $< > $@
+gnome-mobi-thumbnailer.thumbnailer: gnome-mobi-thumbnailer.thumbnailer.in Makefile
+ $(AM_V_GEN) $(SED) -e "s|\ bindir\@|$(bindir)|" $< > $@
-EXTRA_DIST = gnome-epub-thumbnailer.thumbnailer.in AUTHORS COPYING README
+EXTRA_DIST = gnome-epub-thumbnailer.thumbnailer.in gnome-mobi-thumbnailer.thumbnailer.in AUTHORS COPYING
README
-CLEANFILES = gnome-epub-thumbnailer.thumbnailer
+CLEANFILES = $(thumbnailer_DATA)
diff --git a/README b/README
index 5c8db2c..0a575d5 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-Thumbnailer for EPub books
+Thumbnailer for EPub and MOBI books
License and copyright:
See source file
diff --git a/gnome-epub-thumbnailer.doap b/gnome-epub-thumbnailer.doap
index 3e7ba77..e46fb6c 100644
--- a/gnome-epub-thumbnailer.doap
+++ b/gnome-epub-thumbnailer.doap
@@ -4,7 +4,7 @@
xmlns:gnome="http://api.gnome.org/doap-extensions#"
xmlns="http://usefulinc.com/ns/doap#">
<name xml:lang="en">gnome-epub-thumbnailer</name>
- <shortdesc xml:lang="en">Thumbnailer for EPub books</shortdesc>
+ <shortdesc xml:lang="en">Thumbnailer for EPub and MOBI books</shortdesc>
<homepage rdf:resource="http://git.gnome.org/browse/gnome-epub-thumbnailer/" />
<category rdf:resource="http://api.gnome.org/doap-extensions#desktop" />
<maintainer>
diff --git a/gnome-mobi-thumbnailer.c b/gnome-mobi-thumbnailer.c
new file mode 100644
index 0000000..c960934
--- /dev/null
+++ b/gnome-mobi-thumbnailer.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 Bastien Nocera <hadess hadess net>
+ *
+ * Authors: Bastien Nocera <hadess hadess net>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "gnome-thumbnailer-skeleton.h"
+
+/* >H */
+static guint16
+get_guint16 (const char *data)
+{
+ guint16 *ptr = (guint16 *) (data);
+ return GUINT16_FROM_BE (ptr[0]);
+}
+
+/* >L */
+static guint32
+get_guint32 (const char *data)
+{
+ guint32 *ptr = (guint32 *) (data);
+ return GUINT32_FROM_BE (ptr[0]);
+}
+
+typedef struct {
+ char *ident;
+ unsigned short num_sections;
+ GPtrArray *offsets;
+} Sections;
+
+static void
+free_sections (Sections *sections)
+{
+ if (sections == NULL)
+ return;
+ g_free (sections->ident);
+ g_ptr_array_free (sections->offsets, FALSE);
+}
+
+static Sections *
+load_sections (GInputStream *stream,
+ GError **error)
+{
+ Sections *sections;
+ char header[78];
+ int i;
+ char *sections_data;
+ GPtrArray *array;
+
+ if (g_input_stream_read (G_INPUT_STREAM (stream), &header, sizeof(header), NULL, error) < 0)
+ return NULL;
+ sections = g_new0 (Sections, 1);
+ sections->ident = g_strndup (header + 0x3C, 8);
+ g_debug ("ident '%s'", sections->ident);
+
+ sections->num_sections = get_guint16 (header + 76);
+ g_debug ("num sections '%d'", sections->num_sections);
+
+ array = g_ptr_array_sized_new (sections->num_sections + 1);
+
+ sections_data = g_malloc (sections->num_sections * 8);
+ if (g_input_stream_read (G_INPUT_STREAM (stream), sections_data, sections->num_sections * 8, NULL,
error) < 0) {
+ free_sections (sections);
+ return NULL;
+ }
+ for (i = 0; i < sections->num_sections * 2; i = i + 2) {
+ guint32 item = get_guint32 (sections_data + i * sizeof (guint32));
+ g_debug ("section %d: %u", i, item);
+ g_ptr_array_add (array, GUINT_TO_POINTER (item));
+ }
+ /* Add this so there is an upper-boundary to the last section */
+ g_ptr_array_add (array, GUINT_TO_POINTER (0xfffffff));
+
+ sections->offsets = array;
+
+ return sections;
+}
+
+static char *
+get_section_data (GInputStream *stream,
+ Sections *sections,
+ int i,
+ gsize *len)
+{
+ unsigned long before, after;
+ gssize read;
+ char *data;
+
+ before = GPOINTER_TO_UINT (g_ptr_array_index (sections->offsets, i));
+ after = GPOINTER_TO_UINT (g_ptr_array_index (sections->offsets, i + 1));
+ g_seekable_seek (G_SEEKABLE (stream), before, G_SEEK_SET, NULL, NULL);
+
+ data = g_malloc (after - before);
+ read = g_input_stream_read (G_INPUT_STREAM (stream), data, after - before, NULL, NULL);
+
+ *len = read;
+
+ return data;
+}
+
+static int
+get_cover_img_num (const char *header)
+{
+ guint32 len;
+ const char *extheader;
+ guint32 num_items, pos;
+ guint i;
+
+ len = get_guint32 (header + 20);
+ extheader = header + 16 + len;
+
+ num_items = get_guint32 (extheader + 8);
+ g_debug ("num extheader items: %d", num_items);
+ extheader = extheader + 12;
+
+ pos = 0;
+ for (i = 0; i < num_items; i++) {
+ guint32 size, id;
+
+ id = get_guint32 (extheader + pos);
+ size = get_guint32 (extheader + pos + 4);
+ /* 201 is CoverOffset */
+ g_debug ("id %d", id);
+ if (id == 201) {
+ int ret;
+ if (size != 12) {
+ g_warning ("The CoverOffset record has the wrong size");
+ return -1;
+ }
+ ret = get_guint32 (extheader + pos + 8);
+ g_debug ("Found CoverOffset %d", ret);
+ return ret;
+ }
+ pos += size;
+ }
+
+ return -1;
+}
+
+static int
+get_image_section (GInputStream *stream,
+ Sections *sections,
+ GError **error)
+{
+ char *header;
+ gsize len;
+ guint first_img;
+ int image_num;
+
+ header = get_section_data (G_INPUT_STREAM (stream), sections, 0, &len);
+
+ /* Checking crypto type */
+ if (get_guint16 (header + 0xC) != 0) {
+ g_set_error_literal (error, 0, 0, "File is encrypted");
+ g_free (header);
+ free_sections (sections);
+ return -1;
+ }
+
+ /* Checking metadata availability */
+ if (!(get_guint32 (header + 0x80) & 0x40)) {
+ g_set_error_literal (error, 0, 0, "File has no metadata");
+ g_free (header);
+ free_sections (sections);
+ return -1;
+ }
+
+ /* Get the location of the first image */
+ if (!g_str_equal (sections->ident, "TEXtREAd")) {
+ first_img = get_guint32 (header + 0x6C);
+ g_debug ("First image in MOBIBOOK mode: %d", first_img);
+ } else {
+ first_img = get_guint16 (header + 0x8);
+ g_debug ("First image in TEXtREAd mode: %d", first_img);
+ }
+
+ image_num = get_cover_img_num (header);
+
+ return first_img + image_num;
+}
+
+char *
+file_to_data (const char *path,
+ gsize *ret_length,
+ GError **error)
+{
+ Sections *sections;
+ GFileInputStream *stream;
+ GFile *input;
+ int image_num;
+ char *ret;
+
+ /* Open the file for reading */
+ input = g_file_new_for_path (path);
+ stream = g_file_read (input, NULL, error);
+ g_object_unref (input);
+
+ if (!stream)
+ return NULL;
+
+ sections = load_sections (G_INPUT_STREAM (stream), error);
+ if (sections == NULL || sections->ident == NULL)
+ goto bail;
+ if (!g_str_equal (sections->ident, "BOOKMOBI") &&
+ !g_str_equal (sections->ident, "TEXtREAd")) {
+ g_set_error (error, 0, 0, "File is not in a recognised MOBI format '%s'", sections->ident);
+ goto bail;
+ }
+
+ image_num = get_image_section (G_INPUT_STREAM (stream), sections, error);
+ if (image_num < 0) {
+ g_set_error_literal (error, 0, 0, "Got an error getting the image number");
+ goto bail;
+ }
+
+ ret = get_section_data (G_INPUT_STREAM (stream), sections, image_num, ret_length);
+
+ g_object_unref (stream);
+ free_sections (sections);
+
+ return ret;
+
+bail:
+ g_clear_object (&stream);
+ free_sections (sections);
+ return NULL;
+}
diff --git a/gnome-mobi-thumbnailer.thumbnailer.in b/gnome-mobi-thumbnailer.thumbnailer.in
new file mode 100644
index 0000000..f96dcd7
--- /dev/null
+++ b/gnome-mobi-thumbnailer.thumbnailer.in
@@ -0,0 +1,4 @@
+[Thumbnailer Entry]
+TryExec= bindir@/gnome-mobi-thumbnailer
+Exec= bindir@/gnome-mobi-thumbnailer -s %s %u %o
+MimeType=application/x-mobipocket-ebook;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]