[libgepub] Add TOC parse to GepubDoc



commit 8680a463dcd3a50cbb94000f2a3b99699c6a792f
Author: Daniel GarcĂ­a Moreno <danigm wadobo com>
Date:   Fri Sep 21 11:00:09 2018 +0200

    Add TOC parse to GepubDoc
    
    The new `gepub_doc_get_toc` function returns a GList of GepubNavPoint
    with the `toc.ncx` information. There you can find the epub index.
    
    I've added a test function to the gepub-test that shows this
    information.
    
    Fix #6

 libgepub/gepub-doc.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++
 libgepub/gepub-doc.h |   9 ++++
 tests/test-gepub.c   |  16 +++++++
 3 files changed, 143 insertions(+)
---
diff --git a/libgepub/gepub-doc.c b/libgepub/gepub-doc.c
index 9d3219f..ea8dbba 100644
--- a/libgepub/gepub-doc.c
+++ b/libgepub/gepub-doc.c
@@ -51,7 +51,9 @@ typedef enum {
 
 static void gepub_doc_fill_resources (GepubDoc *doc);
 static void gepub_doc_fill_spine (GepubDoc *doc);
+static void gepub_doc_fill_toc (GepubDoc *doc, gchar *toc_id);
 static void gepub_doc_initable_iface_init (GInitableIface *iface);
+static gint navpoint_compare (GepubNavPoint *a, GepubNavPoint *b);
 
 struct _GepubDoc {
     GObject parent;
@@ -64,6 +66,7 @@ struct _GepubDoc {
 
     GList *spine;
     GList *chapter;
+    GList *toc;
 };
 
 struct _GepubDocClass {
@@ -103,6 +106,8 @@ gepub_doc_finalize (GObject *object)
     if (doc->spine) {
         g_list_foreach (doc->spine, (GFunc)g_free, NULL);
         g_clear_pointer (&doc->spine, g_list_free);
+        g_list_foreach (doc->toc, (GFunc)g_free, NULL);
+        g_clear_pointer (&doc->toc, g_list_free);
     }
 
     G_OBJECT_CLASS (gepub_doc_parent_class)->finalize (object);
@@ -309,12 +314,19 @@ gepub_doc_fill_spine (GepubDoc *doc)
     const char *data;
     gsize size;
     GList *spine = NULL;
+    gchar *toc = NULL;
 
     data = g_bytes_get_data (doc->content, &size);
     xdoc = xmlRecoverMemory (data, size);
     root_element = xmlDocGetRootElement (xdoc);
     snode = gepub_utils_get_element_by_tag (root_element, "spine");
 
+    toc = gepub_utils_get_prop (snode, "toc");
+    if (toc) {
+        gepub_doc_fill_toc (doc, toc);
+        g_free (toc);
+    }
+
     item = snode->children;
     while (item) {
         if (item->type != XML_ELEMENT_NODE ) {
@@ -334,6 +346,97 @@ gepub_doc_fill_spine (GepubDoc *doc)
     xmlFreeDoc (xdoc);
 }
 
+static gint
+navpoint_compare (GepubNavPoint *a, GepubNavPoint *b)
+{
+    return a->playorder - b->playorder;
+}
+
+
+static void
+gepub_doc_fill_toc (GepubDoc *doc, gchar *toc_id)
+{
+    xmlDoc *xdoc = NULL;
+    xmlNode *root_element = NULL;
+    xmlNode *mapnode = NULL;
+    xmlNode *item = NULL;
+    const char *data;
+    gsize size;
+    GList *toc = NULL;
+    GBytes *toc_data = NULL;
+
+    doc->toc = toc;
+
+    toc_data = gepub_doc_get_resource_by_id (doc, toc_id);
+    if (!toc_data) {
+        return;
+    }
+
+    data = g_bytes_get_data (toc_data, &size);
+    xdoc = xmlRecoverMemory (data, size);
+    root_element = xmlDocGetRootElement (xdoc);
+    mapnode = gepub_utils_get_element_by_tag (root_element, "navMap");
+
+    // TODO: get docTitle
+    // TODO: parse metadata (dtb:totalPageCount, dtb:depth, dtb:maxPageNumber)
+
+    item = mapnode->children;
+    while (item) {
+        GepubNavPoint *navpoint = NULL;
+        gchar *order;
+        xmlNode *navchilds = NULL;
+
+        if (item->type != XML_ELEMENT_NODE ||
+            g_strcmp0 ((const gchar *)item->name, "navPoint")) {
+            item = item->next;
+            continue;
+        }
+
+        navpoint = g_malloc0 (sizeof (GepubNavPoint));
+
+        order = gepub_utils_get_prop (item, "playOrder");
+        if (order) {
+            g_ascii_string_to_unsigned (order, 10, 0, INT_MAX,
+                                        &navpoint->playorder, NULL);
+            g_free (order);
+        }
+
+        // parsing navPoint->navLabel->text and navPoint->content
+        navchilds = item->children;
+        while (navchilds) {
+            if (item->type != XML_ELEMENT_NODE) {
+                navchilds = navchilds->next;
+                continue;
+            }
+
+            if (!g_strcmp0 ((const gchar *)navchilds->name, "content")) {
+                gchar *uri, *tmpuri;
+                tmpuri = gepub_utils_get_prop (navchilds, "src");
+                uri = g_strdup_printf ("%s%s", doc->content_base, tmpuri);
+                navpoint->content = uri;
+                g_free (tmpuri);
+            }
+
+            if (!g_strcmp0 ((const gchar *)navchilds->name, "navLabel")) {
+                xmlNode *text = gepub_utils_get_element_by_tag (navchilds, "text");
+                if (text->children && text->children->type == XML_TEXT_NODE) {
+                  navpoint->label = g_strdup ((gchar *)text->children->content);
+                }
+            }
+
+            navchilds = navchilds->next;
+        }
+
+        toc = g_list_prepend (toc, navpoint);
+        item = item->next;
+    }
+
+    doc->toc = g_list_sort (toc, (GCompareFunc) navpoint_compare);
+
+    xmlFreeDoc (xdoc);
+    g_bytes_unref (toc_data);
+}
+
 /**
  * gepub_doc_get_content:
  * @doc: a #GepubDoc
@@ -803,3 +906,18 @@ gepub_doc_get_current_id (GepubDoc *doc)
 
     return doc->chapter->data;
 }
+
+/**
+ * gepub_doc_get_toc:
+ * @doc: a #GepubDoc
+ *
+
+ * Returns: (element-type Gepub.NavPoint) (transfer none): the navigation list in order
+ */
+GList *
+gepub_doc_get_toc (GepubDoc *doc)
+{
+    g_return_val_if_fail (GEPUB_IS_DOC (doc), NULL);
+    return doc->toc;
+}
+
diff --git a/libgepub/gepub-doc.h b/libgepub/gepub-doc.h
index 01b8f8c..4290d92 100644
--- a/libgepub/gepub-doc.h
+++ b/libgepub/gepub-doc.h
@@ -40,7 +40,14 @@ struct _GepubResource {
     gchar *uri;
 };
 
+struct _GepubNavPoint {
+    gchar *label;
+    gchar *content;
+    guint64 playorder;
+};
+
 typedef struct _GepubResource GepubResource;
+typedef struct _GepubNavPoint GepubNavPoint;
 
 GType             gepub_doc_get_type                        (void) G_GNUC_CONST;
 
@@ -69,6 +76,8 @@ gint              gepub_doc_get_chapter                     (GepubDoc *doc);
 void              gepub_doc_set_chapter                     (GepubDoc *doc,
                                                              gint      index);
 
+GList            *gepub_doc_get_toc                         (GepubDoc *doc);
+
 G_END_DECLS
 
 /**
diff --git a/tests/test-gepub.c b/tests/test-gepub.c
index 1d6c0d8..afdf714 100644
--- a/tests/test-gepub.c
+++ b/tests/test-gepub.c
@@ -273,6 +273,21 @@ test_doc_spine (const char *path)
     g_object_unref (G_OBJECT (doc));
 }
 
+static void
+test_doc_toc (const char *path)
+{
+    GepubDoc *doc = gepub_doc_new (path, NULL);
+
+    GList *nav = gepub_doc_get_toc (doc);
+    while (nav && nav->data) {
+        GepubNavPoint *point = (GepubNavPoint*)nav->data;
+        PTEST ("%02d: %s -> %s\n", (gint)point->playorder, point->label, point->content);
+        nav = nav->next;
+    }
+
+    g_object_unref (G_OBJECT (doc));
+}
+
 static void
 destroy_cb (GtkWidget *window,
             GtkWidget *view)
@@ -435,6 +450,7 @@ main (int argc, char **argv)
     TEST(test_doc_name, argv[1])
     TEST(test_doc_resources, argv[1])
     TEST(test_doc_spine, argv[1])
+    TEST(test_doc_toc, argv[1])
 
     // Freeing the mallocs :P
     if (buf2) {


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